Проект для управления финансами на C# (.NET) Комиссарова Юлия БПИ249
Конструирование программного обеспечения Домашнее задание №2.
Сборка проекта Требования:
- Установлен .NET 8.0
- Среда разработки: JetBrains Rider (рекомендуется) или Visual Studio.
- Загруженные NuGet пакеты: Microsoft.Extensions.DependencyInjection
Запуск выполнить из корня консольного проекта (HSE_Bank_Console): Откроется текстовое меню с выбором операций.
- Общая идея решения
В проекте реализовано консольное приложение для учёта личных финансов. Программа позволяет создавать банковские счета, категории доходов и расходов, добавлять операции (доход, расход, перевод между счетами), а также выполнять импорт и экспорт данных в разных форматах.
Помимо базового функционала (работа со счетами, категориями и операциями), было добавлено:
Измерение времени выполнения действий (через паттерн Декоратор);
Импорт данных (через паттерны Шаблонный метод и Стратегия);
Экспорт данных (через паттерн Посетитель);
Система уведомлений и логирования событий (через паттерн Наблюдатель);
Отмена команд (через паттерн Команда и хранение истории).
Меню реализовано в консольном виде, с навигацией стрелками вверх и вниз. Навигация полностью отделена от бизнес-логики и вызывает команды, которые обращаются к фасадам, а те уже к сервисам и репозиториям.
- Реализация принципов SOLID и GRASP
-
Single Responsibility Principle: Каждый класс отвечает за одну конкретную задачу:
-
Open/Closed Principle: Добавление новых форматов экспорта/импорта, декораторов команд или наблюдателей не требует изменения существующего кода, только добавление новых классов, реализующих интерфейсы.
-
Liskov Substitution Principle: Любой сервис, репозиторий или стратегия может быть заменён на другую реализацию того же интерфейса без изменения остального кода.
-
Interface Segregation Principle: Интерфейсы разделены по зонам ответственности: IAccountService, ICategoryService, IOperationService, IExportService, IImportService и др. Каждый интерфейс определяет минимальный набор методов, нужный для конкретной задачи.
-
Dependency Inversion Principle: Все зависимости передаются через конструкторы и управляются DI-контейнером (Microsoft.Extensions.DependencyInjection).
GRASP-принципы:
-
Controller: роль контроллеров выполняют классы меню (AccountMenu, OperationMenu, MainMenu), которые принимают ввод пользователя и вызывают соответствующие фасады.
-
Information Expert: данные обрабатываются там, где они хранятся. Например, операции обрабатываются через OperationService, а не напрямую в меню.
-
Creator: фабрика (ServiceFactory) отвечает за создание всех доменных объектов, что исключает дублирование логики и обеспечивает валидацию.
-
Low Coupling / High Cohesion: каждый модуль независим от других, выполняет строго свою задачу.
-
Polymorphism: используется при импорте и экспорте данных (разные стратегии и посетители реализуют общий интерфейс).
- Реализованные паттерны GoF и их взаимосвязь
-
Factory (Простая фабрика) Реализована в классе ServiceFactory. Отвечает за создание всех доменных объектов (BankAccount, Category, Operation). Это избавляет сервисы от дублирования проверок и гарантирует, что все объекты создаются корректно. Пример: в AccountService.CreateAccount() вместо new BankAccount() вызывается factory.CreateBankAccount().
-
Facade (Фасад) Реализован в AccountFacade, CategoryFacade, OperationFacade. Фасады объединяют вызовы нескольких сервисов и предоставляют упрощённый интерфейс для выполнения сложных бизнес-сценариев. Например, перевод между счетами (Transfer) создаёт сразу две операции (списание и зачисление) через фасад.
-
Command (Команда) Каждый пользовательский сценарий оформлен в виде команды (CreateAccountCommand, DeleteAccountCommand, AddOperationCommand и т.д.). Все команды реализуют интерфейс ICommand с методами Execute() и Undo(). Команды выполняются через CommandInvoker, который хранит историю и позволяет отменять последние действия.
-
Decorator (Декоратор) Используется для измерения времени выполнения каждой команды. Класс TimedCommandDecorator оборачивает любую команду и выводит, сколько миллисекунд заняло её выполнение. Это позволяет добавлять функционал без изменения логики самих команд.
-
Template Method (Шаблонный метод) Используется в DataImporterBase. В нём задан общий алгоритм импорта:
чтение файла
парсинг (через стратегию)
сохранение данных (в сервисы). Конкретные шаги могут переопределяться.
-
Strategy (Стратегия) Используется при импорте данных. Разные стратегии (CsvParseStrategy, JsonParseStrategy) реализуют интерфейс IDataParseStrategy и определяют способ парсинга входных данных. Это даёт гибкость при добавлении новых форматов, без изменения основной логики импорта.
-
Visitor (Посетитель) Применён при экспорте данных. Разные посетители (CsvExportVisitor, JsonExportVisitor, TxtExportVisitor) реализуют интерфейс IDataVisitor и умеют обходить структуру данных (BankData) и записывать её в соответствующем формате. Таким образом, формат экспорта можно менять без изменения модели данных.
-
Observer (Наблюдатель) Реализован для отслеживания событий создания операций. Интерфейс IObserver реализуют ConsoleNotificationObserver (сообщения в консоль) и FileLoggerObserver (запись в лог). Класс OperationService выступает в роли наблюдаемого (IObservable). При создании новой операции все подписанные наблюдатели получают уведомление.
Взаимосвязь паттернов:
База (модели + репозитории) — хранят и управляют данными в памяти.
Фабрика создаёт корректные объекты моделей и передаётся в сервисы.
Сервисы управляют логикой, используя фабрику и репозитории.
Фасады объединяют вызовы сервисов для выполнения бизнес-сценариев.
Команды инкапсулируют действия пользователя и выполняются через CommandInvoker.
Декоратор добавляет измерение времени к выполнению команд.
Импорт реализован через Template Method + Strategy, а экспорт — через Visitor. Все данные сохраняются и берутся из папки ExportFiles, в том числе логи записываются.
Наблюдатели реагируют на события из сервисов и логируют их.