访问者如何以迭代器没有的方式“定义新操作”?

时间:2013-06-20 19:26:21

标签: java algorithm design-patterns iterator visitor

我经常知道并使用迭代器和访问者,并且在我听说四人帮的设计模式之前就已经使用过它们。虽然两种模式的语法差异很大,但我将两者用于同一个概念目标:遍历一组对象。粗略地说,当我有相同类型的非结构化对象时,我使用迭代器,当我构建不同类型的对象时,我使用访问者。对我来说,访客只是一个优雅,华丽,强大的类型迭代器。

当我阅读设计模式时,我注意到了访问者的描述,特别是它与迭代器描述的区别。

  

访问者可让您定义新操作,而无需更改其操作元素的类

     

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

我已经考虑过了,我无法弄清楚访客如何定义新操作

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

这就引出了我的问题:访问者如何以迭代器没有的方式“定义新操作”?如果我要相信Gang of Four的描述,我觉得我'我错过了访客模式的真正力量。

2 个答案:

答案 0 :(得分:5)

遍历结构不是访客模式的定义特征。事实上,人们可以想到你使用访问者遍历具有结构的对象作为花哨的迭代器。

访问者与迭代器的不同之处在于访问者允许您执行所谓的Double Dispatch,即将消息路由到依赖于所访问对象的运行时类型和访问者对象的方法。被访问的对象在访问者外部,但操作的可执行代码包含在访问者中。换句话说,遵循访问者模式允许您执行被访问对象外部的操作,这可以被认为是在对象上定义新操作。

答案 1 :(得分:0)

您已将以下内容确定为无法向访问者介绍新操作的原因:

  1. “当您将访问者传递给元素时,它将遍历该对象的整个子结构。”
  2. “此外,您无法从接受/访问方式返回任何内容。”
  3. 我认为原因(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();
    }
    

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

    希望这有帮助!