Почему использование статических вспомогательных методов в 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) Теперь я мог бы легко сделать это 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() но это просто похоже на дополнительные накладные расходы. Может ли кто-нибудь объяснить, почему это плохо, и если есть лучшее решение для того, чтобы сделать методы не статическими (за исключением простого удаления ключевого слова), чтобы я мог заглушить эти методы с помощью насмешливой структуры?

Благодаря!

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

 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 которую вы установили в true в setUp() чтобы убедиться, что она не подключается к базе данных.

Он менее модульный. Вместо этого вы должны определить интерфейс ApiCaller с помощью метода экземпляра makeHttpCall() чтобы вы могли определять отдельные реализации в будущем.

По крайней мере, вы всегда будете иметь 2 реализации интерфейса, оригинальную и издеваемую версию.

(Примечание: есть некоторые издевательские рамки, которые позволяют вам издеваться над статическими методами)

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

То, что вы не можете издеваться над ними легко, когда вам нужно в значительной степени ответить на собственный вопрос.

В частности, когда это что-то, что показано: сделать HTTP-вызов дорогостоящим, и для этого для модульного тестирования ваш код не имеет смысла – сохраните его для тестирования интеграции.

Модульные тесты требуют известных ответов (и кодов ответов) из HTTP-вызовов, чего вы не можете сделать, если вы вызываете чужую службу, используя сеть, которую вы не контролируете.

ЧАСТНЫЕ Статические вспомогательные методы неплохие, по сути, они на самом деле предпочтительны в крупной корпорации, где я работаю. И я использую Mockito с ними все время, обращаясь к методам, которые называют статическим вспомогательным методом.

Существует небольшая разница в том, как компилятор рассматривает статический вспомогательный метод. Созданный байт-код приведет к команде invokestatic , и если вы удалите статичность, результат будет одной из других инструкций, например invokespecial. Разница в том, что invokestatic загружает класс для доступа к методу, где invokespecial сначала выталкивает объект из стека. Таким образом, может быть небольшое преимущество в производительности (возможно, нет).

Intereting Posts
Ошибка во время выполнения Parse.com – андроид Создание шейдеров OpenGL в NativeActivity Просмотр истории Git в Android Studio Как настроить Android SDK для Titanium в Mac OS Lion? Элемент списка Android не отображает выбранный цвет Дважды нажмите кнопку Android back back, чтобы выйти из приложения. Как отслеживать push-уведомления FCM отправлять серверную страницу формы или Rest Client? Как скомпилировать и запустить программу C / C ++ в системе Android (например, MinGW в Windows)? Программно сжать видео без FFMPEG в Android Egl_emulation eglsurfaceattrib не реализована ошибка в приложении android Эквивалент NavUtils, если вы не используете библиотеку поддержки Android? Groovy, Scala, Clojure и т. Д. В Android Метод Robotium ClickOnButton (int ID) вызывает «junit.framework.AssertionFailedError: кнопка с индексом 2131034130 недоступна!» Приложение AChartengine неожиданно остановилось. Пожалуйста, попробуйте еще раз Добавить MenuItem в NavigationView с помощью значка и названия?