Когда я должен использовать Шаблон разработки Посетителя? [закрытый]

308
задан Ravindra babu 18 February 2016 в 18:24
поделиться

5 ответов

Я не очень знаком с Шаблоном "посетитель". Давайте посмотрим, разобрался ли я в нем. Предположим, что у Вас есть иерархия животных

class Animal {  };
class Dog: public Animal {  };
class Cat: public Animal {  };

(Предположим, что это - сложная иерархия с известным интерфейсом.)

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

class Animal
{ public: virtual void makeSound() = 0; };

class Dog : public Animal
{ public: void makeSound(); };

void Dog::makeSound()
{ std::cout << "woof!\n"; }

class Cat : public Animal
{ public: void makeSound(); };

void Cat::makeSound()
{ std::cout << "meow!\n"; }

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

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

class Operation
{
public:
    virtual void hereIsADog(Dog *d) = 0;
    virtual void hereIsACat(Cat *c) = 0;
};

Затем мы изменяем иерархию для принятия новых операций:

class Animal
{ public: virtual void letsDo(Operation *v) = 0; };

class Dog : public Animal
{ public: void letsDo(Operation *v); };

void Dog::letsDo(Operation *v)
{ v->hereIsADog(this); }

class Cat : public Animal
{ public: void letsDo(Operation *v); };

void Cat::letsDo(Operation *v)
{ v->hereIsACat(this); }

Наконец, мы реализуем фактическую операцию, , не изменяя ни CAT, ни Собаки :

class Sound : public Operation
{
public:
    void hereIsADog(Dog *d);
    void hereIsACat(Cat *c);
};

void Sound::hereIsADog(Dog *d)
{ std::cout << "woof!\n"; }

void Sound::hereIsACat(Cat *c)
{ std::cout << "meow!\n"; }

Теперь у Вас есть способ добавить операции, не изменяя иерархию больше. Вот то, как это работает:

int main()
{
    Cat c;
    Sound theSound;
    c.letsDo(&theSound);
}
307
ответ дан alexlomba87 23 November 2019 в 01:17
поделиться

Один способ посмотреть на него состоит в том, что шаблон "посетитель" является способом позволить Вашим клиентам добавить дополнительные методы для всех Ваших классов в конкретной иерархии классов.

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

классический пример для компиляторов и т.п.. Абстрактное синтаксическое дерево (AST) может точно определить структуру языка программирования, но операции, которые Вы могли бы хотеть сделать на AST, изменятся, когда Ваш проект совершенствуется: генераторы кода, симпатичные принтеры, отладчики, метрический анализ сложности.

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

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

18
ответ дан Oddthinking 23 November 2019 в 01:17
поделиться

Посетитель шаблон разработки работает действительно хорошо на "рекурсивные" структуры как деревья каталогов, структуры XML или основы документа.

объект Посетителя А посещает каждый узел в рекурсивной структуре: каждый каталог, каждый XML-тэг, безотносительно. Объект Посетителя не циклично выполняется через структуру. Вместо этого методы Посетителя применяются к каждому узлу структуры.

Вот типичная рекурсивная структура узла. Мог быть каталог или XML-тэг. [Если Ваш человек Java, вообразите большого количества дополнительных методов, чтобы создать и вести дочерний список.]

class TreeNode( object ):
    def __init__( self, name, *children ):
        self.name= name
        self.children= children
    def visit( self, someVisitor ):
        someVisitor.arrivedAt( self )
        someVisitor.down()
        for c in self.children:
            c.visit( someVisitor )
        someVisitor.up()

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

Вот суперкласс для посетителей. Это используется visit метод. Это "прибывает в" каждый узел в структуре. Начиная с эти visit вызовы метода up и down, посетитель может отслеживать глубину.

class Visitor( object ):
    def __init__( self ):
        self.depth= 0
    def down( self ):
        self.depth += 1
    def up( self ):
        self.depth -= 1
    def arrivedAt( self, aTreeNode ):
        print self.depth, aTreeNode.name

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

Вот приложение. Это создает древовидную структуру, someTree. Это создает Visitor, dumpNodes.

Тогда это применяется dumpNodes к дереву. Эти dumpNode объект "посетит" каждый узел в дереве.

someTree= TreeNode( "Top", TreeNode("c1"), TreeNode("c2"), TreeNode("c3") )
dumpNodes= Visitor()
someTree.visit( dumpNodes )

алгоритм TreeNode visit гарантирует, что каждый TreeNode используется в качестве аргумента Посетителю arrivedAt метод.

22
ответ дан S.Lott 23 November 2019 в 01:17
поделиться

Все здесь корректны, но я думаю, что этому не удается обратиться "когда". Во-первых, из Шаблонов разработки:

Посетитель позволяет Вам определить новую операцию, не изменяя классы элементов, на которые она воздействует.

Теперь, давайте думать о простой иерархии классов. У меня есть классы 1, 2, 3 и 4 и методы A, B, C и D. Разметьте их как в электронной таблице: классы являются строками, и методы являются столбцами.

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

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

Между прочим, это - проблема, которую соответствия шаблона Scala намереваются решить.

84
ответ дан Daniel C. Sobral 23 November 2019 в 01:17
поделиться

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

<час>

<глоток> 1) Моим любимым примером является Scott Meyers, приветствуемый автор C++ “Effective ”, кто назвал этот из его самый важный C++ ага! моменты когда-либо .

127
ответ дан Konrad Rudolph 23 November 2019 в 01:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: