Соответственно, окончательная версия принципа единственной ответственности выглядит так:
Модуль должен отвечать за одного и только за одного актора.
Теперь определим, что означает слово «модуль». Самое простое определение – файл с исходным кодом. В большинстве случаев это определение можно принять. Однако некоторые языки среды разработки не используют исходные файлы для хранения кода. В таких случаях модуль – это просто связный набор функций и структур данных.
Слово «связный» подразумевает принцип единственной ответственности. Связность – это сила, которая связывает код, ответственный за единственного актора.
Пожалуй, лучший способ понять суть этого принципа – исследовать признаки его нарушения.
Признак 1: непреднамеренное дублирование
Мой любимый пример – класс Employee из приложения платежной ведомости. Он имеет три метода: >calculatePay()
, >reportHours(
) и >save()
(рис. 7.1).
Рис. 7.1. Класс Employee
Этот класс нарушает принцип единственной ответственности, потому что три его метода отвечают за три разных актора.
• Реализация метода >calculatePay()
определяется бухгалтерией.
• Реализация метода >reportHours()
определяется и используется отделом по работе с персоналом.
• Реализация метода >save()
определяется администраторами баз данных.
Поместив исходный код этих трех методов в общий класс >Employee
, разработчики объединили перечисленных акторов. В результате такого объединения действия сотрудников бухгалтерии могут затронуть что-то, что требуется сотрудникам отдела по работе с персоналом.
Например, представьте, что функции >calculatePay()
и >reportHours()
используют общий алгоритм расчета не сверхурочных часов. Представьте также, что разработчики, старающиеся не дублировать код, поместили реализацию этого алгоритма в функцию с именем >regularHours()
(рис. 7.2).
Рис. 7.2. Общий алгоритм
Теперь вообразите, что сотрудники бухгалтерии решили немного изменить алгоритм расчета не сверхурочных часов. Сотрудники отдела по работе с персоналом были бы против такого изменения, потому что вычисленное время они используют для других целей.
Разработчик, которому было поручено внести изменение, заметил, что функция >regularHours()
вызывается методом >calculatePay()
, но, к сожалению, не заметил, что она также вызывается методом >reportHours()
.
Разработчик внес требуемые изменения и тщательно протестировал результат. Сотрудники бухгалтерии проверили и подтвердили, что обновленная функция действует в соответствии с их пожеланиями, после чего измененная версия системы была развернута.
Разумеется, сотрудники отдела по работе с персоналом не знали о произошедшем и продолжали использовать отчеты, генерируемые функцией >reportHours()
, но теперь содержащие неправильные цифры. В какой-то момент проблема вскрылась, и сотрудники отдела по работе с персоналом разом побледнели от ужаса, потому что ошибочные данные обошлись их бюджету в несколько миллионов долларов.
Все мы видели нечто подобное. Эти проблемы возникают из-за того, что мы вводим в работу код, от которого зависят разные акторы. Принцип единственной ответственности требует разделять код, от которого зависят разные акторы.
Признак 2: слияния
Слияния – обычное дело для исходных файлов с большим количеством разных методов. Эта ситуация особенно вероятна, если эти методы отвечают за разных акторов.
Например, представим, что коллектив администраторов баз данных решил внести простое исправление в схему таблицы >Employee
. Представим также, что сотрудники отдела по работе с персоналом пожелали немного изменить формат отчета, возвращаемого функцией