访客模式VS迭代器模式:访问层次结构类?

时间:2015-02-04 10:33:40

标签: iterator visitor

我正在研究访客模式优势,并引用Design Patterns

  

但是迭代器不能跨越具有不同的对象结构   元素的类型。例如,页面上定义了Iterator接口   295只能访问Item类型的对象:

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

这意味着迭代器可以访问的所有元素都有一个共同的父类Item。   访客没有此限制......

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

MyType和YourType不必通过继承关联   所有

我同意这个引用,但是我无法弄清楚访问者模式可以探索一个结构(例如List)的示例,其中收集的对象与超类没有关联。

换句话说,你能告诉我一个上述功能的例子吗?

2 个答案:

答案 0 :(得分:2)

首先,您应该知道这些模式的用途。

Iterator模式用于顺序访问聚合而不暴露其底层表示。因此,您可以在迭代器后面隐藏列表或数组或类似的聚合。

访客模式用于对元素结构执行操作,而不更改元素本身的实现。

因此,您可以在两种不同情况下使用模式,而不是彼此的替代方案。

在访问者模式中,您在要访问的每个元素中实现了一个接口IAcceptor。因此,访问者模式不依赖于超类,而是依赖于接口

public interface IAcceptor
{
    public void 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 PatternIterator Pattern

编辑:这是一个使用访问者和迭代器的例子。迭代器只是如何在聚合中移动的逻辑。使用层次结构会更有意义。

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
}

答案 1 :(得分: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()和流相似的方式进行交互,我们甚至不知道我们的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;