Лучше расширить класс или изменить его напрямую? - PullRequest
4 голосов
/ 28 апреля 2010

Итак, я работаю над созданием визуализации для структуры данных в Java. У меня уже есть реализация структуры данных (Binary Search Tree) для начала, но мне нужно добавить некоторые дополнительные функции во включенный класс узла. Что касается соглашений и передового опыта, должен ли я создать подкласс узла с этой дополнительной функциональностью или просто изменить то, что у меня есть, и документировать его там?

Мой вопрос похож на тот, что задают здесь , но это немного над моей головой.

Я знаю, что это, вероятно, не имеет большого значения для того, что я делаю, поэтому я спрашиваю об этом как об общем.

Редактировать: Я, наверное, должен был быть более ясным. Мои модификации на самом деле не изменяют исходную реализацию, кроме как добавить пару дополнительных полей (координаты x и y плюс логическое значение для установки того, выделен ли этот узел) и функций для доступа к этим полям и их изменения. Также класс узла, с которым я работаю, включен в реализацию BST

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

Спасибо за информативные ответы.

Ответы [ 5 ]

11 голосов
/ 28 апреля 2010

Вопрос, на который нужно ответить, является ли «базовая функциональность» полезной, даже нежелательной, когда вы не визуализируете структуру данных?

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

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

3 голосов
/ 28 апреля 2010

Поскольку вы спрашиваете в общем, вот краткий ответ: это действительно зависит от ситуации .

Во-первых, предполагается, что подклассы имеют отношения "IS-A" со своими родительскими классами. Если вы не можете сказать, что ваш новый подкласс является специфическим видом исходного класса, вы задаете неправильный вопрос и должны создать новый, не связанный класс.

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

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

2 голосов
/ 29 апреля 2010

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

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

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

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

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

public interface HasNode {
    public Node getNode();
}

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

2 голосов
/ 28 апреля 2010

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

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

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

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

0 голосов
/ 28 апреля 2010

Смешение логически независимых функций приведет к путанице. Подклассы - это особые отношения, часто используемые слишком часто. Подклассы предназначены для отношений Is-a-Kind.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...