【问题标题】:Calling the correct method for an object, when the object is an instance of a base class当对象是基类的实例时,为对象调用正确的方法
【发布时间】:2016-03-21 19:52:37
【问题描述】:

我在 Java 中有以下示例:

public abstract class Vehicle {
    private final String name;
    private final String make;

    public Vehicle(final String name, final String make) {
        this.make = make;
        this.name = name;
    }
}

public final class Car extends Vehicle {
    public Car(final String name, final String make) {
        super(name, make);
    }
}

public final class Truck extends Vehicle  {
    final Integer grossVehicleWeight;

    public Truck(final String name, final String make, final Integer gvw) {
        super(name, make);
        this.grossVehicleWeight = gvw;
}

假设我想对车辆进行一些工作,并且该工作不依赖于车辆的子类。所以,我在另一个类中有这样的方法:

public void doStuff(public final Vehicle vehicle) {
    //do stuff here
    //then insert it into my database:
    insertVehicle(vehicle);
}

但是,我想在我的 insertVehicle 中做不同的事情,所以我为每个子类重写该方法:

public void insertVehicle(Car car) { //do stuff for a car }

public void insertVehicle(Truck truck) { //do stuff for a truck }

在我的 doStuff 方法中,我可以使用 instanceOf 来确定车辆的类别(汽车或卡车),然后将车辆转换为该类别并像这样调用 insertVehicle 方法:

public void doStuff(public final Vehicle vehicle) {
    //do stuff here
    //then insert it into my database:
    if (vehicle instanceof Car) {
        insertVehicle((Car) vehicle);
    } else {
        insertVehicle((truck) vehicle);
    }
}

但是,我读到使用 instanceof 并不是最好的方法。 1

我怎样才能最好地返工,这样我就不必使用 instanceof?

【问题讨论】:

  • 这些 insertVehicle 方法有什么作用? OO 方法是使 insertVehicle(或此方法的特定部分)成为 Vehicle 的实例方法,在每个子类中重写。或者使用访问者模式。发布您的代码以获得更具体的帮助。

标签: java polymorphism subclassing


【解决方案1】:

您可以使用访问者模式:

public interface VehicleVisitor {
    public void visit(Car car);
    public void visit(Truck truck);
}

public class Car extends Vehicle {

    @Override
    public void insert(VehicleVisitor vehicleVisitor) {
        vehicleVisitor.visit(this);
    }
}

public class Truck extends Vehicle {
    @Override
    public void insert(VehicleVisitor vehicleVisitor) {
        vehicleVisitor.visit(this);
    }
}

public abstract class Vehicle {
    public abstract void insert(VehicleVisitor vehicleVisitor);
}

public class VehicleVisitorImpl implements VehicleVisitor {

    @Override
    public void visit(Car car) {
        System.out.println("insert car");
    }

    @Override
    public void visit(Truck truck) {
        System.out.println("insert truck");
    }
}

public class Main {

    public static void main(String[] args) {
        Vehicle vehicle = new Car();
        // finally the agnostic call
        vehicle.insert(new VehicleVisitorImpl());
    }

}

【讨论】:

    【解决方案2】:

    您可以在车辆内部制作抽象函数

    public abstract void doStuff()
    

    从要修改的对象的实例中调用此函数

    ford.doStuff();     //ford is car instance 
    

    然后你可以使用它进行修改。

    doStuff()
    {
        this.cost += 10;
    }
    

    否则,您可以为车辆添加一个变量,指示车辆类型并返回它。喜欢:

      public void doStuff(public final Vehicle vehicle) {
           //do stuff here
           //then insert it into my database:
           if (vehicle.getType()== 'Car') {
                insertVehicle((Car) vehicle);
            } else {
                insertVehicle((truck) vehicle);
            }
       }
    

    这个变量'vehicleType'将在车辆类中,并将在构造函数中初始化:

      public final class Car extends Vehicle {
           public Car(final String name, final String make, final String vehicleType) {
                 super(name, make, type);
            }
        }
    

    【讨论】:

      【解决方案3】:

      这取决于您要解决的问题类型。如果是持久性,请确保您没有重新发明JPA。如果它是特定于类型的处理,那么您可以按照@denis 的建议解决它。或者,如果您想保持 POJO 样式的实体,您可以使用 strategy pattern,例如:

      Map<Class<?>, Consumer<Vehicle>> consumers;
      {
          consumers.put(Car.class, v -> insertVehicle((Car)v));
          consumers.put(Truck.class, v -> insertVehicle((Truck)v));
      }
      public void doStuff(public final Vehicle vehicle) {
          //do stuff here
          consumers
            .get(vehicle.getClass())
            .accept(vehicle);
      }
      

      【讨论】:

        【解决方案4】:

        一种方法是在Vehicle 中抽象出insertVehicle() 方法。然后在子类CarTruck中实现它们。

        但是,这会将逻辑移动到 POJO 中。也许最好将 db-logic 与 POJO 分开,即在这种情况下使用 instanceof

        public abstract class Vehicle {
            private final String name;
            private final String make;
        
            public Vehicle(final String name, final String make) {
                this.make = make;
                this.name = name;
            }
        
            public abstract void insertVehicle();
        }
        
        public final class Car extends Vehicle {
            public Car(final String name, final String make) {
                super(name, make);
            }
        
            public void insertVehicle() {
        
            }
        }
        
        public final  class Truck extends Vehicle {
            final Integer grossVehicleWeight;
        
            public Truck(final String name, final String make, final Integer gvw) {
                super(name, make);
                this.grossVehicleWeight = gvw;
            }
        
            public void insertVehicle() {
        
            }
        }
        
        public void doStuff(Vehicle vehicle) {
            //do stuff here
            //then insert it into my database:
            vehicle.insertVehicle();
        }
        

        【讨论】:

          【解决方案5】:

          如果您不喜欢将doStuff() 放入CarTruck,您可以为它们每个设置一个doStuff() 方法,并将通用的Vehicle 逻辑放入另一个方法中。

          private void doCommonStuff(final Vehicle vehicle) {
              //do stuff here
          }
          
          public void doStuff(final Car car) {
              doCommonStuff(car);
              //then insert it into my database:
              insertVehicle(car);
          }
          
          public void doStuff(final Truck truck) {
              doCommonStuff(truck);
              //then insert it into my database:
              insertVehicle(truck);
          }
          

          不过,使用泛型我们可以做得更好。

          public abstract class StuffDoer<T extends Vehicle> {
            public void doStuff(final T vehicle) {
              // do stuff here
              // then insert it into my database:
              insertVehicle(vehicle);
            }
          
            public abstract void insertVehicle(T vehicle);
          }
          
          public class CarStuffDoer extends StuffDoer<Car> {
            public void insertVehicle(Car car) {
              // whatever
            }
          }
          
          public class TruckStuffDoer extends StuffDoer<Truck> {
            public void insertVehicle(Truck truck) {
              // whatever else
            }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多