Подтвердить что ты не робот

Почему использование статических вспомогательных методов в 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;
   }
}

а затем использовать этот метод call new ApiCaller().makeHttpCall(), но это просто лишние накладные расходы. Может ли кто-нибудь объяснить, почему это плохо, и если есть лучшее решение для того, чтобы сделать методы не статическими (за исключением простого удаления ключевого слова), чтобы я мог заглушить эти методы с помощью насмешливой структуры?

Спасибо!

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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