Страницы

Поиск по вопросам

среда, 29 января 2020 г.

Утилитарные классы - добро или зло?

#java #ооп #классы #объекты #static


Есть вот такой спорный вопрос.

Многие используют утилитарные классы, которые состоят из статических методов. Это
всего функции со входом и результатом. Такие используют и библиотеки Java, например,
Math. Многие делают свои классы типа Utils, которые содержат в себе набор используемых
в разных местах инструментов. Я не вижу минусов, с точки зрения SOLID:
S класс создан для общей по смыслу задачи
O да
L неприменимо. Обычно не используется наследование.
I неприменимо. Нет интерефейса
D да. 
Также они отвечают принципу KISS, и легко модульно тестируются при желании. На методы
также можно ссылаться для функционального подхода (напр., Function delCommentsConverter
= QueryUtils::deleteComments). Ну в общем, полноценные роботяги.

Но также есть мнение, что это неверные подход, не по ООП, а Java это ОО-язык. Что
это не класс, и вызов методов напрямую - это очень плохо. Типа нужно сначала получить
инстанс, и только тогда работать с ним. Я не вижу в этом преимуществ, наоборот - растёт
код, неудобно пользоваться.

А как вы думаете?
    


Ответы

Ответ 1



Имхо, статические методы должны: 1) делать очень маленькую специфичную работу; 2) не должны напрямую относиться к бизнес-логике; 3) как следствие 1) и 2) — их не надо мокать; 4) их можно тестировать отдельно. Давайте рассмотрим каждый пункт отдельно. Делать очень маленькую специфичную работу Я имею ввиду, что в статическом методе-помощнике должно быть минимум логики, которая относится только к той одной конкретной операции, для которой метод предназначен. Например, как вы думаете, сколько логики в методe типа Math.min? Также такие методы должны быть чистыми. То есть при одинаковых входных данных они должны возвращать один и тот же результат и они не должны иметь состояния. (Но тут есть исключения, связанные со временем. Например, получение текущего времени в разные моменты будет возвращать разный результат). Не должны напрямую относиться к бизнес-логике Ваша бизнес-логика должна обладать гибкостью. Например, сегодня вы используете один калькулятор зарплат, а завтра — другой. По идее, это должно достигаться просто подменой одной реализации калькулятора другой. Отсутсвие O и L из SOLID по сути связывает статические методы, вы не можете подложить другую реализацию статического метода в рантайме, если только этот статический метод не нарушает предыдущий пункт. Поэтому, если вам надо сделать что-то, что не относится напрямую к бизнес логике и никогда не планируется измениться (например, округлить число, сортировать массив, возвести число в степень, посчитать расстояние между точками, распарсить дату их строки) — вы смело можете писать статические методы. Но если вам надо реализовать бизнес-логику (найти победителя в игре, посчитать налоговую декларацию, обновить значение поля А в зависимости от значения поля Б на форме, отправить сообщение юзеру при возникновении какого-либо события) — то эта логика должна быть максимально гибкой, основные её части должны быть заменяемы, там где надо даже в райтейме — то есть вы не можете делать её на статических методах. Как следствие 1) и 2) — их не надо мокать При тестировании логики, которая использует ваши статические методы, нет никакого смысла их мокать (опять же, если речь идет не о получении текущей даты/времени), так как эти методы к логике не относятся, никогда не получат альтернативной реализации и всегда на один и тот же запрос вернут один и тот же ответ. Вам никогда не понадобится имитация того, что строка "10" была распарсена в 15 или 20. "10" всегда при парсинге будет 10. Их можно тестировать отдельно Тут, по идее, очевидно, если вы следовали предыдущим советам, то у вас ваши статические методы — небольшие, не будут меняться, не имеют состояния и не относятся напрямую к бизнес-логике. В этом случае для тестирования этих методов вам не надо тянуть классы бизнес-логики или мокать их как-либо: вы можете спокойно тестировать эти методы без привязки к логике приложения. Ещё немножко текста в похожем вопросе

Ответ 2



Конечно, статические методы не являются объектно-ориентированным решением, тут апологеты ООП правы. Однако, есть вопросы к самой объектной парадигме. Она идеально подходит для решения одних задач, и совершенно не подходит для других. Собственно, любая парадигма имеет границы применимости. Именно поэтому концепция всё есть объект обрастает многочисленными но. Пример: в DDD разделяют сущности (entities) и службы (services). Службы предназначены для описания процессов, и они не обладают состоянием. С точки зрения «правильного» ООП объекты без состояния — это нонсенс. Но с практической точки зрения в предметной области есть бизнес-процессы. Это последовательности операций, и никакого состояния они не предусматривают. Поэтому в реальных программах чистые концепции дополняются деталями. Более того, выразительность языков программирования связана с тем, как они позволяют выражать нюансы. Возвращаемся к статическим методам и утилитарным классам. В Java и C# они существуют потому, что это простой способ выразить концепцию функции. Можно спорить о том, делать ли sin функцией класса Double или нет, но в классической математике sin это не метод действительного числа, а функция, которая умеет работать с действительным числом. Статические методы позволяют выразить эту концепцию и упростить код. Однако, есть несколько проблем, связанных с тестированием. Вы можете протестировать функции независимо, но если ваш код вызывает статический метод, вы не можете вставить вместо него заглушку. Решения этой проблемы могут быть разными. Например, вместо прямого вызова DateTime.Now в C# можно предусмотреть класс TimeProvider с виртуальным методом Now(). Можно передавать ссылку на функцию. В конце концов, можно не тестировать вызов функций sin или sqrt, а проверять, что результат вычислений попадает в ожидаемый диапазон. Есть также паттерн Окружающий контекст (Ambient context), который позволяет сочетать выразительность статических методов с возможностью подменить реализацию заглушкой. В целом, в практических целях использование статических методов вполне оправдано.

Комментариев нет:

Отправить комментарий