Статьи

Нарушить принцип единой ответственности

Принцип единой ответственности (ПСП) не является абсолютным. Он существует для облегчения сопровождения кода и его читабельности. Но время от времени вы можете видеть решения, шаблоны, которые нарушают SRP и вроде бы в порядке. Это также верно и для других принципов, но на этот раз я хотел бы поговорить о SRP.

Синглтон ломает СРП

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

  1. Управление созданием экземпляра класса
  2. Сделайте то, что является оригинальной обязанностью класса

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

public class Singleton {
    private static final Singleton instance = new Singleton();
    public static Singleton getInstance() { return instance; }
    private Singleton() {}
}

но такого зверя не так много. Одиночки просты и обсуждаются более чем достаточно в блогах. Позвольте мне взглянуть на что-то более сложное, что нарушает SRP.

Мокито ломает СРП

Mockito  — это  фреймворк , который мы обычно используем в модульных тестах. Я предполагаю, что вы знакомы с  насмешками  и мокито. Типичный тест выглядит следующим образом:

import static org.mockito.Mockito.*;
List mockedList = mock(List.class);
when(mockedList.get(0)).thenReturn("first");
System.out.println(mockedList.get(0));
mockedList.add("one");
mockedList.clear();
verify(mockedList).add("one");
verify(mockedList).clear();

(образец взят со страницы Mockito, фактически смешивая два примера). Макет объекта создается с помощью статического вызова

1
List mockedList = mock(List.class);

и после этого используется для трех разных вещей:

  1. Настройте макет объекта для его насмешливой задачи.
  2. Веди себя как издевательство над издевательством над реальным объектом во время тестирования.
  3. Помощь в проверке использования макета.

Звонок

when(mockedList.get(0)).thenReturn("first");

устанавливает макет объекта. Звонки

System.out.println(mockedList.get(0));
mockedList.add("one");
mockedList.clear();

использовать основную ответственность за макет объекта и, наконец, линии

verify(mockedList).add("one");
verify(mockedList).clear();

действовать как проверка.

Это три разные задачи, а не одна. Я получаю  точку ,  что они тесно связаны друг с другом. Можно даже сказать, что это всего лишь три аспекта одной ответственности. Можно утверждать, что проверка использует только фиктивный объект в качестве параметра, а не функциональность фиктивного объекта. Дело в том, что фиктивный объект отслеживает его фиктивное использование и активно участвует в процессе проверки за кулисами. Хорошо, хорошо: все это может быть правдой, более или менее. Реальный вопрос: имеет ли это значение?

Ну и что?

Нарушается ли читаемость кода Mockito при обработке SRP таким образом? От этого страдает юзабилити API Mockito?

The answer is definite NO for both of the questions. The code is as readable as it gets (imho it is more readable than many other open source projects) but it is not affected by the fact that the mock objects have multiple responsibilities. As for the API you can even say more. It is readable and usable even more with this approach. Former mocking frameworks used strings to specify the method calls like

mailer.expects(once()).method("send");
warehouse.expects(once()).method("hasInventory")

(fragment from the page), which is less readable and error prone. A typo in the name of the method is discovered test run time instead of compile time.

What is the morale? Don’t be dogmatic. Care programming principles, since they are there to help you to write good code. I do not urge anyone to ignore them every day. On the other hand if you feel that some of the principles restrict you and your code would be better without it, do not hesitate to consider writing a code that breaks the principles. Discuss it with your peers (programming is a team work anyway) and come to a conclusion. The conclusion will be that you were wrong considering to break SRP in 90% of the cases. In 10%, however, you may come up with brilliant ideas.