Single Responsibility Principle (SRP) — принцип единственной ответственности

1 минута чтения #php#solid

SRP (Single Responsibility Principle) — это первый и, пожалуй, самый фундаментальный принцип из SOLID. Он гласит:

Класс должен иметь только одну причину для изменения.

Проще говоря — класс должен отвечать за что-то одно.
Если класс делает слишком много вещей, он становится трудно поддерживаемым, сложным для тестирования и увеличивает риск ошибок при изменениях.

❌ Плохой пример: класс делает слишком много

class UserRegistration
{
    public function register(array $data): void
    {
        // 1. Валидация
        if (empty($data['email'])) {
            throw new Exception('Email required');
        }

        // 2. Сохранение в базу
        // ... код сохранения ...

        // 3. Отправка письма
        mail($data['email'], 'Welcome!', 'Thank you for registration');

        // 4. Логирование
        file_put_contents('log.txt', "User {$data['email']} registered\n", FILE_APPEND);
    }
}

Этот класс нарушает SRP, потому что отвечает сразу за:

  • валидацию
  • работу с базой
  • отправку email
  • логирование

Любое изменение (например, смена SMTP-сервера) требует правок в этом же классе.

✅ Хороший пример: разделяем ответственности
class Validator
{
    public function validateRegistration(array $data): void
    {
        if (empty($data['email'])) {
            throw new Exception('Email required');
        }
    }
}

class UserRepository
{
    public function save(array $data): void
    {
        // код сохранения в базу
    }
}

class Mailer
{
    public function sendWelcome(string $email): void
    {
        // отправка письма
    }
}

class Logger
{
    public function log(string $message): void
    {
        file_put_contents('log.txt', $message . "\n", FILE_APPEND);
    }
}

class UserRegistrationService
{
    public function __construct(
        private Validator $validator,
        private UserRepository $users,
        private Mailer $mailer,
        private Logger $logger
    ) {}

    public function register(array $data): void
    {
        $this->validator->validateRegistration($data);
        $this->users->save($data);
        $this->mailer->sendWelcome($data['email']);
        $this->logger->log("User {$data['email']} registered");
    }
}

Теперь:

  • каждый класс отвечает за свою задачу;
  • менять или тестировать каждую часть проще;
  • UserRegistrationService стал оркестратором, а не “монстр-классом”.

🔍 Как понять, что класс нарушает SRP?

Задай себе три вопроса:

  1. Есть ли у этого класса несколько ролей?
  2. Если в системе изменится X, нужно ли менять этот класс?
  3. Трудно ли описать назначение класса одним предложением?

Если ты не можешь кратко объяснить, чем занимается класс — он точно нарушает SRP.

📌 Где чаще всего нарушается SRP?

  1. Контроллеры, которые делают и валидацию, и бизнес-логику.
  2. Модели, которые одновременно отвечают за данные, валидацию, отправку писем, логику.
  3. Сервисы, которые со временем обрастают множеством несвязанных методов.
  4. Помощники и Utils, куда скидывают всё подряд.

🧩 Итог

SRP помогает писать код, который:

  • проще поддерживать;
  • легче тестировать;
  • легче расширять;
  • понятнее другим разработчикам.

Это фундамент для всей архитектуры.
Если не соблюдать SRP — остальные принципы SOLID тоже дадутся тяжелее.