проектирование модели объекта; осведомленность базового класса о производных классах

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


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

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

Учитывая этот набор параметров:

  • Узлы имеют родительские ссылки (односторонний обход)
  • Любой тип объекта, производный от Node, может быть родительским (или дочерним) для любого другого типа объекта, производного от Node.

Итак, у меня есть:

abstract class AbstractNode{

    protected $_parent;

    public function __construct(self $parent = null){
        $this->_parent = $parent;
    }

    public function get_parent(){
        return $this->_parent;
    }

}

class NodeOne extends AbstractNode{ }

class NodeTwo extends AbstractNode{ }

// more derivatives

Теперь вот где возникает моя дилемма дизайна; во время обхода может потребоваться найти экземпляры NodeOne, сами по себе и любые другие экземпляры типов, производных от AbstractNode (обратите внимание, что эта функциональность не является исключительной для экземпляров NodeOne, это просто пример)

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

public function get_node_one_ancestor(){
    if($this->_parent instanceof NodeOne){
        return $this->_parent;
    }
    if(null !== $this->_parent){
        return $this->_parent->get_node_one_ancestor();
    }
    return null;
}

Поскольку любой производный тип может нуждаться в обходе экземпляров NodeOne, было бы разумно использовать этот метод в базовом классе AbstractNode, однако теперь мой базовый класс требует осведомленности о производном типе.

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


Аналогия, которая приходит на ум, — это DOM, выполняющий обход предков для определенных типов:

<root>
    <foo id="1">
        <bar id="2"></bar>
        <bar id="3">
            <foo id="4">
                <bar id="5">
                    <foo id="6">
                        <bar id="7"></bar>
                    </foo>
                </bar>
                <bar id="8"></bar>
            </foo>
        </bar>
    </foo>
</root>
  • Из bar[@id='8'] объединяются все foo значения предка id:
    Результат 4 1

  • Из bar[@id='7'] объединяются все foo значения предка id:
    Результат 6 4 1


person Dan Lugg    schedule 12.12.2011    source источник
comment
Какую проблему вы пытаетесь решить с помощью этой реализации? Вложенный набор?   -  person Mike Purcell    schedule 13.12.2011
comment
Нет @DigitalPrecision - это не имеет ничего общего с представлением дерева РСУБД; это дерево выполнения. Узлы представляют собой процедуры, и любая данная процедура может вызывать любую другую процедуру. Однако некоторые узлы имеют свойства среды, и если узел со свойствами среды (NodeOne из моего примера) выполняется, любые узлы, вызываемые в нем, должны отслеживать любые свойства, которые изменил этот узел, поэтому мне нужно вернуться назад. чтобы найти экземпляры NodeOne и объединить/объединить свойства до завершения вызова. Это позволяет использовать каскадную среду выполнения.   -  person Dan Lugg    schedule 13.12.2011
comment
Я понимаю. У нас была реализация типа узла в более старой кодовой базе, но мы решили не портировать ее в первоначальном виде, потому что сложность не оправдывала времени, необходимого для ее поддержки.   -  person Mike Purcell    schedule 13.12.2011


Ответы (1)


Вы должны быть в состоянии обобщить это:

public function get_ancestor($type){
    if($this->_parent instanceof $type){
        return $this->_parent;
    }
    if(null !== $this->_parent){
        return $this->_parent->get_ancestor($type);
    }
    return null;
}

Мне кажется, что это то, что могло бы жить во внешнем объекте итератора, но я не могу сказать, что много думал, прежде чем сделать этот пост...

person Matthew    schedule 12.12.2011
comment
Спасибо, @Matthew -- я пошутил над $parent, а должен был $this->_parent -- отредактировать. - person Dan Lugg; 13.12.2011
comment
Еще раз спасибо @Matthew - я подумал об этом, я снова просматриваю другие детали реализации, чтобы посмотреть, подойдет ли это. - person Dan Lugg; 13.12.2011