воскресенье, 23 августа 2026 г.


А если этих SubStrategy будет 10 или 20, то это тоже будет нормально ? ЕСли у меня есть сложный объект, который рассматривается с разных сторон, то мы все равно не должны смешивать наши отдельные стратегии. ?

Да, даже при 10–20 sub-стратегиях это тоже нормально — если они разделены по доменам ответственности.

Но есть важный нюанс:
не смешивать все эти стратегии в один «монолитный» интерфейс, а оставить их независимыми.

Много стратегий — это нормально, если они независимы

Пример: ваш сложный объект рассматривается с разных сторон:

  • Поведение (behavior)

  • Расписание (scheduler)

  • Метрики (metrics)

  • Тунинг (tuner)

  • Авторизация (auth)

  • Логирование (logging)

  • Кэширование (cache)

  • Retry-политика (retry)

  • Сериализация (serializer)

  • Сжатие (compressor)

  • и т.д.

Если у вас будет:

text
public class BackgroundService< TBehavior, TScheduler, TMetrics, TTuner, TAuth, TLogging, TCache, TRetry, TSer, TCompress> : BackgroundService where TBehavior : IBehaviorStrategy where TScheduler : ISchedulerStrategy where TMetrics : IMetricsStrategy where TTuner : ITunerStrategy where TAuth : IAuthStrategy where TLogging : ILoggingStrategy where TCache : ICacheStrategy where TRetry : IRetryStrategy where TSer : ISerializerStrategy where TCompress : ICompressorStrategy { ... }

он станет неудобным:

  • Много типов параметров.

  • Конструктор с 10+ аргументами.

  • Сложность чтения и поддержки.

Поэтому при 10–20 стратегиях лучше не делать один класс с 10+ дженериками, а:

  1. Разбить на несколько компонентов (например, ProcessingService, SchedulingService, LoggingService).

  2. Или использовать композицию через интерфейс — собирать сложный сервис из нескольких небольших сервисов, каждый со своей стратегией.

Но принцип «не смешивать стратегии» остается: каждый аспект — отдельный интерфейс и отдельный набор стратегий.

Не смешивать отдельные стратегии — это правильно

Вы спросили:

Если у меня есть сложный объект, который рассматривается с разных сторон,
то мы все равно не должны смешивать наши отдельные стратегии.

Да, не должны смешивать, и это правильно.

Почему:

  1. Разная ответственность

    • Scheduler отвечает за расписание, а Bridge не должен знать, как оно работает.

    • Metrics отвечает за сбор метрик, а Behavior не должен за них заботиться.

  2. Разная динамика изменений

    • Те, кто меняет логику поведения, не должны дергать код метрик.

    • Те, кто меняет способ кэширования, не должны менять код scheduler.

  3. Принцип единой ответственности (SRP)
    Каждый интерфейс стратегии — одна ответственность.
    Смешивание нарушает SRP и усложняет тестирование.

  4. Чистая архитектура
    В чистой архитекттуре интерфейсы (включая стратегии) должны быть маленькими и специализированными, а зависимости направлены от инфраструктуры к бизнес-логике.

Как практично организовать 10–20 стратегий

Вариант 1: Несколько небольших сервисов

Не один класс с 20 дженериками, а несколько:

text
public class ProcessingService<TBehavior, TAuth, TRetry> : IHostedService where TBehavior : IBehaviorStrategy where TAuth : IAuthStrategy where TRetry : IRetryStrategy { ... } public class SchedulingService<TScheduler> : IHostedService where TScheduler : ISchedulerStrategy { ... } public class LoggingService<TLogging> : IHostedService where TLogging : ILoggingStrategy { ... } public class MetricsService<TMetrics> : IHostedService where TMetrics : IMetricsStrategy { ... }

Тогда:

  • Каждый сервис имеет мало стратегий (2–4).

  • Конструкторы небольшие.

  • Логика разделена по доменам.

В DI вы регистрируйте их независимо:

text
services.AddHostedService<ProcessingService<MyBehavior, JwtAuth, DefaultRetry>>(); services.AddHostedService<SchedulingService<IntervalScheduler>>(); services.AddHostedService<LoggingService<FileLogger>>();

Вариант 2: Композитный сервис с «под-сервисами»

Если нужен один «главный» сервис, пусть он собирает другие:

text
public class MasterBackgroundService : BackgroundService { private readonly ProcessingService _processingService; private readonly SchedulingService _schedulingService; private readonly MetricsPoller _metricsPoller; public MasterBackgroundService( ProcessingService processingService, SchedulingService schedulingService, MetricsPoller metricsPoller) { _processingService = processingService; _schedulingService = schedulingService; _metricsPoller = metricsPoller; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // запускаем под-сервисы await Task.WhenAll( _processingService.StartAsync(stoppingToken), _schedulingService.StartAsync(stoppingToken), _metricsPoller.StartAsync(stoppingToken)); } }

Здесь:

  • Универсальный BackgroundService не перегружен 20 дженериками.

  • Каждый под-сервис — отдельная стратегия своей области.

Вариант 3: Функциональные стратегии (делегаты)

Для маленьких стратегий (логирование, метрики, retry) можно использовать делегаты:

text
public class ProcessingService { private readonly Func<Task> _behavior; private readonly Action<string> _log; private readonly Action<string, double> _metric; public ProcessingService( Func<Task> behavior, Action<string> log, Action<string, double> metric) { _behavior = behavior; _log = log; _metric = metric; } }

Тогда:

  • 10–20 стратегий не требуют 10–20 дженериков.

  • Вы просто ¿подсовываете делегаты.

Это тоже «стратегия + делегирование», просто более легковесная.

Ответ на ваш вопрос

  • 10–20 стратегий — это нормально, если они разделены по доменам и не смешаны в один интерфейс.

  • Сложный объект, рассматриваемый с разных сторон, не должен иметь одну «монолитную» стратегию, а должен использовать композицию нескольких стратегий.

  • Подход вида:

    text
    BackgroundService<TBehavior, TScheduler, TMetrics, TTuner>

    — хороший паттерн, но при 10–20 стратегиях лучше:

    • Разбить на несколько сервисов, или

    • Использовать делегаты для маленьких стратегий, или

    • Собрать их через композицию через под-сервисы.

Это сохраняет модульность, тестируемость и читаемость и соответствует принципам чистой архитектуры.

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

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