AntiPattern использует классы *** Helper или *** Util, содержащие только статические методы

19

Я часто сталкиваюсь с вспомогательными или утилитарными классами на Java или на любом другом языке. Поэтому я спрашивал себя, не является ли это своего рода Anti Pattern, а существование таких классов - просто отсутствие недостатков в дизайне и архитектуре программного обеспечения.

Часто эти классы ограничиваются использованием только статических методов, которые делают много вещей. Но в основном это действительно зависит от контекста и состояния.

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

И на каком уровне абстракции вы бы избегали использования таких классов?

На мой взгляд, ключевое слово «static» должно быть разрешено только в объявлении класса (Java), а не для методов. По моему мнению, если использовать его таким образом, это может быть хорошей альтернативой и средним способом, чтобы иметь возможность комбинировать Procuedural- и OO-Paradigms в Java и избежать неправильного использования ключевого слова.

Дополнения из-за ответов:

Сначала я думаю, что вполне законно иметь возможность комбинировать различные парадигмы и даже использовать интерпретируемые во время выполнения языки сценариев в скомпилированном коде машины или vm.

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

Я думаю, staticследует удалить из Java. Особенно сейчас, когда можно использовать еще более сложные функциональные языковые элементы.

разнообразие
источник
Я думаю, что если помощнику не нужно создавать экземпляр класса, это нормально. Проблема в том, что помощник создает конкретную реализацию класса, и тогда вы не можете смоделировать этот интерфейс. Если помощник получает интерфейс или абстрактный класс, я думаю, что все в порядке.
Бобек
Это нормально, если вы заявляете, что занимаетесь процедурным программированием. Это не так, если вы утверждаете, что занимались объектно-ориентированным программированием.
Пятнистый
1
@Spotted: и поскольку нет никакой коммерческой ценности в заявлении о том, что вы занимаетесь исключительно объектно-ориентированным программированием, просто всегда занимайтесь смешанным программированием парадигмы и выполняйте дерьмо.
RemcoGerlich
@RemcoGerlich Точно. Моя точка зрения состояла в том, чтобы отметить, что ответ в основном «философский», связанный с какой парадигмой программирования вы его рассматриваете.
Пятнистый

Ответы:

20

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

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

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

Deduplicator
источник
2
@TimothyTruckle Либо они статичны, либо лишние this, и требуют, чтобы что-то было
создано
6
@TimothyTruckle Потому что это Java. Другие языки могут иметь свободные функции без необходимости помещать их в класс только для того, чтобы объявить, что он не является методом экземпляра. И в какой-то момент вы должны иметь тесную связь. Является ли это хорошо или нет, полностью зависит от того , что рассматриваемая функция делает .
Nvoigt
5
@TimothyTruckle Безусловно, поддержание состояния было бы главной причиной того, чтобы быть «не хорошим».
Nvoigt
2
@nvoigt, я чувствую, что этот ответ был бы значительно улучшен, если явно указать этот момент. Статические статики плохие; лица без гражданства хороши. На самом деле довольно просто.
Дэвид Арно,
3
@TimothyTruckle Это не жесткая связь. Вы описываете слабую связь. Тесная связь в этом контексте подразумевает некоторую взаимозависимость. Односторонняя зависимость не отвечает этому требованию.
JimmyJames
18

Статические утилиты или вспомогательные функции не являются антишаблоном, если они придерживаются некоторых рекомендаций:

  1. Они должны быть свободны от побочных эффектов

  2. Они используются для специфического поведения приложения, которое не принадлежит классу или классам, над которыми работают эти функции

  3. Они не требуют какого-либо общего состояния

Общие случаи использования:

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

Например, в приложении, над которым я работал, пользователи хранят записи в журнале для своих действий. Они могут указать дату отслеживания и загрузить напоминание о событии. Мы создали статический служебный класс, чтобы взять запись журнала и вернуть необработанный текст для файла .ics.

Это не требует какого-либо общего состояния. Оно не изменяло никакое состояние, и создание события iCal определенно зависело от приложения и не входило в класс записей журнала.

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

Грег Бургхардт
источник
4

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

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

Это правильный подход и может работать очень хорошо, особенно в масштабе.

