В чём заключается принцип единственной ответственности (SRP) из SOLID?

Ответ

Принцип единственной ответственности (Single Responsibility Principle, SRP) — первый принцип SOLID. Он гласит: «Класс должен иметь одну и только одну причину для изменения».

Это означает, что класс должен быть ответственен за одну, чётко определённую часть функциональности. Все его методы должны быть направлены на решение этой единственной задачи.

Проблема — нарушение SRP:

class Order {
    // Причина для изменения №1: изменилась бизнес-логика заказа
    void calculateTotal() { /* ... */ }
    // Причина для изменения №2: изменилась схема базы данных
    void saveToDatabase() { /* ... */ }
    // Причина для изменения №3: изменился формат печати
    void printInvoice() { /* ... */ }
}
// У класса три причины для изменения — это нарушение SRP.

Решение — разделение ответственности:

// Класс отвечает только за данные и логику заказа.
class Order {
    void calculateTotal() { /* ... */ }
}

// Класс отвечает только за сохранение/загрузку данных.
class OrderRepository {
    void save(Order order) { /* ... */ }
}

// Класс отвечает только за вывод на печать.
class InvoicePrinter {
    void print(Order order) { /* ... */ }
}

Преимущества следования SRP:

  • Упрощение тестирования: Легче тестировать один аспект поведения.
  • Повышение читаемости и поддерживаемости: Классы становятся меньше и понятнее.
  • Снижение риска ошибок: Изменения в одной области функциональности не затронут другие.
  • Упрощение повторного использования: Класс, делающий одно дело, проще использовать в другом контексте.

Ответ 18+ 🔞

Слушай, а вот этот ваш первый принцип SOLID, SRP, он же просто элементарный, как два пальца об асфальт, блядь!

Представь себе мужика, который одновременно и печку топит, и на скрипке играет, и за свиньями ухаживает. Ну и какого хуя? Он же везде накосячит, этот распиздяй! Так и в коде — если класс пытается делать всё на свете, это пиздец, а не архитектура.

Вот смотри, классический пример говнокода, где всё свалено в одну кучу:

class Order {
    void calculateTotal() { /* ... */ }
    void saveToDatabase() { /* ... */ }
    void printInvoice() { /* ... */ }
}

Этот ваш Order — он же, сука, и бухгалтер, и грузчик, и печатник в одном флаконе! Захотелось поменять принтер — трогай класс заказа. Решили в базу новое поле добавить — опять лезь в этот же класс. Это ж какая-то ебля на постном масле, а не разработка!

А теперь правильный подход, где каждый занимается своим делом, как в хорошей артели:

class Order {
    void calculateTotal() { /* ... */ }
}

class OrderRepository {
    void save(Order order) { /* ... */ }
}

class InvoicePrinter {
    void print(Order order) { /* ... */ }
}

Вот видишь? Один — деньги считает, второй — в базу тыкает, третий — бумажки печатает. Красота, ёпта! Каждый как хитрая жопа — знает свой шесток.

И плюсов-то — овердохуища:

  • Тестировать — раз плюнуть. Подсунул заглушку репозиторию и проверяй логику заказа. Никаких танцев с бубном.
  • Читать и менять — вообще сказка. Не надо рыться в трёхэтажном методе, где всё перемешано.
  • Ошибки — они теперь локализованы, как проказа в карантине. Сломался принтер — чини принтер, а не весь заказ целиком.
  • Переиспользовать — да запросто! Захотел считать итоги в мобильном приложении — бери класс Order и тащи его, он же про базы и принтеры нихуя не знает.

В общем, принцип-то простой, как хуй с горы: делай одну вещь, но делай её хорошо, блядь. Не распыляйся, не будь тем самым мудаком-универсалом, который везде успевает и везде косячит.