Банковская система
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 106 из 107.
Задание
ЛегкоДавайте создадим банковскую систему, которая управляет счетами, транзакциями и финансовыми операциями! Вы создадите надежную систему, где целостность данных имеет решающее значение — баланс счетов должен изменяться только через проверенные методы, а такие ошибки, как овердрафт или недопустимые суммы, должны обрабатываться корректно.
Вы распределите свой код по пяти файлам:
account.go: Определите интерфейсTransactableс методамиDeposit(amount float64) errorиWithdraw(amount float64) error. Затем создайте два типа счетов, реализующих этот интерфейс:CheckingAccountс неэкспортируемыми полямиid,holderиbalance. Расчетные счета позволяют снимать средства, доводя баланс ровно до нуля, но не ниже.SavingsAccountс теми же неэкспортируемыми полями плюс полеminBalance. Сберегательные счета должны поддерживать минимальный баланс 100.0 — операции по снятию средств, которые приведут к падению ниже этого порога, должны завершаться ошибкой.Обоим типам счетов требуются конструкторы (
NewCheckingAccountиNewSavingsAccount) и метод-геттерBalance() float64. Для депозитов отклоняйте отрицательные или нулевые суммы. Для снятия средств отклоняйте отрицательные или нулевые суммы, а также суммы, превышающие доступные средства.transaction.go: Создайте структуруTransactionдля записи активности по счету. Каждая транзакция имеетType(строка: "deposit" или "withdrawal"),Amount(float64) иAccountID(string). Включите конструкторNewTransactionи методString() string, который возвращает формат:[Type] $[Amount] on [AccountID]с суммой, отформатированной до двух знаков после запятой.bank.go: Создайте структуруBank, которая управляет несколькими счетами и записывает транзакции. Храните счета в карте (map) по ID и ведите срез (slice) транзакций. Реализуйте:NewBank() *Bank— инициализирует пустые коллекцииAddAccount(id string, account Transactable)— регистрирует счетDeposit(accountID string, amount float64) error— вносит депозит на счет и записывает транзакцию в случае успехаWithdraw(accountID string, amount float64) error— снимает средства со счета и записывает транзакцию в случае успехаTransfer(fromID, toID string, amount float64) error— атомарно перемещает деньги между счетами (если снятие прошло успешно, но депозит не удался, отмените снятие)GetBalance(accountID string) (float64, error)— возвращает баланс счета
Возвращайте соответствующие ошибки для несуществующих счетов.
errors.go: Определите именованные ошибки (sentinel errors) для ваших банковских операций:ErrAccountNotFound— когда ID счета не существуетErrInsufficientFunds— когда сумма снятия превышает доступный балансErrInvalidAmount— когда сумма депозита/снятия равна нулю или отрицательнаErrMinBalanceRequired— когда снятие со сберегательного счета нарушит требование минимального баланса
main.go: Создайте банк и обрабатывайте операции на основе входных данных.Прочитайте количество создаваемых счетов. Для каждого счета прочитайте тип счета (
checkingилиsavings), ID, имя владельца и начальный баланс. Создайте соответствующий тип счета и добавьте его в банк.Затем прочитайте количество операций. Каждая операция представляет собой одну из следующих:
deposit [accountID] [amount]withdraw [accountID] [amount]transfer [fromID] [toID] [amount]balance [accountID]
Для каждой операции выведите результат:
OKдля успешных депозитов/снятий/переводов, баланс, отформатированный до двух знаков после запятой для запросов баланса, илиError: [message]для сбоев, используя ваши сообщения об ошибках.
Будут предоставлены следующие входные данные:
- Количество счетов, затем тип каждого счета, ID, владелец, начальный баланс (по 4 строки на каждый)
- Количество операций, затем каждая операция (по 1 строке на каждую)
Например, при вводе:
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Ваш вывод должен быть:
OK
700.00
Error: minimum balance required
Error: insufficient funds
OK
1000.00И при вводе:
1
checking
C100
Charlie
100.00
4
withdraw C100 100
balance C100
deposit C100 -50
withdraw C999 50Ваш вывод должен быть:
OK
0.00
Error: invalid amount
Error: account not foundОбратите внимание, как интерфейс Transactable позволяет банку единообразно работать с обоими типами счетов, в то время как каждый тип счета применяет свои собственные правила. Метод Transfer демонстрирует атомарные операции — если что-то пойдет не так на полпути, система останется в согласованном состоянии!
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Считать количество счетов
scanner.Scan()
numAccounts, _ := strconv.Atoi(scanner.Text())
// Создать новый банк
bank := NewBank()
// Считать и создать счета
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: Создать соответствующий тип счета на основе accountType
// и добавить его в банк
_ = accountType
_ = id
_ = holder
_ = balance
}
// Считать количество операций
scanner.Scan()
numOps, _ := strconv.Atoi(scanner.Text())
// Обработать каждую операцию
for i := 0; i < numOps; i++ {
scanner.Scan()
parts := strings.Fields(scanner.Text())
operation := parts[0]
// TODO: Обработать каждый тип операции:
// - "deposit": внести на счет, вывести "OK" или ошибку
// - "withdraw": снять со счета, вывести "OK" или ошибку
// - "transfer": перевод между счетами, вывести "OK" или ошибку
// - "balance": получить и вывести баланс, отформатированный до 2 знаков после запятой
_ = operation
}
}
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool