0

以下使用泛型的访问者模式的 Java 实现是否足够通用?(我想是的)。

可以通过某种方式改进吗?使用匿名类可以轻松调用是很重要的。谢谢。

(使用示例):

Vector<Number> numbers = new Vector<Number>();

        numbers.add(new Double(1.2));
        numbers.add(new Float(-1.2));
        numbers.add(new Double(4.8));
        numbers.add(new Float(-3.4));
        numbers.add(new Long(123456));
        numbers.add(new Short("14"));

        For.each(numbers, new Visitor<Number>() {
            public void doIt(Double n) {
                System.out.println("doIt() for double: " + n);
            }
            public void doIt(Float n) {
                System.out.println("doIt() for float: " + n);
            }
            public void doIt(Number n) {
                System.out.println("doIt() for Number: " + n);
            }
        });

        Visitor<Number> visi =  new Visitor<Number>() {
            private StringBuffer  all = new StringBuffer ();
            public void doIt(Number n) {
                System.out.println("doIt() for Number: " + n);
                all.append(n.toString() + " ");
            }
            public Object getResult () {
                return all;
            }
        };

        For.each(numbers, visi);

        System.out.println ("all -> " + visi.getResult());

定义:

//............................................
abstract class Visitor<T> {
    public void visit(T n) {
        try {
            this.getClass().getDeclaredMethod("doIt", n.getClass()).invoke(this, n);
        } catch (Exception ex) {
            doIt((T) n);
        }
    }
    public void doIt(T n) {
        System.out.println("doIt() for base " + n);
    }
    public Object getResult() {
        return null;
    }
} // class

//............................................
class For {
    public static <T> void each (Collection<T> c, Visitor<T> f) {
        for (T v : c) {
            f.visit(v);
        }
    } // ()
} // class
4

3 回答 3

6

这不是访客模式

访问者的特点是访问者有一个accept(Visitor v)方法,该方法与访问者中的访问方法交互,以访问者为参数,并针对不同类型的访问者进行重载,形成“双重调度”机制。

引用设计模式中访问者的“适用性”部分:

在以下情况下使用访问者模式
  • 一个对象结构包含许多具有不同接口的对象类,并且您希望对这些对象执行依赖于它们的具体类的操作。
  • 需要对对象结构中的对象执行许多不同且不相关的操作,并且您希望避免这些操作“污染”它们的类。访问者允许您通过在一个类中定义相关操作来将它们保持在一起。当对象结构被许多应用程序共享时,使用访问者将操作放在那些需要它们的应用程序中。
  • 定义对象结构的类很少更改,但您经常希望在该结构上定义新操作。更改对象结构类需要重新定义所有访问者的接口,这可能代价高昂。如果对象结构类经常更改,那么最好在这些类中定义操作。

所以这个模式是为了处理对多种类型的对象的类似操作。在您的示例中,您称为访问者的对象只能处理一种类型。

在您修改以使用反射来处理多种类型的答案中(顺便说一下,最好将其作为对问题的编辑或作为单独的问题来完成),您避免accept(Visitor v)使用反射在访问的类中创建方法,即在某种程度上实现了相同的目标,但有些尴尬。我仍然拒绝将其称为访问者的实现。

如果您在此处编写的代码对您有用,请务必使用它,但请不要将其称为访问者。

这更像是Strategy PatternFunction Object,如果您以反映这一点的方式重命名泛型类,它实际上是有用的,并且您的用法类似于函数式语言中列表处理的常见模式。

我可能会对问题中的代码做的是将您的代码重命名为并将您Visitor<T>Operation<T>重命名visit(T t)execute(T t)or apply(T t),将 aOperation视为Function没有返回值的 a 。实际上,我已经以与您正在做的类似的方式使用了这一点,并使用了类似的策略来使用通用Function<Domain, Range>对象进行集合“映射”。我不确定什么模式名称实际上适合它,但它不是访客。它将函数式列表理解风格带入了 OO 世界,在这个世界中,函数不是天生的一流对象。

于 2010-10-15T22:18:27.090 回答
2

感谢 donroby 关于我的初始代码没有实现访问者模式的回答,我来到了这个新版本。

我想它现在实现了访问者模式,而无需使用 accept() 方法修改访问的元素。无论如何,由于反射,它能够根据元素类型调用正确的方法(我猜这是 accept() 的任务)。

首先,一个使用示例:

Vector<Number> numbers = new Vector<Number>();

    numbers.add(new Double(1.2));
    numbers.add(new Float(-1.2));
    numbers.add(new Double(4.8));
    numbers.add(new Float(-3.4));
    numbers.add(new Long(123456));
    numbers.add(new Short("14"));

    For.each(numbers, new Visitor<Number>() {
        public void doIt(Double n) {
            System.out.println("doIt() for double: " + n);
        }
        public void doIt(Float n) {
            System.out.println("doIt() for float: " + n);
        }
        public void doIt(Number n) {
            System.out.println("doIt() for Number: " + n);
        }
    });

产生这个输出

双倍的 doIt():1.2
浮点数的 doIt():-1.2
双倍的 doIt():4.8
浮点数的 doIt():-3.4
doIt() for Number: 123456
doIt() 代表数字:14

最后是代码

abstract class Visitor<T> {
public void visit(T n) {
    try {
        this.getClass().getDeclaredMethod("doIt", n.getClass()).invoke(this, n);
    } catch (Exception ex) {
        doIt((T) n);
    }
}
public void doIt(T n) {
    System.out.println("doIt() for base " + n);
}
public Object getResult() {
    return null;
}

}

class For {
public static <T> void each (Collection<T> c, Visitor<T> f) {
    for (T v : c) {
        f.visit(v);
    }
} // ()

}

于 2010-10-16T10:50:27.610 回答
0

怎么样

    for(String s : words)
        System.out.println (s.toUpperCase());

    int total = 0;
    for(String s : words)
        total = total + s.length();
    System.out.println (" sum of lengths = " + total);
于 2010-10-15T23:02:59.220 回答