Single Responsibility (SRP)
Каждый класс должен иметь одну причину для изменения.
▸❌ Нарушение SRP
1public class UserService2{3 public void CreateUser(UserDto dto) { ... }4 public void SendEmail(string to, string body) { ... }5 public void LogAction(string action) { ... }6}
▸✅ SRP
1public class UserService2{3 private readonly IUserRepository _repo;4 private readonly IEmailService _email;5 private readonly ILogger _logger;67 public UserService(IUserRepository repo, IEmailService email, ILogger logger)8 {9 _repo = repo;10 _email = email;11 _logger = logger;12 }1314 public async Task<User> CreateUserAsync(UserDto dto)15 {16 var user = new User(dto);17 await _repo.SaveAsync(user);18 await _email.SendAsync(dto.Email, "Welcome", "...");19 _logger.Log("User created");20 return user;21 }22}
Open/Closed (OCP)
Классы должны быть открыты для расширения, закрыты для модификации.
1// ✅ Использование полиморфизма2public interface IDiscountStrategy3{4 decimal Calculate(decimal price);5}67public class RegularDiscount : IDiscountStrategy8{9 public decimal Calculate(decimal price) => price * 0.9m;10}1112public class VIPDiscount : IDiscountStrategy13{14 public decimal Calculate(decimal price) => price * 0.7m;15}1617public class PriceCalculator18{19 public decimal CalculatePrice(decimal price, IDiscountStrategy strategy)20 {21 return strategy.Calculate(price);22 }23}
Liskov Substitution (LSP)
Подклассы должны быть заменяемы на родительские классы без нарушения поведения.
1// ❌ Нарушение LSP2public class Bird3{4 public virtual void Fly() { ... }5}67public class Ostrich : Bird8{9 public override void Fly()10 {11 throw new NotImplementedException("Ostriches can't fly");12 }13}1415// ✅ LSP16public interface IFlightCapable17{18 void Fly();19}2021public class Sparrow : Bird, IFlightCapable { ... }22public class Ostrich : Bird { ... } // Без IFlightCapable
Interface Segregation (ISP)
Клиенты не должны зависеть от интерфейсов, которые они не используют.
1// ❌ Толстый интерфейс2public interface IWorker3{4 void Work();5 void Eat();6 void Sleep();7}89// ✅ Разделённые интерфейсы10public interface IWorkable11{12 void Work();13}1415public interface IFeedable16{17 void Eat();18}1920public interface ISleepable21{22 void Sleep();23}2425public class Robot : IWorkable { ... }26public class Human : IWorkable, IFeedable, ISleepable { ... }
Dependency Inversion (DIP)
Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций.
1// ❌ Зависимость от конкретного класса2public class SqlDatabase3{4 public void Save(string data) { ... }5}67public class UserService8{9 private readonly SqlDatabase _db = new SqlDatabase(); // Жёсткая связь10}1112// ✅ Зависимость от абстракции13public interface IDatabase14{15 void Save(string data);16}1718public class UserService19{20 private readonly IDatabase _db;2122 public UserService(IDatabase db)23 {24 _db = db; // Внедрение зависимости25 }26}
Заключение
SOLID — это пять принципов, делающие код поддерживаемым и расширяемым. Их применение в C# позволяет создавать гибкие архитектуры, которые легко тестировать и модифицировать.