使用InstanceOf实现访问者

时间:2012-12-17 13:04:16

标签: java design-patterns visitor open-closed-principle

我很好地掌握访客模式。 但是,我不知道。

使用访问者模式的最重要动机是在客户端添加涉及特定数据模型的逻辑,而无需检查实际数据对象类型。用于求解的技术称为Double-Dispatching。

所以,这里是一个实现accept()方法的数据模型的代码片段:

public class Ferrari extends Car {
    //......
    @Override
    public void accept(Visitor v){
      v.visit(this);
    }
  }

这里有PrintCarVisitor实施Visitor界面:

public class PrintCarVisitor implements Visitor {
  //...
  @Override
  public void visit(Ferrari c){
    System.out.println("Ferrari");
  }
}

因此,不需要if/else系列和instanceof系列。

任何客户都是:

Visitor visitor = new PrintCarVisitor();
car.accept(visitor);  //no need to know the exact Car's type

但是,由于访问者没有保持开放/封闭原则(因为新的数据模型会导致通过添加自己的visit方法来破坏类),为什么我们要麻烦双重调度?

我们不能只在访问者实施中隔离if/else系列。

使用这个假设的替代方案,这部分代码将消失:

 public class Ferrari extends Car {
    //This method is not needed anymore with this alternative
    @Override
    public void accept(Visitor v){
      v.visit(this);
    }
  }

PrintCarVisitor将是:

public class PrintCarVisitor {
   public void visit(Car c){
     if(c instanceof Ferrari){
       System.out.println("Ferrari");
     }
   }      
}

使用这种替代方案,每个调用者仍然会处理数据模型抽象,如下所示:

new PrintCarVisitor().visit(car); //no need to know the exact data type in client side

先验,第二种方法很好,因为它不涉及纯访客模式实现过程中产生的所有样板。

我认为这种方法有两个缺点:

1)无法保证(如Visitor接口强加)任何使用过的访问者都会处理与当前处理的Car对应的方法。

2)BoilerPlate代码在使用instanceofcasting系列的Visitor实现类中保持较重。

它们是否有任何其他缺点解释为什么访问者模式必须使用Double Dispatching,因此不能简单地隔离类中的instanceof系列(例如静态Factory)?

2 个答案:

答案 0 :(得分:4)

  1. 如果你这样做,你不再有访客,你基本上有某种处理器。您的代码只是一个列表迭代,每次通过循环时,您将以前使用的accept传递给处理器,而处理器正式是访问者。而不是访问访问访问者,你在某种意义上颠倒了范式;访问者成为访问者,因为它最初传递给工作者。你可以做到这一点;你不会称之为访客。

  2. 传统智慧通常要求instanceof的使用应留作最后的手段。当你可以让Java的多态性为你做的时候,你为什么要使用instanceof?拥有物体的一点是这种好处。如果你这样做,为什么不避免重写方法,只是使用instanceof来确定在类的方法中做什么,而不是依赖于动态调度,以覆盖方法?

答案 1 :(得分:1)

Xtext项目遇到了同样的问题,他们创建了一个帮助类PolymorphicDispatcher。简而言之,PolymorphicDispatcher在运行时执行编译器在编译时所执行的操作:找到最佳匹配一组参数的方法并调用它。

所以你的访客看起来像:

 public class PrintCarVisitor {
   public void visit(Car c){
      System.out.println("Just a car");
   }
   public void visit(Ferrari c){
      System.out.println("A Ferrari!");
   }
 }

Here is the source.