如何摆脱 instanceof?

How to get rid of instanceof?

有人告诉我,在下面的代码中使用 instanceof 是一种不好的做法,因为它会变得重复并使扩展变得困难。不过,我对 Java 还是个新手,并不能马上看出我的替代方案是什么,你会建议我做什么来摆脱实例和抽象代码?

void moveit(Vehicle car) {
        if(car instanceof Volvo240){
            volvoPoint.x = (int) car.getXCoordinate();
            volvoPoint.y = (int) car.getYCoordinate();
        }
        if(car instanceof Scania) {
            scaniaPoint.x = (int) car.getXCoordinate();
            scaniaPoint.y = (int) car.getYCoordinate() + 100;
        }
        if(car instanceof Saab95) {
            saabPoint.x = (int) car.getXCoordinate();
            saabPoint.y = (int) car.getYCoordinate() + 200;
        }
        repaint();
    }

不知道完整的代码,并假设您只有这三个子class。侵入性最小的方法是利用方法重载:

void moveit(Volvo240 car){
     volvoPoint.x = (int) car.getXCoordinate();
     volvoPoint.y = (int) car.getYCoordinate();
     repaint();
}

void moveit(Scania car){
     volvoPoint.x = (int) car.getXCoordinate();
     volvoPoint.y = (int) car.getYCoordinate() + 100;
     repaint();
}

void moveit(Saab95 car){
     saabPoint.x = (int) car.getXCoordinate();
     saabPoint.y = (int) car.getYCoordinate() + 200;
     repaint();
}

void moveit(Vehicle car){
     repaint();
}

在我看来,变量volvoPoint.xvolvoPoint.y应该属于classVolvo240(其他变量也一样)。但是您将这些变量(应该属于 classes Volvo240ScaniaSaab95)保存在一个地方,这样(我假设)您可以repaint 基于这些变量的值。

您应该考虑另一种方法,您 教导 每个 Vehicle 如何 repaint 自己。因此,将 repaint 逻辑和那些变量移动到每个子 classes,因此:

public class Volvo240 extends Vehicle{

       public repaint(){
              volvoPoint.x = (int) car.getXCoordinate();
              volvoPoint.y = (int) car.getYCoordinate();
              // do the repaint logic
       } 
}

InstanceOf 会使引入新型汽车变得困难。您将必须找到您进行这些 instanceof 检查的所有地方,并用新车进行修改。参见“对扩展开放,对修改关闭”的原则。

你应该有一个界面

interface Vehicle {
  Integer getXCoordinate();
  Integer getYCoordinate();
  void moveIt(Point point);
}

以及三个实施方案,Saab95、Volvo240 和 Scania

class Saab95 implements Vehicle {
   moveIt(Point point) {
      point.x = getXCoordinate();
      point.y= getYCoordinate() + 200
   }
}

其他车以此类推

我要直接说,我对你原来的解决方案没有意见。更多相关信息,请参见此 post 的底部。如果您真的希望当前代码在不使用 instanceof 的情况下工作,这里有一种方法。

您可以创建一个新的 class 来存储与每种类型的车辆相关的信息,如下所示:

public class VehicleInfo {
    private final Point point;
    private final int offset;

    public VehicleInfo(Point point, int offset) {
        this.point = point;
        this.offset = offset;
    }

    public Point getPoint() {
        return point;
    }

    public int getOffset() {
        return offset;
    }
}

然后您可以像这样在您的方法中使用 class:

private static final HashMap<Type, Point> POINT_MAP = new HashMap<Type, Point>() {
    {
        put(Volvo240.class, new VehicleInfo(volvoPoint, 0));
        put(Scania.class, new VehicleInfo(scaniaPoint, 100));
        put(Saab95.class, new VehicleInfo(saabPoint, 200));
    }
};

void moveit(Vehicle car) {
    VehicleInfo info = POINT_MAP.get(car.getClass());
    info.getPoint().x = car.getXCoordinate();
    info.getPoint().y = car.getYCoordinate() + info.getOffset();
    repaint();
}

也就是说,我对您使用 instanceof 的原始解决方案真的没有任何问题。该程序将不得不在某个时候检查参数的类型,因此最好在您的代码中明确显示。除非程序的这一部分将被反复修改,并且这是某个每个人都需要使用标准编码约定的大型项目的一部分,否则我会坚持使用你所拥有的,不管这里的其他人怎么说。最近有人向我介绍了 YAGNI 的概念,我认为这绝对适用于此。如果有效,那就有效。