Статьи

SRP это обман

Согласно принципу чистого кода Роберта Мартина, принцип единой ответственности означает, что «у класса должна быть только одна причина для изменения». Давайте попробуем расшифровать это довольно расплывчатое утверждение и посмотрим, как оно помогает нам создавать лучшее объектно-ориентированное программное обеспечение. Если это так.

Дело Томаса Корона (1999) Джона Мактирнана

Однажды я упомянул SRP в своем посте о SOLID , сказав, что он на самом деле не помогает программистам понять старую добрую концепцию «высокой сплоченности» , которая была введена Ларри Константином еще в 1974 году. Теперь давайте рассмотрим это на примере и проанализируем, как мы можем улучшить класс с учетом SRP, и станет ли он более объектно-ориентированным .

Давайте попробуем класс AwsOcket из jcabi-s3 (я упростил код):

1
2
3
4
5
class AwsOcket {
  boolean exists() { /* ... */ }
  void read(final OutputStream output) { /* ... */ }
  void write(final InputStream input) { /* ... */ }
}

Поправьте меня, если я ошибаюсь, но согласно SRP этот класс отвечает за слишком много вещей: 1) проверка существования объекта в AWS S3, 2) чтение его содержимого и 3) изменение его содержимого. Правильно? Это не хороший дизайн, и его нужно изменить.

Чтобы изменить его и сделать его ответственным только за одну вещь, мы должны ввести геттер, который вернет клиента AWS, а затем создаст три новых класса: ExistenceChecker , ContentReader и ContentWriter . Они будут проверять, читать и писать. Теперь, чтобы прочитать содержимое и распечатать его на консоли, я сейчас делаю это:

1
2
3
if (ocket.exists()) {
  ocket.read(System.out);
}

Завтра, если я проведу рефакторинг класса, я буду делать это:

1
2
3
if (new ExistenceChecker(ocket.aws()).exists()) {
  new ContentReader(ocket.aws()).read(System.out);
}

Помимо того факта, что эти шашки, читатели и писатели на самом деле не классы, а чистые обладатели процедур, использование этого ocket превращается в кошмар. Мы больше не можем знать, что будет с ним, когда мы его где-нибудь передадим. Мы не можем, например, гарантировать, что исходящий от него контент расшифровывается или декодируется на лету. Мы просто не можем это украсить. Это уже не объект, а держатель клиента AWS, который где-то используется некоторыми другими классами.

Да, теперь он отвечает только за одну вещь: инкапсулирует ссылку на клиента AWS. Это идеальный класс для SRP. Но это больше не объект.

То же самое произойдет с любым классом, если вы в полной мере примените принцип SRP: он станет держателем данных или других объектов с набором сеттеров и геттеров поверх них. Может быть, с одним дополнительным методом в дополнение к этим.

Я хочу сказать, что SRP — неправильная идея.

Хорошая идея — сделать классы маленькими и сплоченными, но сделать их ответственными «за одну вещь» — вводящее в заблуждение упрощение концепции «высокой сплоченности». Это только превращает их в глупых носителей чего-то другого, вместо того, чтобы быть инкапсуляторами и декораторами меньших сущностей, чтобы создавать большие.

В нашей борьбе за эту ложную идею SRP мы теряем гораздо более важный принцип, который на самом деле касается настоящего объектно-ориентированного программирования и мышления: инкапсуляция. Гораздо менее важно, за сколько вещей отвечает объект, чем насколько он защищает объекты, которые он инкапсулирует. Объект-монстр с сотней методов представляет собой гораздо меньшую проблему, чем DTO с пятью парами геттеров и сеттеров! Это связано с тем, что DTO распространяет проблему по всему коду, где мы даже не можем ее найти, в то время как объект-монстр всегда перед нами, и мы всегда можем реорганизовать его в более мелкие части.

На первом месте — инкапсуляция, на втором месте — размер.

Опубликовано на Java Code Geeks с разрешения Егора Бугаенко, партнера нашей программы JCG . Смотрите оригинальную статью здесь: SRP — обман

Мнения, высказанные участниками Java Code Geeks, являются их собственными.