访问者模式VS迭代程序模式:跨层次类访问



我正在研究访问者模式的优势,并引用设计模式:

但是迭代器不能跨具有不同元素的类型。例如,第页上定义的Iterator接口295只能访问类型为项:的对象

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

这意味着迭代器可以访问的所有元素都有一个公共的parentclass项。访问者没有此限制。。。

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

MyType和YourType不必通过继承在全部的

我同意这句话,但我想不出一个Visitor Pattern可以探索一个结构(如List)的例子,其中收集的对象与超类无关。

换句话说,你能给我举一个例子,上面的特征是真的吗?

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

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

访问者模式用于在不更改元素本身实现的情况下对元素结构执行操作。

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

在Visitor模式中,您在要访问的每个元素中实现一个Interface 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);
    }
}

要完成这里的代码,如果Visitor访问我的或另一个接收器的实现,它将向Console写入。

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

要获得更多有用的示例和更好的解释,请查看访问者模式和迭代器模式

编辑:这里有一个同时使用访问者和迭代器的例子。迭代器只是如何在聚合中移动的逻辑。如果采用分级结构,这将更有意义。

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
}

我知道有两个很好的例子,其中访问者显然比迭代器更可取。

第一种是与一些未知的类成员交互,特别是在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源代码将详细介绍。以下是亮点:

/// 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;

最新更新