среда, 1 июля 2026 г.

God Object, The Blob, Monster Class

God Object, The Blob, Monster Class

https://giga.chat/link/gcsYHpPkWU

Английское наименование: God Object (также известен как The Blob — «Капля» или Monster Class).

Русская трактовка: 

Это антипаттерн объектно-ориентированного проектирования. Он описывает класс, который контролирует слишком много частей системы и обладает чрезмерным количеством обязанностей. Такой объект знает о практически всем в приложении: он хранит огромное количество данных, содержит сложную бизнес-логику, управляет зависимостями, работает с базой данных и отвечает за пользовательский интерфейс.

Название происходит от идеи, что такой объект становится почти всеведущим и всемогущим внутри своей программной вселенной, нарушая все принципы чистого кода.

Основные признаки «Божественного объекта»

  • Нарушение SRP (Принцип единственной ответственности): у класса есть десятки причин для изменения. Любое изменение в любой части системы требует модификации этого одного файла.
  • Огромный размер: это один из самых длинных файлов в кодовой базе, часто содержащий сотни или даже тысячи строк кода.
  • Обилие полей-состояний: класс владеет множеством приватных полей, которые слабо связаны друг с другом логически.
  • Сложные зависимости: ему приходится внедрять (inject) через конструктор множество сервисов (ILogger, IRepository, IEmailService, IConfiguration и так далее), потому что он делает всё сразу.
  • «Спагетти-код»: методы такого класса вызывают друг друга сложным, запутанным образом, создавая трудноуловимые побочные эффекты.

Пример

Представим себе сервис обработки заказов, который превратился в «божественный объект»:

csharp
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; 
        
        // ...и так далее
    }
}

Этот класс нарушает границы ответственности. Он одновременно является контроллером, сервисом, репозиторием и моделью представления.

Почему это плохо?

  1. Нулевая тестируемость. Протестировать метод ProcessOrderAsync изолированно невозможно. Вам придется либо мокать десяток зависимостей, либо поднимать интеграционный тест, что очень медленно и хрупко.
  2. Трудность изменений. Чтобы изменить логику отправки SMS, нужно открывать этот гигантский файл, искать нужную строку среди сотен других и рисковать сломать расчет скидок или работу с БД.
  3. Проблемы с многопоточностью. Если экземпляр такого объекта расшарен между потоками, его многочисленные поля состояния приведут к гонкам данных (race conditions) и непредсказуемому поведению.
  4. Низкое переиспользование. Вы не можете взять часть логики (например, только расчет скидки) и использовать её в другом месте без подтягивания всего остального «балласта».

Как бороться с «Божественным объектом»? (Рефакторинг)

Лечение всегда заключается в декомпозиции — расчленении монстра на мелкие, специализированные компоненты. Ваша текущая архитектура с разделением на Транспорт, Диспетчер и Поведения уже является отличным лекарством от этой болезни.

Если бы мы рефакторили пример выше, мы бы получили:

  1. OrderValidator: небольшой класс с одним методом Validate(). Отвечает только за проверку входящих данных.
  2. DiscountCalculator: чистая функция или класс, который принимает данные клиента и возвращает число (скидку). Не имеет побочных эффектов.
  3. PaymentService: обертка над платежным шлюзом.
  4. NotificationService: агрегирует в себе _emailSender и _smsGateway, отвечая только за уведомления.
  5. OrderProcessor (наш координатор): легкий класс, который не делает ничего сам, а лишь вызывает вышеперечисленные сервисы в нужном порядке.

В вашей архитектуре роль таких специализированных компонентов выполняют поведения. Вместо того чтобы писать всю логику внутри FractalCellTemplate, вы создаете маленькие классы HeartbeatBehavior, DataProcessingBehavior, каждый из которых несет ровно одну ответственность. А BehaviorRouter выступает в роли координатора, избавляя саму ячейку от необходимости знать обо всем на свете.

Комментариев нет:

Отправить комментарий