Java “一个访客是怎么做的?”;“定义新操作”;以迭代器没有的方式?

Java “一个访客是怎么做的?”;“定义新操作”;以迭代器没有的方式?,java,algorithm,design-patterns,iterator,visitor,Java,Algorithm,Design Patterns,Iterator,Visitor,我知道并经常使用迭代器和访问者,在我听说GangofFour的设计模式之前,我就已经使用了这两种方法。虽然这两种模式的语法非常不同,但我使用这两种模式的目的是相同的:遍历一组对象。粗略地说,当我有相同类型的非结构化对象时,我使用迭代器,当我有不同类型的结构化对象时,我使用访问者。对我来说,访问者只是一个优雅、奇特、更强的类型化迭代器 当我阅读设计模式时,我注意到对访问者的描述,尤其是它与迭代器描述的区别 Visitor允许您定义一个新操作,而无需更改其操作的元素的类 迭代器:提供一种顺序访问聚合

我知道并经常使用迭代器和访问者,在我听说GangofFour的设计模式之前,我就已经使用了这两种方法。虽然这两种模式的语法非常不同,但我使用这两种模式的目的是相同的:遍历一组对象。粗略地说,当我有相同类型的非结构化对象时,我使用迭代器,当我有不同类型的结构化对象时,我使用访问者。对我来说,访问者只是一个优雅、奇特、更强的类型化迭代器

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

Visitor允许您定义一个新操作,而无需更改其操作的元素的类

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

从那以后,我一直在考虑这个问题,但我真的不知道访问者是如何定义一个新操作的

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


这就引出了我的问题:访问者如何以迭代器所没有的方式“定义一个新的操作”如果我相信四人帮的描述,我觉得我错过了访问者模式的真正功能。

遍历结构不是访问者模式的定义特性。事实上,可以将访问者作为一个奇特的迭代器来使用结构遍历对象


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

您已经确定了以下原因,即无法对访问者引入新操作:

  • 当您将访问者传递给元素时,它将遍历该对象的整个子结构
  • “此外,您不能从接受/访问方法返回任何内容。”
  • 我认为原因(1)通常是一个合理的担忧。然而,我认为你可以修改你的访问者来避开这个问题。假设让
    visit
    accept
    返回一个
    boolean
    ,指示是否应继续探索结构。然后,您可以让
    accept
    像这样工作(在伪代码中):

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

    然而,对于第(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
    return
    void
    ,我仍然可以构建一个整体方法,通过让访问者保持内部状态,然后在访问完成后返回该状态来返回值。从现在起,调用
    count(obj)
    wil;返回所有对象的计数,本质上是将额外的方法“嫁接”到类层次结构中


    希望这有帮助

    谢谢。我知道布尔技巧,但它本质上只是一个
    if(..)break在迭代器中。同样地,count元素也可以很容易地用迭代器通过
    int count来计算;对于(每个元素)计数++@bitnus-我同意。我的意思是要表明你对访客模式局限性的担忧是没有道理的。您可以想象,通过使用私有状态技巧,并结合基于每种类型获取本地化字符串的自定义实现,可以执行一个更精细的操作,如
    toLocalizedString
    。如果你有一个迭代器,你不能不写一个巨大的级联“如果是这个类型,做这个,如果是那个类型,做那个,…”这样做有意义吗?
    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();
    }