SOLID: Принцип единственности ответственности

Принцип единственности ответственности  - один из первых в пятерке SOLID-принципов, которые следует использовать каждый раз, когда вы пишете код.

Формулировка

Класс должен иметь только одну обязанность, и она должна быть полностью инкапсулирована внутри.

Почему это так важно? Обратимся к Agile практикам разработки ПО, и тогда причина станет очевидной. Дело в том, что если разные группы команды разработчиков работают над разным функционалом, то смешение обязанностей в один класс приведет к лишним проблемам при параллельной работе. В условиях ежедневных релизов это может стать проблемой, и на тестирование уйдет куда больше времени, чем хотелось бы.

Помимо этого, при соблюдение этого принципа ваш код становится куда более логичным и чистым, его лечге поддерживать и расширять.

Определение обязанностей

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

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

  • модуль приема платежей будут использовать Бухгалтерия и Юристы
  • модуль поиска будут использовать Клиенты и Контент-менеджеры
  • и т.д.

Ключевая идея здесь – разделение. Если Бухгалтерии понадобится, например, изменить принцип формирования квартальных отчетов, то это должно повлечь за собой изменения в коде, относящемся к этой части функционала. Причем, если вдруг Юристам потребуется изменить порядок проведения платежей, не должно быть ситуации, при которой мы начинаем корректировать все тот же участок кода. Можно сформулировать это так:

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

Примеры

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

Такое положение дел в рамках одного класса противоречит принципу единственности ответственности. Поэтому стоит немного переделать пример выше и получить что-то вроде этого:

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

И снова мы видим все те же методы что и ранее, за исключением того, что вместо печати объект может сохранить себя в файл. При изменении способа сохранения (Механизм сохранения данных) мы сталкиваемся с проблемой, так как нам надо вносить изменения в класс, который используют и другие Пользователи (Управление библиотекой).

В этом нарушается принцип единственности ответственности. Исправим:

 Обобщаем

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

Глобально выделяют, например:

  • слой бизнес-логики,
  • слой хранения данных,
  • слой фабрик,
  • слой представления и т.д.

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

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

Менее очевидный пример

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

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

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

Попробуем исправить ситуацию с книгами:

После выделения класса-поисковика все стало на свои места. Изменение принципов поиска или добавление новых никак не повлияет на книгу, и наоборот.

Итого

При написании кода всегда стоит держать в уме принцип единственности ответственности. Это может помочь держать ваш код к чистом и логичном виде. Тем не менее, существует и другая сторона медали. Чрезмерное применение этого метода на ранних этапах может повлечь избыточную “оптимизацию” вашей системы, что в итоге запутает разработку с ранних стадий, и значительно усложнит выделение Пользователей того или иного модуля или класса.

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

 

По материалам

 

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">