2015-02-04 2 views
1

Я изучаю посетитель шаблонных преимущество и цитирование Design Patterns:Шаблон посетителя VS Итератор Выкройка: посещение класса иерархии?

Но iteratorcan't работу через объектные структуры с различными типов элементов. Forexample, интерфейс итератора, определенный на странице 295 может иметь доступ только объекты типа Item:

template <class Item> 
clas Iterator { // ... Item CurrentItem() const; }; 

Это означает, что все элементы итератор может посетить имеют общую ParentClass позицию. Посетитель не имеет этого ограничения ...

class Visitor { 
public: 
// ... 
void VisitMyType(MyType*); 
void VisitYourType(YourType*); 
}; 

MyType и YourType не должны быть связаны throughinheritance на всех.

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

Другими словами, можете ли вы показать мне пример, где приведенные выше функции верны?

ответ

1

Сначала вы должны знать, для чего предназначены шаблоны.

Шаблон Итератора используется для одновременного доступа к агрегату без раскрытия его основного представления. Таким образом, вы можете скрыть список или массив или аналогичные агрегаты за Iterator.

Шаблон посетителя используется для выполнения действия над структурой элементов без изменения реализации самих элементов.

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

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

public interface IAcceptor 
{ 
    public Accept(IVisitor visitor); 
} 

Так что если у вас есть список объектов, которые вы можете перебрать его и посетить объекты реализации IAcceptor

public VisitorExample() 
{ 
    MyVisitorImplementation visitor = new MyVisitorImplementation(); 
    List<object> objects = GetList(); 
    foreach(IAcceptor item in objects) 
     item.Accept(visitor); 
} 


public interface IVisitor 
{ 
    public void Visit(MyAcceptorImplementation item); 
    public void Visit(AnotherAcceptorImplementation item); 
} 

public class MyAcceptorImplementation : IAcceptor 
{ 
    //Some Code ... 
    public void Accept(IVisitor visitor) 
    { 
     visitor.Visit(this); 
    } 
} 

Для завершения код здесь - «Посетитель» для записи в «Консоль», если он посещает мою или другую реализацию акцептора.

public class MyVisitorImplementation : IVisitor 
{ 
     public void Visit(MyAcceptorImplementation item) 
     { 
      Console.WriteLine("Mine"); 
     } 
     public void Visit(AnotherAcceptorImplementation item) 
     { 
      Console.WriteLine("Another"); 
     } 
} 

Для более полезных примеров и лучшего объяснения взглянуть на Visitor Pattern и Iterator Pattern

EDIT: Вот пример использования обоих, посетителей и итератора. Итератор - это просто логика, как перемещаться по вашему агрегату. Это имело бы смысл с иерархической структурой.

public VisitorExample2() 
{ 
    MyVisitorImplementation visitor = new MyVisitorImplementation(); 
    List<object> myListToHide = GetList(); 

    //Here you hide that the aggregate is a List<object> 
    ConcreteIterator i = new ConcreteIterator(myListToHide); 

    IAcceptor item = i.First(); 
    while(item != null) 
    { 
     item.Accept(visitor); 
     item = i.Next(); 
    } 
    //... do something with the result 
} 
+0

Извините, но я не согласен с вашим ответом. Оба шаблона Visitor и Iterator могут использоваться для «посещения» структуры объекта. Как вы правильно сказали, весь посещаемый элемент в шаблоне посетителя реализует «супер-интерфейс». Как следствие (как хорошо сказано в книге), нет необходимости, чтобы посещаемый объект имел общий класс (но общий интерфейс). Я не уверен, это пример, показанный в тексте: почему бы не вернуть объект, который реализует интерфейс вместо объекта класса? В более простых словах: если общий интерфейс «IVisitable», напишите 'IVisitable CurrentItem()' – justHelloWorld

+0

Вы можете использовать итератор вместо списка, чтобы перебирать элементы, а затем использовать посетителя. Посетитель использует данные объектов, которые он посещает, для выполнения некоторых вычислений или выполнения некоторых других действий с данными. Итератор просто перемещается по коллекции/агрегату, скрывая его реализацию. Но вы действительно можете реализовать Iterator, который работает на интерфейсе. – BoeseB

2

Есть два хороших примера. Я знаю, где посетитель явно предпочтительнее итератора.

Первый взаимодействует с некоторым неизвестным набором членов класса, в частности в C++. Например, вот посетитель, который печатает все члены других классов. Представьте, что вы являетесь автором Printer, а кто-то, с кем вы не знакомы, является автором Heterogeneous3Tuple.

#include <iostream> 

template<class ElemType1, class ElemType2, class ElemType3> 
class Heterogeneous3Tuple 
{ 
public: 
    Heterogeneous3Tuple(ElemType1 elem1, ElemType2 elem2, ElemType3 elem3) 
     : elem1_(std::move(elem1)), elem2_(std::move(elem2)), elem3_(std::move(elem3)) 
    {} 

    template<class Visitor> 
    void accept(const Visitor& visitor) 
    { 
     visitor(elem1_); 
     visitor(elem2_); 
     visitor(elem3_); 
    } 

private: 
     ElemType1 elem1_; 
     ElemType2 elem2_; 
     ElemType3 elem3_; 
}; 

class Printer 
{ 
public: 
    template<class VisitedElemType> 
    void operator()(const VisitedElemType& visitee) const 
    { 
     std::cout << visitee << std::endl; 
    } 

private: 
}; 


int main() { 
    Heterogeneous3Tuple<char, int, double> h3t('a', 0, 3.14); 
    Printer p; 
    h3t.accept(p); 
} 

a 
0 
3.14 

coliru

Там нет разумного способа, чтобы получить итератор, чтобы работать здесь. Даже не зная, какие типы могут взаимодействовать с классом Printer, это работает, пока посетитель accept() ed, и все элементы взаимодействуют аналогично с operator << и потоком.

Другой хороший пример, который я знаю о проявлениях в абстрактных синтаксических манипуляциях с деревьями. CPython и LLVM используют посетителей. Использование посетителя здесь предотвращает использование кода, который управляет определенными узлами AST, из-за необходимости знать, как выполнять итерацию по всем различным узлам AST, которые могут ветвиться сложными способами. LLVM source code подробнее. Вот изюминка:

/// Instruction visitors are used when you want to perform different actions 
/// for different kinds of instructions without having to use lots of casts 
/// and a big switch statement (in your code, that is). 
/// 
/// To define your own visitor, inherit from this class, specifying your 
/// new type for the 'SubClass' template parameter, and "override" visitXXX 
/// functions in your class. I say "override" because this class is defined 
/// in terms of statically resolved overloading, not virtual functions. 
/// 
/// For example, here is a visitor that counts the number of malloc 
/// instructions processed: 
/// 
/// /// Declare the class. Note that we derive from InstVisitor instantiated 
/// /// with _our new subclasses_ type. 
/// /// 
/// struct CountAllocaVisitor : public InstVisitor<CountAllocaVisitor> { 
/// unsigned Count; 
/// CountAllocaVisitor() : Count(0) {} 
/// 
/// void visitAllocaInst(AllocaInst &AI) { ++Count; } 
/// }; 
/// 
/// And this class would be used like this: 
/// CountAllocaVisitor CAV; 
/// CAV.visit(function); 
/// NumAllocas = CAV.Count;