Утилиты - это зло?
Я видел этот поток
Если "Утилиты" класс - это зло, где я помещаю свой общий код?
и подумал, почему классы полезности злые?
Допустим, у меня есть модель домена, которая является десятком классов. Мне нужно иметь возможность экземпляров xml-ify. Создать метод toXml для родителя? Создать класс помощника MyDomainXmlUtility.toXml? Это тот случай, когда потребность бизнеса охватывает всю модель домена - действительно ли она относится к методу экземпляра? Что, если в функциональности xml приложения есть куча вспомогательных методов?
Ответы
Ответ 1
Утилиты не совсем злые, но они могут нарушать принципы, которые составляют хороший объектно-ориентированный дизайн. В хорошем объектно-ориентированном дизайне большинство классов должны представлять одну вещь, а все атрибуты и операции. Если вы работаете над чем-то, этот метод должен, вероятно, быть членом этой вещи.
Однако есть моменты, когда вы можете использовать классы утилиты для группировки нескольких методов вместе - примером является класс java.util.Collections
, который предоставляет ряд утилит, которые можно использовать в любой коллекции Java. Они не относятся к конкретному типу коллекции, но вместо этого реализуют алгоритмы, которые могут использоваться в любой коллекции.
На самом деле, вам нужно подумать о своем дизайне и определить, где именно смысл использовать методы. Обычно это как операции внутри класса. Однако иногда это действительно как класс полезности. Однако, когда вы используете класс утилиты, не просто бросайте в него случайные методы - организуйте методы по назначению и функциональности.
Ответ 2
Я думаю, что общий консенсус в том, что классы полезности сами по себе не являются злом. Вам просто нужно использовать их разумно:
-
Создайте статические методы утилиты для общего и многоразового использования. Убедитесь, что они не имеют гражданства; т.е. нет статических переменных.
-
Если у вас много полезных методов, разделите их на классы таким образом, чтобы разработчикам было легко найти их.
-
Не используйте классы утилиты, где статические методы или методы экземпляра в классе домена будут лучшим решением. Например, рассмотрите, было бы лучшим решением решение в абстрактном базовом классе или экземпляре вспомогательного класса.
-
Для Java 8, "методы по умолчанию" в интерфейсе могут быть лучшим вариантом, чем классы утилиты.
Другой способ взглянуть на этот вопрос - заметить, что в цитируемом Вопросе "Если классы полезности являются" злыми ", это аргумент соломона. Его, как я спрашиваю:
"Если свиньи могут летать, я должен носить зонтик?".
В вышеупомянутом вопросе я на самом деле не говорю, что свиньи могут летать... или что я согласен с предложением, что они могут летать.
Типичные утверждения "xyz is evil" - это риторические устройства, которые призваны заставить вас думать, создавая экстремальную точку зрения. Они редко (если вообще когда-либо) предназначены как утверждения буквального факта.
Ответ 3
Классы полезности являются проблематичными, поскольку они не могут группировать обязанности с данными, которые их поддерживают.
Они, однако, чрезвычайно полезны, и я все время их строю как постоянные структуры или как ступеньки во время более тщательного рефакторинга.
Из классов Clean Code перспективы нарушают Единую Ответственность и Открытый Принцип Закрытия. У них есть много причин для изменения и по дизайну не расширяемость. Они действительно должны существовать только во время рефакторинга в качестве промежуточного рывка.
Ответ 4
Я полагаю, что он начинает злиться, когда
1) Он становится слишком большим (просто группируйте их в значимые категории в этом случае).
2) Существуют методы, которые не должны быть статическими.
Но пока эти условия не выполняются, я думаю, что они очень полезны.
Ответ 5
Классы полезности плохо, потому что они означают, что вы слишком ленились, чтобы придумать лучшее имя для класса:)
Говоря, я ленив. Иногда вам просто нужно выполнить задание, а ваш ум - пустое, что когда классы "Утилиты" начнут ползать.
Ответ 6
Очень легко создать что-то полезное просто потому, что дизайнер не мог придумать подходящее место для размещения кода. Часто бывает мало настоящих "утилит".
Как правило, я обычно храню код в пакете, где он впервые используется, а затем рефакторинг только в более общем месте, если я нахожу, что позже он действительно нужен в другом месте. Единственное исключение - если у меня уже есть пакет, который выполняет аналогичные/связанные функции, и код лучше всего подходит там.
Ответ 7
Я не совсем согласен, что классы полезности являются злыми.
В то время как класс утилиты может в какой-то мере нарушать принципы OO, они не всегда плохие.
Например, представьте, что вам нужна функция, которая очистит строку всех подстрок, соответствующих значению x.
stl С++ (на данный момент) напрямую не поддерживает это.
Вы можете создать полиморфное расширение std::string.
Но проблема в том, действительно ли вы хотите, чтобы каждая строка, которую вы используете в своем проекте, была вашим расширенным классом строк?
Бывают случаи, когда ОО не имеет смысла, и это один из них. Мы хотим, чтобы наша программа была совместима с другими программами, поэтому мы будем придерживаться std::string и создадим класс StringUtil_ (или что-то еще).
Я бы сказал, что лучше всего, если вы придерживаетесь одного использования для каждого класса. Я бы сказал, что глупо иметь один утилит для всех классов или много utils для одного класса.
Ответ 8
Полезные классы, содержащие статичные статичные методы, могут быть полезны. Они часто очень легки для unit test.
Ответ 9
Утилиты - это зло, хотя они могут выглядеть очень полезными и удобными. Этот пост объясняет это более подробно: http://www.yegor256.com/2014/05/05/oop-alternative-to-utility-classes.html Если вы пишете истинное объектно-ориентированное программное обеспечение, вы должны использовать объекты вместо этого, независимо от того, сколько из них вы создадите.
Ответ 10
С Java 8 вы можете использовать статические методы в интерфейсах... проблема решена.
Ответ 11
Большинство классов Util являются плохими, потому что:
- Они расширяют возможности методов. Они делают общедоступным код, который в противном случае был бы приватным. Если метод утилиты стабилен (т.е. Не нуждается в обновлении), лучше, на мой взгляд, копировать и вставлять частные вспомогательные методы, чем раскрывать его как API и затруднять понимание того, что точка входа в общедоступный компонент (вы поддерживать иерархическую иерархию дерева с одним родителем на один метод, который легче мысленно разделять на компоненты, которые сложнее, когда вы вызываете методы из нескольких родительских методов).
- Они приводят к мертвому коду. Утилитные методы со временем становятся неиспользованными, так как ваше приложение развивается, и вы получаете неиспользуемый код, загрязняющий вашу базу кода. Если бы он оставался конфиденциальным, ваш компилятор скажет вам, что метод не используется, и вы можете просто удалить его (лучший код вообще не является кодом).
Есть несколько аналогий с статическими или динамическими библиотеками.
Ответ 12
Правило большого пальца
Вы можете посмотреть эту проблему с двух сторон:
- В целом методы
*Util
часто представляют собой идею плохого проектирования кода или ленивого соглашения об именах.
- Это законное дизайнерское решение для многократных межсекторальных функций без гражданства. Обратите внимание, что для почти всех распространенных проблем существуют существующие решения.
Пример 1. Правильное использование классов/модулей util
. Пример внешней библиотеки
Предположим, вы пишете приложение, которое управляет кредитами и кредитными картами. Данные из этих модулей отображаются через веб-службы в формате json
.
В теории вы можете вручную преобразовать объекты в строки, которые будут в json
, но это будет изобретать колесо. Правильным решением было бы включить в оба модуля внешнюю библиотеку, используемую для преобразования объектов Java в желаемый формат. (в примере изображение, которое я показал gson)
![введите описание изображения здесь]()
Пример 2. Правильное использование классов/модулей util
. Написание собственных util
без оправданий другим членам команды
В качестве примера предположим, что нам нужно выполнить некоторые вычисления в двух модулях приложения, но оба они должны знать, когда в Польше проводятся государственные праздники. Теоретически вы можете делать эти вычисления внутри модулей, но было бы лучше извлечь эту функциональность для разделения модуля.
Вот небольшая, но важная деталь. Класс/модуль, который вы написали, не называется HolidayUtil
, но PolishHolidayCalculator
. Функционально это класс util
, но нам удалось избежать родового слова.
![введите описание изображения здесь]()
Ответ 13
Оглядываясь назад на этот вопрос, я бы сказал, что методы расширения С# полностью уничтожают необходимость в служебных классах. Но не все языки имеют такую гениальную конструкцию.
У вас также есть JavaScript, где вы можете просто добавить новую функцию прямо к существующему объекту.
Но я не уверен, что действительно есть элегантный способ решить эту проблему на более старом языке, таком как С++...
Хороший код OO очень трудно писать, и его трудно найти, поскольку для написания Good OO требуется гораздо больше времени/знаний, чем написания достойного функционального кода.
И когда вы находитесь в бюджете, ваш босс не всегда рад видеть, что вы целый день написали кучу классов...
Ответ 14
Когда я не могу добавить метод в класс (скажем, Account
заблокирован от изменений разработчиками Jr.), я просто добавляю несколько статических методов в мой класс Utilities, например:
public static int method01_Account(Object o, String... args) {
Account acc = (Account)o;
...
return acc.getInt();
}