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

关于访问者模式的问题(Java示例),java,design-patterns,visitor,Java,Design Patterns,Visitor,我只是想了解使用访问者模式的主要好处 下面是一个示例Java实现 /////////////////////////////////// // Interfaces interface MamalVisitor { void visit(Mammal mammal); } interface MammalVisitable { public void accept(MamalVisitor visitor); } interface Mammal extends MammalVi

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

下面是一个示例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);
    }
}
///////////////////////////////////
而不是

        legCounterVisitor.visit(mammal);
        woodLegCounterVisitor.visit(mammal);
第二个选项似乎删除了模型零件上的accept(…)方法

在我发现的许多示例中,它们似乎没有为模型对象使用公共接口。我添加它是因为我只需要添加一个访问(哺乳动物)方法,而不是为每个哺乳动物实现一个。 把我所有的东西都变成哺乳动物好吗?(我想有时候这根本不可能)。这仍然是一种访客模式吗

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


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

访问者模式只是一个例子

我不确定我是否同意你对访问者的实施。我将实现如下内容:

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”检查。它使您能够将处理层次结构的逻辑分离到单独的类中。它还使您能够在不更改原始类的情况下添加新行为。

的目的是将对象结构(在您的情况下是哺乳动物)与算法(在您的情况下是计数器腿计数器算法)分离

整个想法是,您的对象(主要是java、JavaBeans)根本不会改变其结构,只会引入一个新的算法来引入一个新的算法

与的实现不同,可以使用泛型使代码更容易。这为您的访客带来了特殊性,例如:

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; }
}
公共界面{
公众访问(T哺乳动物);
}
公共类LegCounterVisitor实现MamalVisitor{
私有整数=0;
@凌驾
公共无效访问(人类哺乳动物){legNumber+=哺乳动物.getLegsNumber();}
public int getLegNumber(){return legNumber;}
}
公共类WoodLegCounterVisitor实现MamalVisitor{
私有整数=0;
@凌驾
公共无效访问(海盗哺乳动物){legNumber+=emopal.getWoodLegNumber();}
public int getLegNumber(){return legNumber;}
}
是一款别致的开关盒/模式匹配系统,便于安装

由于典型的函数式语言提供了模式匹配和遍历图的有效方法,人们的兴趣受到了很大的限制


即使是在JAVA中,使用
instanceof
或使用
enum
,访问者也比通用解决方案更适合执行任务,因为许多算法都不适合它。

实际上,我在这里也觉得差不多。我使用了instanceof,但我想在真正复杂的访问者模式中,我们宁愿避免使用instanceof……事实上,我使用instanceof是因为我很懒,但我可以在没有任何instanceof的情况下,以访问者/站场方式做同样的事情……在你的示例中,你怎么会有木腿和普通腿的总和?@Jeff Forster,Simple,由于
Pirate
类有一个
getWoodLegNumber()
方法,因此
WoodLegCounterVisitor
将只访问任何属于海盗的内容。我想了解的是,您的访问者实现不能同时具有海盗和人类的行为(没有类型转换),因为您只有一个访问方法。我当时的印象是,摆脱字体铸造是一件好事。我可能没有清楚地表达我的观点。您的访问者有一个单一的访问方法。这是不好的,因为通常情况下,您希望不同对象具有不同的行为。也许您只是指出了一种在这种特定情况下缩短代码的方法,但一般来说,为访问者提供单一方法并不是一件好事。关于许多未实现的方法,我确实同意你的观点,这就是为什么通常会有一个“VisitorAdapter”类提供空实现(类似于Swing函数)的原因,Jeff Forster在上一篇评论中提到了这个原因。我只是在学习访问者,这是一个混乱的模式实现,因为它只有一个单一的访问方法。
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; }
}