Какие паттерны проектирования GoF вы используете в работе?

Ответ

В зависимости от задачи, часто применяю следующие паттерны из каталога GoF:

1. Стратегия (Strategy) Использую для инкапсуляции семейства алгоритмов и обеспечения их взаимозаменяемости. Это устраняет условные операторы и упрощает добавление новых правил.

// Интерфейс стратегии
public interface ISortStrategy
{
    void Sort(List<int> data);
}

// Конкретные стратегии
public class QuickSort : ISortStrategy { /*...*/ }
public class MergeSort : ISortStrategy { /*...*/ }

// Контекст
public class Sorter
{
    private ISortStrategy _strategy;
    public Sorter(ISortStrategy strategy) => _strategy = strategy;
    public void SetStrategy(ISortStrategy strategy) => _strategy = strategy;
    public void ExecuteSort(List<int> data) => _strategy.Sort(data);
}

2. Наблюдатель (Observer) Незаменим для реализации событийных моделей, например, в UI или для оповещения подсистем о изменениях состояния.

// Используем встроенные события .NET
public class OrderService
{
    public event EventHandler<OrderEventArgs> OrderCreated;
    public void CreateOrder(Order order)
    {
        // Логика создания...
        OnOrderCreated(new OrderEventArgs(order));
    }
    protected virtual void OnOrderCreated(OrderEventArgs e) => OrderCreated?.Invoke(this, e);
}
// Подписчик (например, сервис нотификаций) подписывается на событие OrderCreated.

3. Фабричный метод (Factory Method) Применяю, когда логика создания объектов должна быть делегирована подклассам, или когда тип создаваемого объекта зависит от контекста.

public abstract class DocumentCreator
{
    // Фабричный метод
    public abstract IDocument CreateDocument();
    public void OpenDocument()
    {
        var doc = CreateDocument(); // Создание делегировано подклассам
        doc.Open();
    }
}
public class ReportCreator : DocumentCreator
{
    public override IDocument CreateDocument() => new Report();
}

Другие часто используемые паттерны:

  • Адаптер (Adapter): Для интеграции классов со несовместимыми интерфейсами (например, работа со сторонними библиотеками).
  • Декоратор (Decorator): Для динамического добавления поведения объектам без наследования (например, кэширующие или логирующие декораторы для репозиториев).
  • Внедрение зависимости (Dependency Injection): Хотя это не классический GoF-паттерн, он является краеугольным камнем для достижения слабой связанности, часто используя конструкции, похожие на Абстрактную фабрику.

Важно: Паттерн — это не самоцель, а инструмент для решения конкретных проблем проектирования. Их следует применять обоснованно, когда они упрощают код, а не усложняют его.

Ответ 18+ 🔞

Да ты посмотри, какие у нас тут паттерны пошли, прям как в аптеке — на любой чих. Ну ладно, расскажу, с чем обычно работаю, чтобы не изобретать велосипед каждый раз.

1. Стратегия (Strategy) Вот, например, когда у тебя куча алгоритмов, а какой выбрать — зависит от настроения, времени суток или фазы луны. Вместо того чтобы городить if на if, просто упаковываешь каждый алгоритм в отдельный класс. Получается чисто, как у кота под хвостом.

// Интерфейс стратегии
public interface ISortStrategy
{
    void Sort(List<int> data);
}

// Конкретные стратегии
public class QuickSort : ISortStrategy { /*...*/ }
public class MergeSort : ISortStrategy { /*...*/ }

// Контекст
public class Sorter
{
    private ISortStrategy _strategy;
    public Sorter(ISortStrategy strategy) => _strategy = strategy;
    public void SetStrategy(ISortStrategy strategy) => _strategy = strategy;
    public void ExecuteSort(List<int> data) => _strategy.Sort(data);
}

Вот и вся магия. Хочешь быструю сортировку — подсунул один класс, хочешь слиянием — другой. И ни одного switch на три экрана.

2. Наблюдатель (Observer) А этот паттерн — просто песня, когда нужно разослать уведомления всем, кто подписался. Типа, "эй, народ, у меня тут состояние поменялось, кто хочет — приходите, берите". В .NET для этого события вообще родные, грех не использовать.

// Используем встроенные события .NET
public class OrderService
{
    public event EventHandler<OrderEventArgs> OrderCreated;
    public void CreateOrder(Order order)
    {
        // Логика создания...
        OnOrderCreated(new OrderEventArgs(order));
    }
    protected virtual void OnOrderCreated(OrderEventArgs e) => OrderCreated?.Invoke(this, e);
}
// Подписчик (например, сервис нотификаций) подписывается на событие OrderCreated.

Сделал заказ — событие выстрелило, и все подписчики, как тараканы из щелей, побежали обрабатывать. Удобно, блядь, до невозможности.

3. Фабричный метод (Factory Method) А это когда самому создавать объекты лень или неудобно, и ты говоришь: "на, потомок, роди мне нужную хрень, а я потом приду и буду ей пользоваться". Делегирование создания — сила.

public abstract class DocumentCreator
{
    // Фабричный метод
    public abstract IDocument CreateDocument();
    public void OpenDocument()
    {
        var doc = CreateDocument(); // Создание делегировано подклассам
        doc.Open();
    }
}
public class ReportCreator : DocumentCreator
{
    public override IDocument CreateDocument() => new Report();
}

Базовый класс только каркас держит, а конкретный тип документа создаёт уже наследничек. Красота.

Ещё из частого:

  • Адаптер (Adapter): Когда нужно прикрутить старую библиотеку, которая говорит на языке, понятном только ей. Берёшь, оборачиваешь её в свой нормальный интерфейс — и вуаля, она уже как родная.
  • Декоратор (Decorator): Хочешь добавить функциональность объекту, но чтоб он об этом даже не догадывался? Пожалуйста — оберни его в декоратор, который добавит, например, логирование или кэширование. Можно хоть десять слоёв намотать, как капусту.
  • Внедрение зависимости (Dependency Injection): Это, конечно, не из банды GoF, но без него сейчас никуда. Всё ради слабой связанности, чтобы один компонент не держал другого за горло. По сути, та же Абстрактная фабрика, только на стероидах.

И главное, чувак, запомни: паттерны — это не священные скрижали, а просто инструменты в твоём ящике. Не нужно впихивать их везде, где только можно, лишь бы было. Применяй, когда реально видишь проблему, которую они решают, а не потому что "так модно". А то можно такую архитектурную астрономию наворотить, что сам потом будешь плакать, разбираясь.