Рубрики
Без рубрики

Это не швейцарский армейский нож – Принцип единой ответственности

Принцип единой ответственности (SRP) можно объяснить в двух частях: Класс должен иметь только… С пометкой ооп, java, качество кода, новички.

Принцип единой ответственности (SRP) может быть объяснен в двух частях:

  1. У класса должна быть только одна причина для изменения. – Он хорошо выполняет одну задачу и он меняется только тогда, когда ему нужно выполнить ту же задачу по-другому.

  2. Только один актер должен вызвать изменение. – Актера лучше всего объяснить как личность или роль, которая предъявляет требования, которые потенциально могут потребовать изменения класса.

Почему у моего класса не может быть нескольких обязанностей?

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

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

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

Почему не должно быть нескольких действующих лиц, требующих изменений?

Ваш класс используется для реализации полноценной функциональности (надеюсь, не так много функций). В любой средней сквозной функциональности требования поступают из нескольких источников – например, для бизнес-аналитиков, веб-дизайнеров, администраторов баз данных, сторонних интеграторов и т.д. Если класс выполняет сложную задачу, на которую могут повлиять несколько участников, она будет меняться чаще. Мы увидим это в примере кода ниже.

Это делает ваш код менее надежным, если существует более одного сценария, в котором он может быть изменен. Разработчики, изменившие его для выполнения одного требования, могут не осознавать, какое влияние это может оказать на другое требование, которого они не касались. На самом деле это является причиной большинства конфликтов слияния кода . Два разработчика пытались решить две разные проблемы, изменив один и тот же код .

Давайте посмотрим какой-нибудь код

Рассмотрим приведенный ниже класс, который связан с созданием новой подписки, когда пользователь отправляет форму подписки:

public class SubscriptionCreator {
    private User user;

    public SubscriptionCreator(User user) {
        this.user = user;
    }
    public void createSubcription() {
        if(isUserValid()) {
            updateSubscribers();
            sendEmail();   
        }
    }

    private boolean isUserValid() {
        //validates the details entered by a user
    }

    private void sendEmail() {
        //sends an email to the user that subscription is sucessful
    }

    private void updateSubscribers() {
        // updates subscriber list in a db
    }
}

Класс выглядит довольно простым. Однако мы видим, что он не соответствует SRP:

Несколько причин для изменения – Он выполняет две задачи – проверяет данные, введенные пользователем. Теперь он будет подвержен изменениям по следующим причинам –

  1. Если есть изменения в проверке сведений о пользователе.
  2. Если произошли изменения в структуре электронной почты.
  3. Если произошли изменения в атрибутах пользователя, хранящихся в базе данных.

Несколько участников, вызывающих изменение – Давайте рассмотрим сценарии ниже:

  1. Владельцы продукта хотят, чтобы вы добавили новую проверку, то есть изменение бизнес-логики. Они хотят создавать подписки только в том случае, если пользователю больше 18 лет.
  2. Дизайнеры контента хотят, чтобы вы отображали другое сообщение в электронном письме, если клиент находится в европейском регионе, т.е. изменили логику представления.
  3. Администратор базы данных указывает, что профили пользователей должны иметь атрибут полного имени, т.Е. Изменение логики сохранения.

Как мы можем его улучшить?

Давайте разделим причины изменения (или выполняемые задачи) и заменим их специализированными классами (или интерфейсами), которые лучше инкапсулируют его.

public class SubscriptionCreator {
    private User user;
    private ValidationService validationService;
    private EmailService emailService;
    private DatabaseService databaseService;
    public SubscriptionCreator(User user, ValidationService validationService, EmailService emailService) {
        this.user = user;
        this.validationService = validationService; 
        this.emailService = emailService; 
    }

    public void createSubcription() {
        if(validationService.isUserValid(user)) { 
            databaseService.updateSubscribers(user);            
            emailService.sendEmail(user);   
        }
    }
}

public interface EmailService {
    //only concerned with sending an email
    void sendEmail(User user);
    ...
}

public interface ValidationService {
    //only concerned with validating the user details
    void isUserValid(User user);
    ...
}

public interface DatabaseService {
    //only concerned with database interactions
    void updateSubscribers (User user);
    ...
}

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

Выгоды

  1. Более понятно – Классы обладают высокой сплоченностью, а их методы довольно малочисленны и понятны сами по себе.
  2. Уменьшены конфликты кода – Когда изменения исходят от одного участника, маловероятно, что два разработчика изменят его одновременно.
  3. Предотвращает сопутствующий ущерб – Внедрение новых изменений становится проще по мере уменьшения области их воздействия.

Мысленный эксперимент

  1. Относится ли SRP только к классам? – SRP составляет основу многих хороших вещей. Первоначально он был предложен на уровне класса, но принцип может быть применен на любом уровне – методы, компоненты, модули – и не ограничивается объектно-ориентированным программированием. Он может быть очень хорошо применен в функциональном программировании, а также при рассмотрении функций в качестве основной единицы. Более того, это основа предметно-ориентированного проектирования. Архитектура микросервисов в принципе является реализацией SRP на уровне обслуживания.
  2. Должны ли вы всегда следовать ему? – В большинстве случаев при написании кода будет полезно учитывать SRP. Однако это не эмпирическое правило. Вы должны проявлять осмотрительность при разработке своего решения, и у вас не всегда может быть преимущество в том, что вы прилагаете дополнительные усилия для соблюдения этого принципа.

Оригинал: “https://dev.to/abh1navv/how-solid-is-your-code-single-responsibility-principle-4b7l”