SOLID в C# с примерами

↑ 8.1 Принципы и паттерны проектирования в C♯ · → Следующая

Обязательно~3 мин чтенияУровень: senior

Зачем это на собесе

SOLID спрашивают всегда. Нужно приводить пример нарушения и исправления, а не заучивать определения.

Объяснение

ПринципСутьПризнак нарушения
S — Single Responsibilityу класса одна причина для изменения«God class», сервис на 2000 строк
O — Open/Closedоткрыт для расширения, закрыт для измененияswitch по типу, который растёт с каждой фичей
L — Liskov Substitutionподтип можно подставить вместо базового без нарушения поведенияNotSupportedException в наследнике, усиление предусловий
I — Interface Segregationмного узких интерфейсов лучше одного широкогопустые реализации методов
D — Dependency Inversionзависеть от абстракций, а не от реализацийnew SqlRepo() внутри бизнес-класса
// OCP: расширяем стратегиями, не правя калькулятор
public interface IDiscount { bool AppliesTo(Order o); decimal Apply(Order o); }
public class PriceCalculator(IEnumerable<IDiscount> discounts)
{
    public decimal Total(Order o) => o.Subtotal - discounts.Where(d => d.AppliesTo(o)).Sum(d => d.Apply(o));
}
 
// LSP: нарушение
class Bird { public virtual void Fly() { } }
class Penguin : Bird { public override void Fly() => throw new NotSupportedException(); }   // подтип ломает контракт
 
// ISP: узкие интерфейсы
public interface IOrderReader { Task<Order?> GetAsync(Guid id); }
public interface IOrderWriter { Task AddAsync(Order o); }
 
// DIP: бизнес-логика зависит от абстракции; реализация подключается в корне композиции
public class OrderService(IOrderWriter writer, IClock clock) { }

SRP — не «один метод», а «одна причина для изменения» (один актор-владелец требований). DIP ≠ DI: DIP — принцип направления зависимостей, DI — техника внедрения.

Нюансы и подводные камни

  • Слепое следование SOLID даёт лишние интерфейсы и абстракции без пользы (over-engineering).
  • OCP не значит «никогда не менять код»: расширяемость закладывайте там, где реально ожидаются изменения.
  • Наследование ради переиспользования нарушает LSP; предпочитайте композицию.
  • Интерфейс с одной реализацией ради тестов — допустим, но не обязателен для всего.

Практика

  1. Найдите в коде switch по типу и замените на стратегии.
  2. Разбейте «толстый» интерфейс репозитория на читающий и пишущий.
  3. Найдите нарушение LSP и переработайте иерархию.

Вопросы с ответами

Связанные темы