Tracker1
источник
"может работать очень хорошо, особенно в масштабе" Нет. Тесная связь, вызванная статическим доступом , скоро встанет у вас на пути развития проекта.
Тимоти Траклле
@TimothyTruckle Не могли бы вы объяснить, как Agent.Save (obj) гораздо более тесно связаны, чем obj.Save ()? Агент также способен получать ссылку DI / IoC, даже для статических методов, как экземпляр класса. Для справки: Stack Exchange написан с таким типом мышления и масштабируется лучше, чем любое другое приложение ASP.Net с меньшим количеством ресурсов, чем любое другое, которое я знаю.
Tracker1
«Не могли бы вы объяснить, как Agent.Save (obj) гораздо более тесно связан, чем obj.Save ()?» Это неправильный пример. Правильный пример будет Agent.Save(obj)против agentInstance.Save(obj). С последним у вас есть возможность внедрить agentInstance в использование кода. В результате вы можете придать любой дочерний класс от Agentслишком который позволяет повторно использовать О.Г. с помощью кода с различными «типами» Agentбез перекомпиляции. Это низкая связь .
Тимоти Траклле
Я думаю, что это действительно сводится к паре вещей. Требуется ли состояние, насколько оно должно быть гибким, и нормально ли иметь глобальное состояние / состояние экземпляра? Это зависит от каждого разработчика / архитектора. Я бы предпочел более простую кодовую базу по сравнению с добавлением сложности для сценариев, которые усложняют понимание всего в целом. Одна из вещей, которые мне нравятся в node / js, - это то, что я могу писать простой / чистый код и по-прежнему внедрять его для тестирования без кода, записанного в соответствии со спецификациями DI / IoC.
Tracker1
2

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

Я думаю об этом - если при реализации класса (или функции) что-то вроде этого полезно и делает эту реализацию более понятной, как это может быть плохо? И ЧАСТО очень важно определить такие частные вспомогательные классы, чтобы позволить интеграцию и использование других алгоритмов, которые зависят от данных, находящихся в определенной форме.

Называть ли его «помощником» - это маленький вопрос личного вкуса, но это означает, что он помогает в реализации и не представляет интереса / пользы для более широкой аудитории. Если это имеет смысл - дерзайте!

Льюис Прингл
источник
1
Классы общественной полезности могут все еще быть полезными. Prime пример: java.lang.Math.
Амон
1

Распространенность staticвспомогательных классов основана на заблуждении. Тот staticфакт, что мы называем классы только с методами «служебными классами», не означает, что в POJO нельзя писать общее поведение.

static вспомогательные классы являются анти-паттернами по трем причинам:

  1. Статический доступ к этим вспомогательным методам скрывает зависимости . Если бы эти «служебные классы» были POJO, вы могли бы внедрить их int в зависимый класс в качестве параметров конструктора, что сделало бы зависимость очевидной для любого пользователя зависимого класса.

  2. Статический доступ к этим вспомогательным методам вызывает тесную связь . Это означает, что код, использующий вспомогательные методы, трудно использовать повторно и (как побочный эффект) тестировать.

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

Статические вспомогательные классы являются частью анти-паттерна кода STUPID .


Глобальное состояние не имеет отношения к вопросу, - max630

ОП написал:

Но в основном это действительно зависит от контекста и состояния.

Статические statefull конструкции являются глобальными состояниями.

любой вид кода может использовать его. - max630

Да, конечно. И почти всем приложениям нужно какое-то глобальное состояние .

Но глобальное состояние ! = Глобальная переменная .

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

Тимоти Тракл
источник
2
Глобальное состояние не имеет отношения к вопросу, любой код может использовать его.
max630
@ max630, пожалуйста, смотрите мое обновление
Тимоти Траклле
1
Вы можете использовать статические функции без связи. Вы передаете Func<Result, Args>объекты, которые создаются в другом месте.
Caleth
1
1) Я вообще думаю, что скрытая реализация - это хорошо, когда это подходит. 2) Наличие всего в качестве аргументов / инъекций зависимостей также создает некую связь времени разработки, вы можете почувствовать это, когда вам придется потратить много времени на изменение сигнатур в длинных цепочках зависимостей. И подобно тому, как каждая отдельная функция, требующая вашего Logger, или другие сквозные проблемы, в качестве аргумента, хрю ... (Но да, это может затруднить низкоуровневое тестирование) 3) Конечно, статические функции должны быть без состояний.
Алекс
1
@ Алекс Затем позвольте мне сказать это с другой стороны: блок - это любая группа кода (включая статические ссылочные «служебные» методы), которая имеет ту же причину для изменения . Все, что находится внутри этого блока, является деталью реализации . Все остальное является зависимостью , и у модуля не должно быть статического доступа к нему.
Тимоти Траклле