4

我知道并经常使用迭代器和访问者,而且在我听说四人帮的设计模式之前就一直在使用它们。尽管这两种模式的语法非常不同,但我将它们用于相同的概念目标:遍历一组对象。粗略地说,当我有相同类型的非结构化对象时,我使用迭代器,而当我有不同类型的结构化对象时,我使用访问者。对我来说,访问者只是一个优雅的、花哨的、更强大的类型迭代器。

当我阅读设计模式时,我注意到访问者的描述,尤其是它与迭代器描述的不同之处。

Visitor 允许您在不更改其操作的元素的类的情况下定义新操作

迭代器:提供一种顺序访问聚合对象的元素而不暴露其底层表示的方法。

从那以后我一直在考虑它,但我无法真正弄清楚访问者如何定义新操作

例如,如果我想实现一个相当简单的操作,比如toLocalizedString()作为toString(). 当您将访问者传递给元素时,它将遍历该对象的整个子结构。此外,您不能从接受/访问方法返回任何内容。这些特征中的每一个都阻止我使用访问者来定义toLocalizedString().

这带来了我的问题:访问者如何以迭代器没有的方式“定义新操作”?如果我相信四人帮的描述,我觉得我错过了访问者模式的真正力量。

4

2 回答 2

5

结构的遍历不是访问者模式的定义特征。实际上,可以将您使用访问者遍历具有结构的对象视为一种奇特的迭代器。

访问者与迭代器的区别在于,访问者允许您执行所谓的双重调度,即将消息路由到取决于被访问对象的运行时类型和访问者对象的方法。被访问的对象在访问者外部,但操作的可执行代码包含在访问者内部。换句话说,遵循访问者模式可以让您执行正在访问的对象之外的操作,这可以被认为是在对象上定义新的操作。

于 2013-06-20T19:34:10.090 回答
0

您已确定以下原因是您无法向访客介绍新操作的原因:

  1. “当您将访问者传递给元素时,它将遍历该对象的整个子结构。”
  2. “此外,您不能从接受/访问方法返回任何内容。”

我认为原因(1)通常是一个有效的担忧。但是,我认为您可以修改您的访问者来解决这个问题。假设您创建visitaccept返回一个boolean指示结构的探索是否应该继续。然后你可以有这样的accept工作(在伪代码中):

 for (each child) {
     if (!child.accept(visitor)) return false;
 }
 return visitor.visit(this);

这样,一旦访问者意识到它已经完成,它就可以停止搜索。这类似于对链接在一起的对象进行深度优先的回溯搜索。

但是,对于(2),我认为您缺少访问者的一项重要技术-由于访问者是对象,因此它们可以包含私有状态信息。例如,假设我想编写一个简单的count函数来返回结构中元素的总数。然后我可以写这样的东西:

public int count(Base object) {
    Visitor v = new Visitor() {
        private int numElems = 0;

        public void visit(Base object) {
            numElems++;
        }
        public void visit(Derived1 object) {
            numElems++;
        }
        // ... etc

        public int getCount() {
            return numElems;
        }
    };
    object.accept(v);
    return v.getCount();
}

visit请注意,即使accept返回void此处,我仍然可以构建一个返回值的整体方法,方法是让访问者保持内部状态,然后在访问完成后返回该状态。从现在开始,呼唤count(obj)威尔;返回所有对象的计数,本质上是“嫁接”到类层次结构的额外方法上。

希望这可以帮助!

于 2013-06-20T19:34:51.020 回答