关于访客模式的问题(Java中的示例)

时间:2011-06-06 12:58:41

标签: java design-patterns visitor

我只是想了解使用访客模式的主要好处。

这是一个Java实现示例

///////////////////////////////////
// Interfaces
interface MamalVisitor {
    void visit(Mammal mammal);
}
interface MammalVisitable {
    public void accept(MamalVisitor visitor);
}
interface Mammal extends MammalVisitable {
    public int getLegsNumber();
}
///////////////////////////////////


///////////////////////////////////
// Model
class Human implements Mammal {
    @Override
    public void accept(MamalVisitor visitor) {  visitor.visit(this);  }
    @Override
    public int getLegsNumber() { return 2; }
}
//PIRATE HAS A WOOD LEG
class Pirate extends Human { 
    @Override
    public int getLegsNumber() { return 1; }
    public int getWoodLegNumber() { return 1; }
}
class Dog implements Mammal {
    @Override
    public void accept(MamalVisitor visitor) {  visitor.visit(this);  }
    @Override
    public int getLegsNumber() { return 4; }
}
///////////////////////////////////


///////////////////////////////////
class LegCounterVisitor implements MamalVisitor {
    private int legNumber = 0;
    @Override
    public void visit(Mammal mammal) {   legNumber += mammal.getLegsNumber();   }
    public int getLegNumber() { return legNumber; }
}
class WoodLegCounterVisitor implements MamalVisitor {
    private int woodLegNumber = 0;
    @Override
    public void visit(Mammal mammal) {   
        // perhaps bad but i'm lazy
        if ( mammal instanceof Pirate ) {
            woodLegNumber += ((Pirate) mammal).getWoodLegNumber();
        }
    }
    public int getWoodLegNumber() { return woodLegNumber; }
}
///////////////////////////////////



///////////////////////////////////
public class Main {
    public static void main(String[] args) {
        // Create a list with 9 mammal legs and 3 pirate woodlegs
        List<Mammal> mammalList = Arrays.asList(
                new Pirate(),
                new Dog(),
                new Human(),
                new Pirate(),
                new Pirate()
        );

        ///////////////////////////////////
        // The visitor method
        LegCounterVisitor legCounterVisitor = new LegCounterVisitor();
        WoodLegCounterVisitor woodLegCounterVisitor = new WoodLegCounterVisitor();
        for ( Mammal mammal : mammalList ) {
            mammal.accept(legCounterVisitor);
            mammal.accept(woodLegCounterVisitor);
            // why not also using:
            // legCounterVisitor.visit(mammal);
            // woodLegCounterVisitor.visit(mammal);
        }
        System.out.println("Number of legs:" + legCounterVisitor.getLegNumber());
        System.out.println("Number of wood legs:" + woodLegCounterVisitor.getWoodLegNumber());

        ///////////////////////////////////
        // The standart method
        int legNumber = 0;
        int woodLegNumber = 0;
        for ( Mammal mammal : mammalList ) {
            legNumber += mammal.getLegsNumber();
            // perhaps bad but i'm lazy
            if ( mammal instanceof Pirate ) {
                woodLegNumber += ((Pirate) mammal).getWoodLegNumber();
            }
        }
        System.out.println("Number of legs:" + legNumber);
        System.out.println("Number of wood legs:" + woodLegNumber);
    }
}
///////////////////////////////////

我只是想知道这种情况下使用这种模式的主要优势是什么。我们也可以遍历集合并获得几乎相同的东西,除了我们不必处理新的接口并将模板代码添加到模型中...

使用Apache Commons或功能语言,经典的方式似乎是做一些map / reduce操作(映射到腿数并减少添加),这很容易......

我也想知道为什么我们使用

        mammal.accept(legCounterVisitor);
        mammal.accept(woodLegCounterVisitor);

而不是

        legCounterVisitor.visit(mammal);
        woodLegCounterVisitor.visit(mammal);

第二个选项似乎删除了模型部件上的accept(...)方法。

在我发现的许多样本中,似乎他们没有为模型对象使用通用接口。我添加它是因为我只需要添加一个访问(哺乳动物)方法,而不是为每个哺乳动物实现一个。 让我的所有对象实现哺乳动物是好的吗? (我猜有时它无论如何都不可能)。它仍然是这样的访客模式吗?

所以我的问题是: - 你看到我使用访客的例子有什么好处吗? - 如果没有,你能为游客提供一些具体的用具吗? - 访问者在函数式编程语言中很有用

我发现与此模式相关的唯一例子是漂亮的打印机,在访问者的状态中保持在访问不同节点期间使用的偏移量(用于显示XML树的例子)

3 个答案:

答案 0 :(得分:5)

访客模式只是double dispatch

我不确定我是否同意您对访问者的实施。我会实现这样的东西:

interface MammalVisitor {
    void visit(Pirate pirate);
    void visit(Human human);
    void visit(Dog dog);
}

// Basic visitor provides no-op behaviour for everything.
abstract class MammalAdapter implements MammalVisitor {
    void visit(Pirate pirate) {};
    void visit(Human human) {};
    void visit(Dog dog) {};
}

然后实施变得更加清洁:

// We only want to provide specific behaviour for pirates
class WoodLegCounterVisitor extends MammalAdaptor {
    private int woodLegNumber = 0;
    @Override
    public void visit(Pirate pirate) {   
        woodLegNumber += pirate.getWoodLegNumber();
    }

    public int getWoodLegNumber() { return woodLegNumber; }
}

在回答您的实际问题时,使用访问者的主要优点是无需进行“instanceof”检查。它使您能够将用于处理层次结构的逻辑分离到单独的类中。它还使您能够在不更改原始类的情况下添加新行为。

答案 1 :(得分:4)

Visitor pattern是一个花哨的开关案例/模式匹配系统,以方便graph traversal

作为典型的功能语言提供模式匹配和有效的遍历图表的方式,兴趣更加有限。

即使在使用instanceof或使用enum的JAVA中,访问者更像是一种通用解决方案而不是通用解决方案,因为很多算法都不适合它。

答案 2 :(得分:3)

Visitor Pattern的目的是将对象结构(在您的情况下,Mammal)与算法(在您的情况下,计数器腿计数器算法)分开。

整个想法是你的对象(主要是java,JavaBeans)根本不改变它的结构,只引入一个新的virtual function来引入一个新的算法。

Jeff Foster的实现不同,可以使用泛型来简化代码。这为您的访问者带来了特殊性,例如:

public interface MammalVisitor<T extends Mammal> {

    public void visit(T mammal);
}

public class LegCounterVisitor implements MamalVisitor<Human> {
    private int legNumber = 0;
    @Override
    public void visit(Human mammal) {   legNumber += mammal.getLegsNumber();   }
    public int getLegNumber() { return legNumber; }
}

public class WoodLegCounterVisitor implements MamalVisitor<Pirate> {
    private int legNumber = 0;
    @Override
    public void visit(Pirate mammal) {legNumber += mammal.getWoodLegNumber();   }
    public int getLegNumber() { return legNumber; }
}