Menu
Coddy logo textTech

Sistema Bancário

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 106 de 107.

challenge icon

Desafio

Fácil

Vamos criar um Sistema Bancário que gerencia contas, transações e operações financeiras! Você criará um sistema robusto em que a integridade dos dados é crítica: os saldos das contas só devem mudar por meio de métodos validados, e erros como saques que excedem o saldo ou valores inválidos devem ser tratados de forma adequada.

Você organizará seu código em cinco arquivos:

  • account.go: Defina uma interface Transactable com os métodos Deposit(amount float64) error e Withdraw(amount float64) error. Em seguida, crie dois tipos de conta que implementem essa interface:

    Uma CheckingAccount com os campos não exportados id, holder e balance. Contas correntes permitem saques que reduzem o saldo exatamente a zero, mas não abaixo disso.

    Uma SavingsAccount com os mesmos campos não exportados, além de um campo minBalance. Contas poupança devem manter um saldo mínimo de 100.0: saques que reduziriam o saldo abaixo desse limite devem falhar.

    Ambos os tipos de conta precisam de construtores (NewCheckingAccount e NewSavingsAccount) e de um método getter Balance() float64. Para depósitos, rejeite valores negativos ou iguais a zero. Para saques, rejeite valores negativos ou iguais a zero e valores que excedam os fundos disponíveis.

  • transaction.go: Crie uma struct Transaction para registrar a atividade das contas. Cada transação possui um Type (string: "deposit" ou "withdrawal"), um Amount (float64) e um AccountID (string). Inclua um construtor NewTransaction e um método String() string que retorne o formato: [Type] $[Amount] on [AccountID], com o valor formatado para duas casas decimais.
  • bank.go: Crie uma struct Bank que gerencie várias contas e registre transações. Armazene as contas em um map por ID e mantenha uma slice de transações. Implemente:
    • NewBank() *Bank: inicializa coleções vazias
    • AddAccount(id string, account Transactable): registra uma conta
    • Deposit(accountID string, amount float64) error: deposita em uma conta e registra a transação em caso de sucesso
    • Withdraw(accountID string, amount float64) error: realiza um saque de uma conta e registra a transação em caso de sucesso
    • Transfer(fromID, toID string, amount float64) error: move dinheiro entre contas atomicamente (se o saque for bem-sucedido, mas o depósito falhar, reverta o saque)
    • GetBalance(accountID string) (float64, error): retorna o saldo de uma conta

    Retorne erros apropriados para contas inexistentes.

  • errors.go: Defina erros sentinela para suas operações bancárias:
    • ErrAccountNotFound: quando um ID de conta não existe
    • ErrInsufficientFunds: quando o saque excede o saldo disponível
    • ErrInvalidAmount: quando o valor do depósito/saque é igual a zero ou negativo
    • ErrMinBalanceRequired: quando um saque de uma conta poupança violaria o saldo mínimo
  • main.go: Crie um banco e processe operações com base na entrada.

    Leia o número de contas a serem criadas. Para cada conta, leia o tipo da conta (checking ou savings), o ID, o nome do titular e o saldo inicial. Crie o tipo de conta apropriado e adicione-o ao banco.

    Em seguida, leia o número de operações. Cada operação é uma destas:

    • deposit [accountID] [amount]
    • withdraw [accountID] [amount]
    • transfer [fromID] [toID] [amount]
    • balance [accountID]

    Para cada operação, imprima o resultado: OK para depósitos/saques/transferências bem-sucedidos, o saldo formatado para duas casas decimais em consultas de saldo ou Error: [message] para falhas, usando suas mensagens de erro sentinela.

As seguintes entradas serão fornecidas:

  • Número de contas, seguido do tipo, ID, titular e saldo inicial de cada conta (4 linhas para cada uma)
  • Número de operações, seguido de cada operação (1 linha para cada uma)

Por exemplo, considerando:

2
checking
ACC001
Alice
500.00
savings
ACC002
Bob
1000.00
6
deposit ACC001 200
balance ACC001
withdraw ACC002 950
withdraw ACC001 800
transfer ACC001 ACC002 100
balance ACC002

Sua saída deve ser:

OK
700.00
Error: minimum balance required
Error: insufficient funds
OK
1000.00

E considerando:

1
checking
C100
Charlie
100.00
4
withdraw C100 100
balance C100
deposit C100 -50
withdraw C999 50

Sua saída deve ser:

OK
0.00
Error: invalid amount
Error: account not found

Observe como a interface Transactable permite que o Bank trabalhe uniformemente com os dois tipos de conta, enquanto cada tipo de conta impõe suas próprias regras. O método Transfer demonstra operações atômicas: se algo der errado no meio do processo, o sistema permanece consistente!

Experimente você mesmo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// Ler o número de contas
	scanner.Scan()
	numAccounts, _ := strconv.Atoi(scanner.Text())

	// Criar um novo banco
	bank := NewBank()

	// Ler e criar contas
	for i := 0; i < numAccounts; i++ {
		scanner.Scan()
		accountType := scanner.Text()
		scanner.Scan()
		id := scanner.Text()
		scanner.Scan()
		holder := scanner.Text()
		scanner.Scan()
		balance, _ := strconv.ParseFloat(scanner.Text(), 64)

		// TODO: Criar o tipo de conta apropriado com base em accountType
		// e adicioná-lo ao banco
		_ = accountType
		_ = id
		_ = holder
		_ = balance
	}

	// Ler o número de operações
	scanner.Scan()
	numOps, _ := strconv.Atoi(scanner.Text())

	// Processar cada operação
	for i := 0; i < numOps; i++ {
		scanner.Scan()
		parts := strings.Fields(scanner.Text())
		operation := parts[0]

		// TODO: Lidar com cada tipo de operação:
		// - "deposit": depositar na conta, imprimir "OK" ou erro
		// - "withdraw": sacar da conta, imprimir "OK" ou erro
		// - "transfer": transferir entre contas, imprimir "OK" ou erro
		// - "balance": obter e imprimir o saldo formatado com 2 casas decimais
		_ = operation
	}
}

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de Go online