Почему использование статических вспомогательных методов в Java плохо?
Я спрашиваю, потому что я пытаюсь использовать макет (Mockito), который не позволяет вам высмеивать статические методы. Просматривая его, я нашел немало постов в блоге, в которых говорилось, что у вас должно быть как можно меньше статических методов, но мне трудно понять, почему. В частности, почему методы, которые не изменяют глобальное состояние и являются в основном вспомогательными методами. Например, у меня есть класс под названием ApiCaller
это имеет несколько статических методов. Одна из целей статического метода - выполнить HTTP-вызов, решить любые пользовательские проблемы, которые мог вернуть наш сервер (например, пользователь не вошел в систему), и вернуть ответ. Чтобы упростить, что-то вроде:
public class ApiCaller {
...
public static String makeHttpCall(Url url) {
// Performs logic to retrieve response and deal with custom server errors
...
return response;
}
}
Чтобы использовать это все, что мне нужно сделать, это позвонить ApiCaller.makeHttpCall(url)
Теперь я могу легко сделать этот нестатический метод, например:
public class ApiCaller {
...
public String makeHttpCall(Url url) {
// Performs logic to retrieve response and deal with custom server errors
...
return response;
}
}
а затем использовать этот вызов метода new ApiCaller().makeHttpCall()
но это только кажется лишним. Может кто-нибудь объяснить, почему это плохо, и есть ли лучшее решение сделать методы не статичными (кроме простого удаления ключевого слова), чтобы я мог заглушить эти методы с помощью фреймворка?
Спасибо!
4 ответа
Проблема со статическими методами в том, что их очень сложно подделать, когда они не имеют отношения к системе, которую вы пытаетесь протестировать. Представьте себе этот код:
public void systemUnderTest() {
Log.connectToDatabaseForAuditing();
doLogicYouWantToTest();
}
connectToDatabaseForAuditing()
Метод статичен. Вам все равно, что этот метод делает для теста, который вы хотите написать. Но, чтобы протестировать этот код сейчас вам нужна доступная база данных.
Если бы он не был статичным, код выглядел бы так:
private Logger log; //instantiate in a setter AKA dependency injection/inversion of control
public void systemUnderTest() {
log.connectToDatabaseForAuditing();
doLogicYouWantToTest();
}
И ваш тест будет легко написать без базы данных:
@Before
public void setUp() {
YourClass yourClass = new YourClass();
yourClass.setLog(new NoOpLogger());
}
//.. your tests
Представьте, что вы пытаетесь сделать это, когда метод статичен. Я не могу придумать, кроме как изменить регистратор, чтобы статическая переменная называлась inTestMode
что вы установили на истину в setUp()
чтобы убедиться, что он не подключается к базе данных.
Это менее модульно. Вместо этого вы должны определить интерфейс ApiCaller
с методом экземпляра makeHttpCall()
так что вы можете определить отдельные реализации в будущем.
По крайней мере, у вас всегда будет 2 реализации интерфейса, оригинальная и проверенная версия.
(Примечание: есть некоторые фальшивые фреймворки, которые позволяют вам симулировать статические методы)
В качестве дополнения, хотя это может не иметь место в вашем конкретном приложении, обычно использование статических методов свидетельствует о большем упущении дизайна. Проектирование для модульности и возможности повторного использования должно быть распространено во всем приложении, потому что даже если вам это не нужно прямо сейчас, оно может понадобиться вам в будущем, и гораздо сложнее и намного больше времени изменить ситуацию после факта.
PRIVATE Статические вспомогательные методы не плохие, на самом деле, они действительно предпочтительнее в большой корпорации, где я работаю. И я использую Mockito с ними все время, доступ к которым осуществляется из методов, вызывающих статический вспомогательный метод.
Существует небольшая разница в том, как компилятор обрабатывает статический вспомогательный метод. Созданный байт-код приведет к команде invokestatic, и если вы удалите статический файл, результатом будет одна из других инструкций, например, invokespecial. Разница в том, что invokestatic загружает класс для доступа к методу, где invokespecial сначала выталкивает объект из стека. Так что может быть небольшое преимущество в производительности (возможно, нет).
Это объяснение кажется мне очень прямым и легким для понимания.
Объявите служебные методы как
static
, часто упрощает понимание вашего кода, и это хорошо.
Но у этого подхода есть серьезное ограничение: такие методы / классы нелегко высмеять.
Поэтому, если вспомогательный метод имеет какую-либо внешнюю зависимость (например, БД), которая делает его - а значит, и вызывающие его - трудными для модульного тестирования, лучше объявить его нестатическим.
Это позволяет внедрять зависимости, облегчая тем самым модульное тестирование вызывающих методов.
Источник: /questions/7422661/kakoj-prefiks-vyi-ispolzuete-dlya-peremennyih-chlenov/7422683#7422683
То, что вы не можете легко издеваться над ними, когда вам нужно в значительной степени ответить на свой вопрос.
Особенно, когда это показано на рисунке: выполнение HTTP-вызова обходится дорого, и делать это для модульного тестирования вашего кода не имеет смысла - оставьте это для интеграционного тестирования.
Модульные тесты требуют известных ответов (и кодов ответов) от HTTP-вызовов, чего вы не можете сделать, если вы звоните в чужую службу, используя сеть, которую вы не контролируете.