Банковская система
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 106 из 107.
Задание
ЛегкоДавайте создадим Banking System, которая управляет account, транзакциями и финансовыми операциями! Вы создадите надёжную систему, где целостность данных критически важна: балансы account должны изменяться только через проверенные методы, а такие ошибки, как овердрафт или недопустимые суммы, должны обрабатываться корректно.
Вы распределите код по пяти файлам:
account.go: Define интерфейсTransactableс методамиDeposit(amount float64) errorиWithdraw(amount float64) error. Затем создайте два типа account, реализующих этот интерфейс:CheckingAccountс неэкспортируемыми полямиid,holderиbalance. Checking account позволяют выполнять снятие средств, при котором баланс становится ровно нулевым, но не опускается ниже нуля.SavingsAccountс теми же неэкспортируемыми полями и дополнительным полемminBalance. Savings account должны поддерживать минимальный баланс 100.0: снятие средств, в результате которого баланс станет ниже этого порога, должно завершаться ошибкой.Оба типа account должны иметь конструкторы (
NewCheckingAccountиNewSavingsAccount) и метод-геттерBalance() float64. Для пополнений отклоняйте отрицательные и нулевые суммы. Для снятий отклоняйте отрицательные и нулевые суммы, а также суммы, превышающие available средства.transaction.go: Create структуруTransactionдля записи activity account. Каждая транзакция содержитType(string: "deposit" или "withdrawal"),Amount(float64) иAccountID(string). Добавьте конструкторNewTransactionи методString() string, возвращающий формат:[Type] $[Amount] on [AccountID], где сумма отформатирована с двумя знаками после десятичной точки.bank.go: Создайте структуруBank, которая управляет несколькими account и записывает транзакции. Храните account в map по ID и используйте срез транзакций. Реализуйте:NewBank() *Bank: инициализирует empty коллекцииAddAccount(id string, account Transactable): регистрирует accountDeposit(accountID string, amount float64) error: пополняет account и записывает транзакцию при успешном выполненииWithdraw(accountID string, amount float64) error: снимает средства с account и записывает транзакцию при успешном выполненииTransfer(fromID, toID string, amount float64) error: atomically перемещает средства между account (если снятие прошло успешно, но пополнение завершилось ошибкой, отмените снятие)GetBalance(accountID string) (float64, error): возвращает баланс account
Возвращайте appropriate ошибки для несуществующих account.
errors.go: Define sentinel-ошибки для банковских операций:ErrAccountNotFound: когда ID account doesn существуетErrInsufficientFunds: когда снятие превышает available балансErrInvalidAmount: когда сумма пополнения или снятия равна нулю либо отрицательнаErrMinBalanceRequired: когда снятие из savings account нарушает минимальный баланс
main.go: Создайте bank и обработайте операции на основе входных данных.Прочитайте количество account, которые необходимо создать. Для каждого account прочитайте тип account (
checkingилиsavings), ID, имя holder и начальный баланс. Создайте appropriate тип account и добавьте его в bank.Затем прочитайте количество операций. Каждая операция представляет собой одну из следующих:
deposit [accountID] [amount]withdraw [accountID] [amount]transfer [fromID] [toID] [amount]balance [accountID]
Для каждой операции выведите результат:
OKдля успешных пополнений, снятий и переводов, баланс с двумя знаками после десятичной точки для запросов баланса илиError: [message]в случае ошибки, используя сообщения sentinel-ошибок.
Будут предоставлены следующие входные данные:
- Количество account, затем тип, ID, holder и начальный баланс каждого account (по 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 позволяет Bank единообразно работать с обоими типами account, в то время как каждый тип account применяет собственные правила. Метод 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Потренируйтесь самостоятельно: Онлайн-компилятор Go