Паттерны проектирования тесно связаны с SOLID-принципами — основой правильной объектно-ориентированной архитектуры. Понимание этой связи помогает применять паттерны осознанно, усиливая сильные стороны ООП.

Напоминание: принципы SOLID

  • S — Single Responsibility: один класс — одна ответственность.
  • O — Open/Closed: открытость для расширения, закрытость для модификации.
  • L — Liskov Substitution: наследник должен корректно заменять родителя.
  • I — Interface Segregation: интерфейсы должны быть узкоспециализированными.
  • D — Dependency Inversion: зависимости должны направляться от деталей к абстракциям.

Связь паттернов с SOLID

Принцип Проблема Паттерны, которые помогают
SRP Класс берёт на себя слишком много обязанностей Facade, Decorator, Command, Observer
OCP Изменения требуют правки существующего кода Strategy, Template Method, Chain of Responsibility
LSP Наследники нарушают поведение базового класса State, Bridge — позволяют изолировать вариации поведения
ISP Интерфейсы перегружены методами, которые не нужны клиентам Adapter, Proxy, Mediator
DIP Класс зависит от конкретных реализаций, а не от абстракций Factory Method, Abstract Factory, Dependency Injection

Примеры на стыке паттернов и SOLID

1. Strategy + OCP

Strategy идеально реализует принцип открытости/закрытости: можно добавлять новые алгоритмы без изменения существующего кода.

// Интерфейс стратегии
public interface IPaymentStrategy { void Pay(decimal amount); }

// Конкретные реализации
public class CardPayment : IPaymentStrategy { public void Pay(decimal amount) => Console.WriteLine("Картой"); }
public class CryptoPayment : IPaymentStrategy { public void Pay(decimal amount) => Console.WriteLine("Криптой"); }

// Контекст
public class PaymentService
{
    private IPaymentStrategy _strategy;
    public void SetStrategy(IPaymentStrategy strategy) => _strategy = strategy;
    public void Pay(decimal amount) => _strategy.Pay(amount);
}

2. Decorator + SRP

Decorator помогает не нарушать принцип SRP, добавляя новое поведение без изменения базового класса.

// Базовый интерфейс
public interface INotifier { void Send(string message); }

// Основной класс
public class EmailNotifier : INotifier
{
    public void Send(string message) => Console.WriteLine($"Email: {message}");
}

// Декоратор
public class SmsDecorator : INotifier
{
    private INotifier _inner;
    public SmsDecorator(INotifier inner) => _inner = inner;
    public void Send(string message)
    {
        _inner.Send(message);
        Console.WriteLine($"SMS: {message}");
    }
}

3. Factory Method + DIP

Factory Method позволяет зависеть от абстракции, а не от конкретных классов.

public abstract class LoggerFactory
{
    public abstract ILogger CreateLogger();
}

public class FileLoggerFactory : LoggerFactory
{
    public override ILogger CreateLogger() => new FileLogger();
}

4. Mediator + ISP

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

public interface IMediator
{
    void Notify(object sender, string ev);
}

5. Observer + SRP/OCP

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

public interface IObserver { void Update(string message); }
public interface ISubject { void Attach(IObserver o); void Notify(string msg); }

Как применять паттерны с точки зрения SOLID

  • Используйте паттерны как реализацию принципов, а не как “готовые рецепты”.
  • Сначала нарушьте SOLID — чтобы увидеть боль и понять, какой паттерн её устраняет.
  • Старайтесь, чтобы каждый паттерн усиливал хотя бы один из принципов SOLID, но не нарушал другие.
  • Не усложняйте архитектуру там, где достаточно простого наследования или композиции.

Заключение

Паттерны — это не замена SOLID, а его практическое воплощение. Каждый из них помогает реализовать конкретный принцип или устранить типичное нарушение: Decorator поддерживает SRP, Strategy — OCP, Factory Method — DIP. Чем глубже вы понимаете SOLID, тем точнее выбираете паттерны — и наоборот.