Спроектируйте простую систему банковских счетов (открыть счёт, снять/пополнить, перевод) тремя разными подходами: процедурным, объектно-ориентированным и функциональным; опишите плюсы и минусы каждого подхода с точки зрения читаемости, тестируемости и сопровождения.

12 Окт в 08:20
11 +11
0
Ответы
1

Ниже — три простые реализации системы банковских счётов open,deposit/withdraw,transferopen, deposit/withdraw, transferopen,deposit/withdraw,transfer в трёх парадигмах: процедурной, объектно‑ориентированной и функциональной. Для каждой — краткий код на Python чтобыбылонаглядночтобы было наглядночтобыбылонаглядно и разбор плюсов/минусов с точки зрения читаемости, тестируемости и сопровождения.

1) Процедурный подход
Описание: глобальное состояние словарьсловарьсловарь + функции, которые его мутируют.

Код PythonPythonPython:

# procedural.py
accounts = {} # id -> {"owner": str, "balance": int}
_next_id = 1
def open_accountowner,initial=0owner, initial=0owner,initial=0:
global _next_id
acc_id = _next_id
_next_id += 1
accountsaccidacc_idacci d = {"owner": owner, "balance": initial}
return acc_id
def depositaccid,amountacc_id, amountacci d,amount:
if amount < 0 or acc_id not in accounts:
return False
accountsaccidacc_idacci d"balance""balance""balance" += amount
return True
def withdrawaccid,amountacc_id, amountacci d,amount:
if amount < 0 or acc_id not in accounts:
return False
if accountsaccidacc_idacci d"balance""balance""balance" < amount:
return False
accountsaccidacc_idacci d"balance""balance""balance" -= amount
return True
def transferfromid,toid,amountfrom_id, to_id, amountfromi d,toi d,amount:
# простая реализация: сначала снять, потом положить; откат в случае ошибки
if withdrawfromid,amountfrom_id, amountfromi d,amount:
if deposittoid,amountto_id, amounttoi d,amount:
return True
else:
# откат
depositfromid,amountfrom_id, amountfromi d,amount return False

Плюсы:

Очень простая и понятная реализация для малого кода.Мало шаблонного кода — легко писать быстро.

Минусы:

Читаемость: при небольших скриптах — OK, при росте код становится хаотичным глобальныепеременныеглобальные переменныеглобальныепеременные.Тестируемость: сложнее запускать параллельные тесты общийглобальныйстейтобщий глобальный стейтобщийглобальныйстейт, нужно вручную чистить/мокать глобальные переменные.Сопровождение: высокая связность через глобальное состояние, риск побочных эффектов, трудно управлять транзакциями и конкурентностью.

2) Объектно‑ориентированный подход
Описание: объекты Account инкапсуляцияинкапсуляцияинкапсуляция и Bank менеджерсчётов,операциименеджер счётов, операциименеджерсчётов,операции.

Код PythonPythonPython:

# oop.py
from dataclasses import dataclass
@dataclass
class Account:
id: int
owner: str
balance: int = 0
def depositself,amountself, amountself,amount:
if amount < 0:
raise ValueError("amount < 0")
self.balance += amount
def withdrawself,amountself, amountself,amount:
if amount < 0:
raise ValueError("amount < 0")
if self.balance < amount:
raise ValueError"insufficientfunds""insufficient funds""insufficientfunds" self.balance -= amount
class Bank:
def __init__selfselfself:
self._accounts = {}
self._next_id = 1
def open_accountself,owner,initial=0self, owner, initial=0self,owner,initial=0:
acc = Accountself.nextid,owner,initialself._next_id, owner, initialself.n exti d,owner,initial self._accountsself.nextidself._next_idself.n exti d = acc
self._next_id += 1
return acc.id
def getself,accidself, acc_idself,acci d:
return self._accounts.getaccidacc_idacci d
def transferself,fromid,toid,amountself, from_id, to_id, amountself,fromi d,toi d,amount:
a_from = self.getfromidfrom_idfromi d a_to = self.gettoidto_idtoi d if a_from is None or a_to is None:
raise ValueError"accountnotfound""account not found""accountnotfound" # здесь можно добавить блокировку по счетам в многопоточном окружении
a_from.withdrawamountamountamount a_to.depositamountamountamount

Плюсы:

Читаемость: код структурирован, доменные сущности Account,BankAccount, BankAccount,Bank очевидны.Тестируемость: можно тестировать методы классов, легко мокать/заменять объекты, изолировать сценарии.Сопровождение: хорошая инкапсуляция, расширяемость наследование,композициянаследование, композициянаследование,композиция, удобно добавлять правила/валидации, логирование.

Минусы:

Мутируемые объекты: состояние меняется внутри объектов, что требует внимания при многопоточности нужныблокировкинужны блокировкинужныблокировки.Возможна чрезмерная сложность при неправильной модели слишкомглубокиеиерархиислишком глубокие иерархиислишкомглубокиеиерархии.Иногда больше шаблонного кода, чем в функциональном варианте.

3) Функциональный подход
Описание: состояние неизменяемо; операции — чистые функции, возвращающие новое состояние и результат безглобальныхпобочныхэффектовбез глобальных побочных эффектовбезглобальныхпобочныхэффектов.

Код PythonPythonPython:

