0

在不支持多分派的语言中,例如 Java,代码可能看起来像这样

/* Example using run time type comparison via Java's "instanceof" operator */
interface Collideable {
  void collideWith(Collideable other);
}

class Asteroid implements Collideable {
 public void collideWith(Collideable other) {
     if (other instanceof Asteroid) {
        System.out.println("AAAAAA");
     } else if (other instanceof Spaceship) {
        System.out.println("BBBBBB");
     } else if (other instanceof Plane) {
        System.out.println("CCCCCCC");
     } 
 }
}
class Spaceship implements Collideable {
 public void collideWith(Collideable other) {
     if (other instanceof Asteroid) {
         System.out.println("DDDDDDD");
     } else if (other instanceof Spaceship) {
         System.out.println("EEEEEEE");
     } else if (other instanceof Plane) {
        System.out.println("FFFFFFF");
     } 
 }
}
class Plane implements Collideable {
 public void collideWith(Collideable other) {
     if (other instanceof Asteroid) {
       System.out.println("GGGGGGG");
     }else if (other instanceof Spaceship) {
       System.out.println("HHHHHHH");
     }else if (other instanceof  Plane) {
       System.out.println("KKKKKK");
     }
 }
}

由于访问者模式可以帮助解决这个问题,我正在考虑是否应该使用 void collideWith() { visitor.visit(this); 来实现每个叶子类。// 在 setter 中设置访问者 }

interface Visitor {
   void visit(Collideable c);
   void visit(Asteroid c);
   void visit(Spaceship c);
   void visit(Plane c);
}

那么每个唯一的 println 应该在如下访问者的子类中实现?

class AsteroidVisitor implements Visitor {
   void visit(Collideable c) {}
   void visit(Asteroid c) { System.out.println("AAAAAA"); }
   void visit(Spaceship c) { System.out.println("BBBBBBB"); }
   void visit(Plane c) { System.out.println("CCCCCC"); }
}
//etc for SpaceshipVisitor and PlaneVisitor()

替换instanceof是处理这种重构的最佳方法吗?

编辑:打印输出只是每种方法中独特操作的示例,而不是最终结果。我修改了我的示例以使其清楚。

4

3 回答 3

1

在您提供的示例中,您不需要两个接口,您只需要一个Collideable接口。Collideable 接口可以定义如下:

interface Collideable {
  void collideWith(Collideable other);
  String getType();
}

然后Spaceship,PlaneAsteroid将全部实现collideWithand getType。作为一个示例实现,Spaceship 的实现如下所示:

class Spaceship implements Collideable {
 public void collideWith(Collideable other) {
     System.out.println(this.getType() + " collides with " + other.getType());
 }

 public String getType(){
   return "Spaceship";
 }
}

你可以更进一步,将 Collideable 声明为一个抽象类,提供 collideWith 的实现,因为它总是相同的。例如:

abstract class Collideable {
  void collideWith(Collideable other){
    System.out.println(this.getType() + " collides with " + other.getType());
  }
  String getType();
}

作为旁注和一般提示,您的Visitor界面设计不佳。接口的目的是定义实现该接口的所有类必须提供方法定义(实现)的方法。但是,您的接口非常具体:它提供了几种类型(SpaceshipAsteroid等)作为方法参数。因此这是一个糟糕的设计,为什么你甚至需要那个界面?它看起来不能在其他任何地方使用。

于 2013-04-19T20:31:38.233 回答
0

双重分派与方法重载有关。您似乎在代码段中显示的内容仅依赖于单次调度——您有一个接口,其实现将在运行时调用。

在我看来,真正的问题是你的Collidable接口应该有一个getNameorgetType方法,它返回“小行星”或“宇宙飞船”或者你有什么,然后你的实现collidesWith可以简单地打印出它自己的名字和它的碰撞伙伴的名字。在此示例中,我没有看到您需要双重分派——这只是为什么instanceof代码异味的一个例子。

于 2013-04-19T20:21:38.030 回答
0

我希望我理解你的问题是正确的。

您可以考虑添加一个方法,例如:

interface Collideable {
    void collideWith(Collideable other);
    //new method
    String getSpecificStuff(); 
}

class Asteroid implements Collideable {

    String getSpecificStuff(){
        return "Asteroid";
    }
}

然后方法:

 public void collideWith(Collideable other) {
     System.out.println(this.getSpecificStuff() +" VS " + other.getSpecificStuff());
 }

其他子类只是遵循上面的规则。

当然,这些只是例子。getSpecificStuff()你可以用你的真实逻辑实现你自己的方法。

代码不是用 IDE 编写的,如果有错别字或伤眼的格式,我很抱歉。

于 2013-04-19T20:29:48.787 回答