Принцип единой ответственности сложен.
Давайте начнем с определения того, что является ответственностью в контексте программной системы и сферы этой ответственности.
Обязанности бывают разнымиразные формы.Все, что вы думаете, может быть ответственностью, и все в вашем коде может иметь ответственность.
У ваших модулей есть обязанности.Если у вас есть Модуль выставления счетов , этот модуль отвечает за обработку счетов.
Теперь, если вы копаете глубже, вы Модуль биллинга может содержать несколько слоев.Каждый слой несет определенную ответственность. Презентация , Бизнес-логика и т. Д.
Копаем глубже, сужая сферу, получаемчто каждый слой состоит из разных классов и / или функций.У каждого из них могут быть обязанности.
Теперь перейдем к функциям и коду внутри.Там вы можете иметь несколько утверждений, если, для, = и т. Д. Каждое утверждение делает что-то, что является ответственностью.Если вы проанализируете свои утверждения, вы заметите, сколько обязанностей выполняет функция / метод.
Если у вас есть UserRepository
, и единственное, что он делает, - это общение с БД или посредник между вами.приложение и ORM, то у него есть ответственность .Это не значит, что у вас Repository
будет один метод.Чтобы придерживаться SRM, в вашем UserRepository
должны быть только методы, связанные с обменом данными с БД, связанные с users
.В нем не должно быть какой-либо бизнес-логики.
Если у вас есть UserService
, и у него есть только операции, связанные с пользователями, то эта служба соответствует SRP, поскольку ее ответственность состоит в том, чтобы иметь операции, связанные с Users
.
Теперь вот чрезвычайно сложная часть SRP.
Чтобы ваш UserService
мог выполнить свою работу, ему нужно позвонитьUserRepository
.Если эта служба создает пользователей, а затем добавляет их в базу данных, не означает ли это, что UserService
несет ответственность за сохранение новых пользователей ?
Мы видим, что у нас есть два отличительныхобязанности здесь.
ответственность , зная, как сделать вещь .UserRepository
знает, как взаимодействовать с ORM или БД и сохранять новых пользователей.
Ответственность , говорящего о том, когда что-то должно быть сделано .UserService
знает, когда User
должен быть сохранен не как или где его следует сохранить .
У вас есть два разныхобязанности.Если ваше постоянство изменится, потому что вы измените как и / или где , вы измените только реализацию Репозитория, но Сервис не будет затронут.Если ваша бизнес-логика меняется, вы меняете , когда , поэтому служба будет меняться, но не репозиторий.
Еще одна вещь, связанная с этим, - это когда интерфейс объекта становится большим.
Люди начинают задаваться вопросом, придерживается ли этот большой интерфейс единственной ответственности - нет?
Если все методы являются связными, это так.Это означает, что размер чего-либо не означает, что это нарушает SRP.Это может нарушать другие принципы, такие как Принцип разделения интерфейса , но это не означает, что оно нарушает SRP.Это просто сложно использовать, читать, изменять и т. Д. В этом случае вы можете разбить его на несколько более мелких вещей.
Вот пример.Допустим, вы храните настройки для своего приложения.Вы можете разработать единый интерфейс ISettingsProvider
, который имеет свойства для всех настроек и приведет к интерфейсу с 50 методами.Если мы определим ответственность как сохранение настроек , этот интерфейс не нарушает SRP.Если мы определим ответственность как сохранение настроек для определенной части приложения , то этот интерфейс нарушит ее.
Приведенный выше пример имеет целью показать, что иногда SRP может быть субъективным и что гранулярность имеет значение.Если вы определяете свои обязанности с меньшей областью, то для того, чтобы придерживаться SRP, вам нужно будет проектировать меньшие интерфейсы, функции классов.
Дело в том, что это древовидная структура.Верхняя сфера его применения шире и состоит из более мелкозернистых областей и так далее.В зависимости от того, где вы смотрите, ваши компоненты / объекты / модули могут придерживаться SRP.
Если у вас есть Модуль биллинга в одном огромном классе, с точки зрения системы, модуль идеально подходит для SRP.С точки зрения ответственности внутри модуля, класс, который реализует модуль, будет иметь бизнес-логику, код связи с БД, построение запросов и т. Д., И этот класс будет нарушать SRP.