# functional.py
from dataclasses import dataclass, replace
@dataclassfrozen=Truefrozen=Truefrozen=True class Account:
id: int
owner: str
balance: int = 0
# state: {"accounts": {id: Account}, "next_id": int}
def open_accountstate,owner,initial=0state, owner, initial=0state,owner,initial=0:
nid = state"nextid""next_id""nexti d" acc = Accountnid,owner,initialnid, owner, initialnid,owner,initial new_accounts = dictstate["accounts"]state["accounts"]state["accounts"] new_accountsnidnidnid = acc
new_state = {"accounts": new_accounts, "next_id": nid + 1}
return new_state, nid
def depositstate,accid,amountstate, acc_id, amountstate,acci d,amount:
if amount < 0 or acc_id not in state"accounts""accounts""accounts":
return state, "error","badrequest""error", "bad request""error","badrequest" acc = state"accounts""accounts""accounts"accidacc_idacci d new_acc = replaceacc,balance=acc.balance+amountacc, balance=acc.balance + amountacc,balance=acc.balance+amount new_accounts = dictstate["accounts"]state["accounts"]state["accounts"] new_accountsaccidacc_idacci d = new_acc
new_state = {**state, "accounts": new_accounts}
return new_state, "ok","ok","ok",
def withdrawstate,accid,amountstate, acc_id, amountstate,acci d,amount:
if amount < 0 or acc_id not in state"accounts""accounts""accounts":
return state, "error","badrequest""error", "bad request""error","badrequest" acc = state"accounts""accounts""accounts"accidacc_idacci d if acc.balance < amount:
return state, "error","insufficient""error", "insufficient""error","insufficient" new_acc = replaceacc,balance=acc.balance−amountacc, balance=acc.balance - amountacc,balance=acc.balanceamount new_accounts = dictstate["accounts"]state["accounts"]state["accounts"] new_accountsaccidacc_idacci d = new_acc
new_state = {**state, "accounts": new_accounts}
return new_state, "ok","ok","ok",
def transferstate,fromid,toid,amountstate, from_id, to_id, amountstate,fromi d,toi d,amount:
state2, r1 = withdrawstate,fromid,amountstate, from_id, amountstate,fromi d,amount if r1000 != "ok":
return state, r1
state3, r2 = depositstate2,toid,amountstate2, to_id, amountstate2,toi d,amount if r2000 != "ok":
# откат: вернуть предыдущее состояние state безснятиябез снятиябезснятия return state, "error","depositfailed""error", "deposit failed""error","depositfailed" return state3, "ok","ok","ok",

Плюсы:

Читаемость: функции небольшие и предсказуемые; поведение легче понимать, если привыкли к FP.Тестируемость: очень легко тестировать чистые функции — вход -> выход; тесты не зависят от внешнего состояния.Сопровождение: отсутствие скрытых побочных эффектов упрощает рефакторинг; хорошо для конкурентной среды нетобщеймутируемойпамятинет общей мутируемой памятинетобщеймутируемойпамяти.

Минусы:

Требуется передавать и возвращать состояние, что может добавить шаблонного кода и усложнить API.Может быть менее интуитивно для программистов, привыкших к OOP.Копирование структур впростомпримереdict.copy()в простом примере dict.copy()впростомпримереdict.copy() может быть затратным по памяти/времени; в больших системах нужны оптимизированные неизменяемые структуры persistentdatastructurespersistent data structurespersistentdatastructures.

Сравнение по критериям

Читаемость

Процедурный: читаемо для простых сценариев; быстро становится хаотичным при усложнении.OOP: хорошая читаемость для моделирования предметной области Accounts,BankAccounts, BankAccounts,Bank. При правильной декомпозиции — очень понятен.Функциональный: читаем при небольших чистых функциях; требует привычки к явной передаче состояния.

Тестируемость

Процедурный: труднее — глобальный стейт, тесты должны контролировать/сбрасывать состояние.OOP: легко тестировать отдельные объекты/методы; можно мокать внешние зависимости.Функциональный: самое простое тестирование — чистые функции, нет скрытых побочных эффектов.

Сопровождение расширение,багфикс,рефакторинграсширение, багфикс, рефакторинграсширение,багфикс,рефакторинг

Процедурный: плохо масштабируется; риск регрессий из‑за глобального состояния.OOP: хорош для сложной логики, легко вводить новые правила/стратегии; при правильном дизайне — лучший баланс.Функциональный: безопасный для рефакторинга и параллельного выполнения; но требует дисциплины и может потребовать инфраструктуры для управления состоянием например,потокизмененийнапример, поток измененийнапример,потокизменений.

Рекомендации

Для небольшого скрипта или демо: процедурный подход — быстрый старт.Для типичного бизнес‑приложения с богатой предметной логикой и долгим сроком поддержки: OOP — наиболее привычный и удобный вариант.Если важны корректность, простота тестирования и конкурентность многопотоков,события,распределённостьмного потоков, события, распределённостьмногопотоков,события,распределённость, или команда ориентирована на функциональный стиль — выбирайте функциональный подход илигибрид:immutableмодели+сервисывOOPили гибрид: immutable модели + сервисы в OOPилигибрид:immutableмодели+сервисывOOP.

Если нужно, могу:

Сделать «промышленную» версию собработкойошибокчерезисключения/Result,логированием,транзакциямис обработкой ошибок через исключения/Result, логированием, транзакциямисобработкойошибокчерезисключения/Result,логированием,транзакциями.Показать пример unit‑тестов для каждого подхода.Добавить обработку многопоточности блокировки,optimisticlockingблокировки, optimistic lockingблокировки,optimisticlocking и примеры использования реальной БД.
12 Окт в 09:37
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир