God Object, The Blob, Monster Class
https://giga.chat/link/gcsYHpPkWU
Английское наименование: God Object (также известен как The Blob — «Капля» или Monster Class).
Русская трактовка:
Это антипаттерн объектно-ориентированного проектирования. Он описывает класс, который контролирует слишком много частей системы и обладает чрезмерным количеством обязанностей. Такой объект знает о практически всем в приложении: он хранит огромное количество данных, содержит сложную бизнес-логику, управляет зависимостями, работает с базой данных и отвечает за пользовательский интерфейс.
Название происходит от идеи, что такой объект становится почти всеведущим и всемогущим внутри своей программной вселенной, нарушая все принципы чистого кода.
Основные признаки «Божественного объекта»
- Нарушение SRP (Принцип единственной ответственности): у класса есть десятки причин для изменения. Любое изменение в любой части системы требует модификации этого одного файла.
- Огромный размер: это один из самых длинных файлов в кодовой базе, часто содержащий сотни или даже тысячи строк кода.
- Обилие полей-состояний: класс владеет множеством приватных полей, которые слабо связаны друг с другом логически.
- Сложные зависимости: ему приходится внедрять (inject) через конструктор множество сервисов (
ILogger,IRepository,IEmailService,IConfigurationи так далее), потому что он делает всё сразу. - «Спагетти-код»: методы такого класса вызывают друг друга сложным, запутанным образом, создавая трудноуловимые побочные эффекты.
Пример
Представим себе сервис обработки заказов, который превратился в «божественный объект»:
public class OrderProcessingGodObject
{
// Огромное количество зависимостей
private readonly IEmailSender _emailSender;
private readonly ISmsGateway _smsGateway;
private readonly IInventoryDbContext _db;
private readonly IPaymentProvider _paymentProvider;
private readonly ILogger _logger;
private readonly IConfiguration _config;
// Сотни полей состояния
private decimal _currentDiscount;
private string _lastErrorMessage;
private bool _isFraudCheckPassed;
// ... еще 50 полей ...
public OrderProcessingGodObject(/* длинный список инъекций */) { /*...*/ }
// Метод на 300 строк, который делает всё подряд
public async Task ProcessOrderAsync(OrderDto order)
{
// 1. Валидация DTO
if (order.Items.Count == 0) throw new Exception("Empty order");
// 2. Прямая работа с БД
var customer = await _db.Customers.FindAsync(order.CustomerId);
// 3. Бизнес-логика расчета скидок
_currentDiscount = CalculateDiscount(customer.IsPremium);
// 4. Вызов внешнего API оплаты
var paymentResult = await _paymentProvider.ChargeAsync(...);
// 5. Логирование
_logger.LogInformation($"Payment for {order.Id} is {paymentResult.Status}");
// 6. Отправка уведомлений
await _emailSender.SendAsync(customer.Email, "Receipt...");
await _smsGateway.SendAsync(customer.Phone, "Your order shipped!");
// 7. Изменение глобального состояния
_isFraudCheckPassed = true;
// ...и так далее
}
}Этот класс нарушает границы ответственности. Он одновременно является контроллером, сервисом, репозиторием и моделью представления.
Почему это плохо?
- Нулевая тестируемость. Протестировать метод
ProcessOrderAsyncизолированно невозможно. Вам придется либо мокать десяток зависимостей, либо поднимать интеграционный тест, что очень медленно и хрупко. - Трудность изменений. Чтобы изменить логику отправки SMS, нужно открывать этот гигантский файл, искать нужную строку среди сотен других и рисковать сломать расчет скидок или работу с БД.
- Проблемы с многопоточностью. Если экземпляр такого объекта расшарен между потоками, его многочисленные поля состояния приведут к гонкам данных (race conditions) и непредсказуемому поведению.
- Низкое переиспользование. Вы не можете взять часть логики (например, только расчет скидки) и использовать её в другом месте без подтягивания всего остального «балласта».
Как бороться с «Божественным объектом»? (Рефакторинг)
Лечение всегда заключается в декомпозиции — расчленении монстра на мелкие, специализированные компоненты. Ваша текущая архитектура с разделением на Транспорт, Диспетчер и Поведения уже является отличным лекарством от этой болезни.
Если бы мы рефакторили пример выше, мы бы получили:
OrderValidator: небольшой класс с одним методомValidate(). Отвечает только за проверку входящих данных.DiscountCalculator: чистая функция или класс, который принимает данные клиента и возвращает число (скидку). Не имеет побочных эффектов.PaymentService: обертка над платежным шлюзом.NotificationService: агрегирует в себе_emailSenderи_smsGateway, отвечая только за уведомления.OrderProcessor(наш координатор): легкий класс, который не делает ничего сам, а лишь вызывает вышеперечисленные сервисы в нужном порядке.
В вашей архитектуре роль таких специализированных компонентов выполняют поведения. Вместо того чтобы писать всю логику внутри FractalCellTemplate, вы создаете маленькие классы HeartbeatBehavior, DataProcessingBehavior, каждый из которых несет ровно одну ответственность. А BehaviorRouter выступает в роли координатора, избавляя саму ячейку от необходимости знать обо всем на свете.
Комментариев нет:
Отправить комментарий