Считается ли связь внутри объекта анти-паттерном?


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

Например:

   public class Item
   {
       private Item prev; 
       private Item next;
       ...
   }
Часто цитируемая рекомендация состоит в том, чтобы просто использовать универсальный контейнерный класс (такой как java.util.LinkedList в Java), но это создает накладные расходы, связанные с наличием отдельного объекта узла (и связанный объект не может легко ссылаться на своих братьев и сестер).

Мысли?

8 5

8 ответов:

Это не обязательно анти-паттерн. Анти-паттерны должны иметь негативные побочные эффекты, чтобы заслужить право на "анти".

Например, Node в структуре данных Treeбудет нужна связь, кэшированная внутри Node. Все остальное нарушило бы гораздо более важную концепцию инкапсуляции и локализации данных и кода, основанную на ответственности объекта.

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

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

Иногда небольшие накладные расходы-это стоимость "чистых" проектов. Готовы ли вы платить за эти накладные расходы-другой вопрос. На мой взгляд, небольшие накладные расходы, добавляемые с помощью утилит, таких как встроенные связанные списки, того стоят... он экономит время разработки, тщательно тестируется и делает дизайн чище.

Это анти-паттерн, если это не так всегда используется в связанном списке. Вы получите швейцарскую армию, если добавите в свои классы свойства/методы, которые вам могут понадобиться. Читайте о ЯГНИ.

Добавление этих методов также нарушит SRP, поскольку элемент также будет отвечать за отслеживание своих братьев и сестер.

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

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

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

Я считаю, что классы XML DOM в java делают (что-то вроде) то, что вы описываете, так что есть по крайней мере прецедент для этого. Обычно то, что вы описываете, вероятно, является исключением, а не правилом, потому что в противном случае вы просто повторно реализуете связанный список.

В Java это меньше проблем, чем в других языках, так как Item должен быть указателем класса. Если, с другой стороны, Item является структурой, как это возможно с C# и другими языками, вы столкнетесь с очевидными проблемами. Помимо того, что у вас есть изменяемая структура, вы также, вероятно, получите ошибку во время компиляции, пытаясь определить структуру с бесконечно большим требованием к памяти.

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

Этот паттерн называетсяинвазивным контейнером .

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

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

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

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

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