Статьи

SOLID — принцип единой ответственности

Принцип единой ответственности (SRP) гласит, что:

Никогда не должно быть более одной причины для изменения класса.

Мы можем связать «причину перемен» с «ответственностью класса». Таким образом, каждая ответственность будет осью для изменений. Этот принцип аналогичен разработке классов, которые очень сплочены. Таким образом, идея состоит в том, чтобы разработать класс, который несет одну ответственность или, другими словами, обслуживает реализацию функциональности. Я хотел бы уточнить здесь, что одна ответственность не означает, что у класса есть только ОДИН метод. Ответственность может быть реализована с помощью различных методов в классе.

Почему этот принцип необходим?

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

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

Один из способов исправить нарушение SRP — разложить функциональные возможности классов на разные классы, каждый из которых подтверждает SRP.

Пример для пояснения этого принципа:

Предположим, вас попросили внедрить службу UserSetting, где пользователь может изменить настройки, но перед этим пользователь должен пройти аутентификацию. Одним из способов реализации этого будет:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public class UserSettingService
{
  public void changeEmail(User user)
  {
    if(checkAccess(user))
    {
       //Grant option to change
    }
  }
  public boolean checkAccess(User user)
  {
    //Verify if the user is valid.
  }
}

Все выглядит хорошо, пока вы не захотите повторно использовать код checkAccess в каком-либо другом месте ИЛИ не захотите внести изменения в способ выполнения checkAccess ИЛИ не захотите внести изменения в способ одобрения изменений электронной почты. Во всех последующих двух случаях вы бы в конечном итоге изменили один и тот же класс, и в первом случае вам придется использовать UserSettingService для проверки доступа, что не является необходимым.

Один из способов исправить это — разложить UserSettingService на UserSettingService и SecurityService. И переместите код checkAccess в SecurityService.

01
02
03
04
05
06
07
08
09
10
public class UserSettingService
{
  public void changeEmail(User user)
  {
    if(SecurityService.checkAccess(user))
    {
       //Grant option to change
    }
  }
}
1
2
3
4
5
6
7
public class SecurityService
{
  public static boolean checkAccess(User user)
  {
    //check the access.
  }
}

Другой пример будет:

Предположим, есть требование загрузить файл — может быть в формате csv / json / xml, проанализировать файл и затем обновить содержимое в базе данных или файловой системе. Один из подходов заключается в следующем:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class Task
{
  public void downloadFile(location)
  {
    //Download the file
  }
  public void parseTheFile(file)
  {
    //Parse the contents of the file- XML/JSON/CSV
  }
  public void persistTheData(data)
  {
    //Persist the data to Database or file system.
  }
}

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

Одним из способов декомпозиции класса Task является создание различных классов для загрузки файла — Downloader, для анализа файла — Parser и для сохранения в базе данных или файловой системе.

Даже в JDK вы, наверное, видели, что Rectangle2D или другие классы Shape в пакете java.awt на самом деле не имеют информации о том, как это должно быть нарисовано в пользовательском интерфейсе. Информация о чертеже была встроена в пакет Graphics / Graphics2D.

Подробное описание можно найти здесь .

Ссылка: SOLID — принцип единой ответственности от нашего партнера JCG Мохамеда Санауллы в блоге «Experiences Unlimited» .

Статьи по Теме :