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

Рис. 7.4. Шаблон «Фасад»
Некоторые разработчики предпочитают держать наиболее важные бизнес-правила как можно ближе к данным. Это можно сделать, сохранив важные методы в оригинальном классе Employee, и затем использовать этот класс как фасад для низкоуровневых функций (рис. 7.5).

Рис. 7.5. Наиболее важные методы остаются в оригинальном классе Employee и используются как фасад для низкоуровневых функций
Вы можете возразить против такого решения на том основании, что каждый класс содержит только одну функцию. Однако в реальности такое едва ли возможно. Количество функций, необходимых для расчета зарплаты, создания отчета или сохранения данных, наверняка будет больше в любом случае. Каждый из таких классов может иметь также много приватных методов.
Каждый такой класс, содержащий подобное семейство методов, образует свою область видимости. Вне этой области никто не знает о существовании приватных членов семейства.
Заключение
Принцип единственной ответственности (Single Responsibility Principle; SRP) касается функций и классов, но он проявляется в разных формах на еще двух более высоких уровнях. На уровне компонентов он превращается в принцип согласованного изменения (Common Closure Principle; CCP), а на архитектурном уровне – в принцип оси изменения (Axis of Change), отвечающий за создание архитектурных границ. Все эти идеи мы обсудим в последующих главах.
8. Принцип открытости/закрытости

Принцип открытости/закрытости (Open-Closed Principle; OCP) был сформулирован Бертраном Мейером в 1988 году [23]. Он гласит:
Программные сущности должны быть открыты для расширения и закрыты для изменения.