Что такое SOLID и как эти принципы влияют на разработку ПО

Ответ

SOLID — это мнемонический акроним для пяти ключевых принципов объектно-ориентированного проектирования, сформулированных Робертом Мартином. Их цель — создание более понятных, гибких и поддерживаемых программных систем.

Следование этим принципам помогает избежать "хрупкого" кода, который ломается при малейших изменениях, и упрощает рефакторинг и расширение функциональности.

Принципы SOLID:

  • S - Single Responsibility Principle (Принцип единственной ответственности) Класс должен иметь только одну причину для изменения, то есть выполнять только одну основную задачу.
  • O - Open/Closed Principle (Принцип открытости/закрытости) Программные сущности (классы, модули) должны быть открыты для расширения, но закрыты для модификации.
  • L - Liskov Substitution Principle (Принцип подстановки Барбары Лисков) Объекты в программе должны быть заменяемы на экземпляры их подтипов без изменения правильности выполнения программы.
  • I - Interface Segregation Principle (Принцип разделения интерфейса) Лучше иметь много специализированных интерфейсов, чем один универсальный.
  • D - Dependency Inversion Principle (Принцип инверсии зависимостей) Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба должны зависеть от абстракций.

Пример (Нарушение и исправление SRP):

Неправильно: Класс Order отвечает и за данные заказа, и за его сохранение в базу данных.

class Order:
    def __init__(self, items):
        self.items = items

    # Этот метод нарушает SRP
    def save_to_database(self):
        # ... сложная логика подключения к БД и сохранения ...
        print("Order saved to DB")

Правильно: Ответственность за сохранение вынесена в отдельный класс OrderRepository.

class Order:
    def __init__(self, items):
        self.items = items

class OrderRepository:
    def save(self, order: Order):
        # ... логика сохранения ...
        print(f"Saving order with {len(order.items)} items to DB")

# Использование:
my_order = Order(["item1", "item2"])
repository = OrderRepository()
repository.save(my_order)

Ответ 18+ 🔞

А, смотри-ка, SOLID! Это ж тот самый набор принципов, который дядя Роберт Мартин вывел, чтобы код не превращался в такую кашу, что сам чёрт ногу сломит. По сути, это пять правил, чтобы твоё творение не разваливалось как карточный домик от одного чиха и чтобы другие программисты, глядя на него, не хотели тебя найти и ебать в сраку.

Вот они, эти священные заповеди, расшифровываются так:

S — Single Responsibility (Принцип единственной ответственности) Это про то, что каждый класс должен заниматься своим делом, а не пытаться быть швецом, жнецом и на дуде игрецом одновременно. Одна причина для изменений — и всё. Не надо в один класс пихать и логику, и работу с базой, и отправку писем, а то получится монстр, которого потом сам же и будешь проклинать.

O — Open/Closed (Принцип открытости/закрытости) Сущности должны быть открыты для расширения, но закрыты для модификации. То есть, ты должен иметь возможность добавить новую фичу, не перелопачивая и не ломая старый, отлаженный код. Как будто пристраиваешь новую комнату к дому, а не сносишь несущую стену, чтобы поставить диван.

L — Liskov Substitution (Принцип подстановки Лисков) Если у тебя есть, скажем, класс Утка, и от него наследуется класс РезиноваяУтка, то везде, где в коде ожидается Утка, должна спокойно работать и РезиноваяУтка. Если подмена ломает логику — значит, ты накосячил с иерархией, и твоя резиновая утка — какая-то пиздопроебибна, а не настоящая утка.

I — Interface Segregation (Принцип разделения интерфейса) Не надо делать один жирный-прежирный интерфейс на все случаи жизни. Лучше наделать кучу маленьких и точных. А то получится, как если бы ты заставлял и принтер, и сканер, и кофеварку реализовывать метод scan(). Кофеварке-то нахуй он сдался?

D — Dependency Inversion (Принцип инверсии зависимостей) Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций. Проще говоря, не привязывайся к конкретной реализации (типа к MySQL), а работай через интерфейс (типа БазаДанных). Тогда когда начальство скажет «переезжаем на PostgreSQL», ты не будешь рыдать, а просто подсунешь другую реализацию.

Вот тебе наглядный пиздец, простите, пример с первым принципом (SRP):

Код-уродец (делает всё и сразу):

class Order:
    def __init__(self, items):
        self.items = items

    # Смотри-ка, блядь, он ещё и в базу умеет сохраняться! Герой!
    def save_to_database(self):
        # ... тут он и с SQL борется, и с коннектами...
        print("Order saved to DB")

Вот этот класс — тот ещё распиздяй. Он и заказ хранит, и в базу лезет. Изменять его придётся и когда поменяется структура заказа, и когда сменится база данных. Причин для изменения — овердохуища.

А теперь код с мозгом:

class Order:
    def __init__(self, items):
        self.items = items

class OrderRepository:
    # А вот это уже специалист. Его дело — тыкать в базу. И только.
    def save(self, order: Order):
        # ... вся грязная работа с БД тут ...
        print(f"Saving order with {len(order.items)} items to DB")

# Использование:
my_order = Order(["item1", "item2"])
repository = OrderRepository()
repository.save(my_order)

Вот так-то лучше. Order теперь просто контейнер для данных, а OrderRepository — мастер по сохранению. Каждый занят своим делом. Красота, ёпта. Никакой лишней головной боли.