Есть вот такой спорный вопрос.
Многие используют утилитарные классы, которые состоят из статических методов. Это всего функции со входом и результатом. Такие используют и библиотеки Java, например, Math. Многие делают свои классы типа Utils, которые содержат в себе набор используемых в разных местах инструментов. Я не вижу минусов, с точки зрения SOLID:
S класс создан для общей по смыслу задачи
O да
L неприменимо. Обычно не используется наследование.
I неприменимо. Нет интерефейса
D да.
Также они отвечают принципу KISS, и легко модульно тестируются при желании. На методы также можно ссылаться для функционального подхода (напр., Function<String,String> delCommentsConverter = QueryUtils::deleteComments
). Ну в общем, полноценные роботяги.
Но также есть мнение, что это неверные подход, не по ООП, а Java это ОО-язык. Что это не класс, и вызов методов напрямую - это очень плохо. Типа нужно сначала получить инстанс, и только тогда работать с ним. Я не вижу в этом преимуществ, наоборот - растёт код, неудобно пользоваться.
А как вы думаете?
Имхо, статические методы должны:
1) делать очень маленькую специфичную работу;
2) не должны напрямую относиться к бизнес-логике;
3) как следствие 1) и 2) — их не надо мокать;
4) их можно тестировать отдельно.
Давайте рассмотрим каждый пункт отдельно.
Я имею ввиду, что в статическом методе-помощнике должно быть минимум логики, которая относится только к той одной конкретной операции, для которой метод предназначен. Например, как вы думаете, сколько логики в методe типа Math.min
?
Также такие методы должны быть чистыми. То есть при одинаковых входных данных они должны возвращать один и тот же результат и они не должны иметь состояния. (Но тут есть исключения, связанные со временем. Например, получение текущего времени в разные моменты будет возвращать разный результат).
Ваша бизнес-логика должна обладать гибкостью. Например, сегодня вы используете один калькулятор зарплат, а завтра — другой. По идее, это должно достигаться просто подменой одной реализации калькулятора другой. Отсутсвие O и L из SOLID по сути связывает статические методы, вы не можете подложить другую реализацию статического метода в рантайме, если только этот статический метод не нарушает предыдущий пункт. Поэтому, если вам надо сделать что-то, что не относится напрямую к бизнес логике и никогда не планируется измениться (например, округлить число, сортировать массив, возвести число в степень, посчитать расстояние между точками, распарсить дату их строки) — вы смело можете писать статические методы. Но если вам надо реализовать бизнес-логику (найти победителя в игре, посчитать налоговую декларацию, обновить значение поля А в зависимости от значения поля Б на форме, отправить сообщение юзеру при возникновении какого-либо события) — то эта логика должна быть максимально гибкой, основные её части должны быть заменяемы, там где надо даже в райтейме — то есть вы не можете делать её на статических методах.
При тестировании логики, которая использует ваши статические методы, нет никакого смысла их мокать (опять же, если речь идет не о получении текущей даты/времени), так как эти методы к логике не относятся, никогда не получат альтернативной реализации и всегда на один и тот же запрос вернут один и тот же ответ. Вам никогда не понадобится имитация того, что строка "10" была распарсена в 15 или 20. "10" всегда при парсинге будет 10.
Тут, по идее, очевидно, если вы следовали предыдущим советам, то у вас ваши статические методы — небольшие, не будут меняться, не имеют состояния и не относятся напрямую к бизнес-логике. В этом случае для тестирования этих методов вам не надо тянуть классы бизнес-логики или мокать их как-либо: вы можете спокойно тестировать эти методы без привязки к логике приложения.
Ещё немножко текста в похожем вопросе
Конечно, статические методы не являются объектно-ориентированным решением, тут апологеты ООП правы.
Однако, есть вопросы к самой объектной парадигме. Она идеально подходит для решения одних задач, и совершенно не подходит для других. Собственно, любая парадигма имеет границы применимости.
Именно поэтому концепция всё есть объект обрастает многочисленными но. Пример: в DDD разделяют сущности (entities) и службы (services). Службы предназначены для описания процессов, и они не обладают состоянием. С точки зрения «правильного» ООП объекты без состояния — это нонсенс.
Но с практической точки зрения в предметной области есть бизнес-процессы. Это последовательности операций, и никакого состояния они не предусматривают. Поэтому в реальных программах чистые концепции дополняются деталями. Более того, выразительность языков программирования связана с тем, как они позволяют выражать нюансы.
Возвращаемся к статическим методам и утилитарным классам. В Java и C# они существуют потому, что это простой способ выразить концепцию функции. Можно спорить о том, делать ли sin
функцией класса Double
или нет, но в классической математике sin
это не метод действительного числа, а функция, которая умеет работать с действительным числом.
Статические методы позволяют выразить эту концепцию и упростить код.
Однако, есть несколько проблем, связанных с тестированием. Вы можете протестировать функции независимо, но если ваш код вызывает статический метод, вы не можете вставить вместо него заглушку.
Решения этой проблемы могут быть разными. Например, вместо прямого вызова DateTime.Now
в C# можно предусмотреть класс TimeProvider
с виртуальным методом Now()
. Можно передавать ссылку на функцию. В конце концов, можно не тестировать вызов функций sin
или sqrt
, а проверять, что результат вычислений попадает в ожидаемый диапазон.
Есть также паттерн Окружающий контекст (Ambient context), который позволяет сочетать выразительность статических методов с возможностью подменить реализацию заглушкой.
В целом, в практических целях использование статических методов вполне оправдано.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Для List коллекции на такой случай есть расширенный итератор ListIterator
Каким образом можно обфусцировать api-key от firebase в android приложении на javaApi-ключ лежит в google-services
При попытке получить connection через DriverManager получал SQLExceptionЭто все на tomcat
Как сохранить presenter при смене конфигурации телефона, например при повороте? Прочитал много способов, но так и не понял, какой лучше всего использоватьЗнаю...