具有可变参数类型的接口方法

Boj*_*vic 4 java oop generics design-patterns solid-principles

我有java接口和类实现,在调用类似的行为时需要不同的参数.以下哪项最适合?

在第一个选项中,我有不同的类从基接口继承公共行为,所有差异只在类中直接实现,而不是在接口中实现.这个似乎最合适,但我必须在代码中进行手动类型转换.

public class VaryParam1 {

    static Map<VehicleType, Vehicle> list = new HashMap<>();

    static List<Car> carsList = new ArrayList<>();
    static List<TruckWithTrailer> trucksList = new ArrayList<>();

    public static void main(String[] args) {
        list.put(VehicleType.WITHOUT_TRAILER, new Car());
        list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());

        //violates LSP?
        ((Car)list.get(VehicleType.WITHOUT_TRAILER)).paint(1); //ok - but needed manual cast
        ((TruckWithTrailer)list.get(VehicleType.WITH_TRAILER)).paint(1, "1"); //ok - but needed manual cast

        carsList.add(new Car());
        trucksList.add(new TruckWithTrailer());

        //Does not violate LSP
        carsList.get(0).paint(1);
        trucksList.get(0).paint(1, "1");
    }
}

enum VehicleType {
    WITHOUT_TRAILER,
    WITH_TRAILER;
}

interface Vehicle{
    //definition of all common methods
    void drive();
    void stop();
}

class Car implements Vehicle {

    public void paint(int vehicleColor) {
        System.out.println(vehicleColor);
    }

    @Override
    public void drive() {}

    @Override
    public void stop() {}
}

class TruckWithTrailer implements Vehicle {

    public void paint(int vehicleColor, String trailerColor) {
        System.out.println(vehicleColor + trailerColor);
    }

    @Override
    public void drive() {}

    @Override
    public void stop() {}
}
Run Code Online (Sandbox Code Playgroud)

在第二个选项中,我已将方法一级移动到接口,但现在我需要使用UnsupportedOpException实现行为.这看起来像代码味道.在代码中,我不必进行手动转换,但我也可以调用在运行时产生异常的方法 - 无需编译时检查.这不是一个大问题 - 只有这种方法有异常,看起来像代码味道.这种实施方式是最佳实践吗?

public class VaryParam2 {

    static Map<VehicleType, Vehicle> list = new HashMap<>();

    public static void main(String[] args) {
        list.put(VehicleType.WITHOUT_TRAILER, new Car());
        list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());

        list.get(VehicleType.WITHOUT_TRAILER).paint(1); //works
        list.get(VehicleType.WITH_TRAILER).paint(1, "1"); //works

        list.get(VehicleType.WITHOUT_TRAILER).paint(1, "1"); //ok - exception - passing trailer when no trailer - no compile time check!
        list.get(VehicleType.WITH_TRAILER).paint(1); //ok - exception - calling trailer without trailer args - no compile time check!
    }
}

enum VehicleType {
    WITHOUT_TRAILER,
    WITH_TRAILER;
}

interface Vehicle{
    void paint(int vehicleColor);
    void paint(int vehicleColor, String trailerColor);    //code smell - not valid for all vehicles??
}

class Car implements Vehicle {

    @Override
    public void paint(int vehicleColor) {
        System.out.println(vehicleColor);
    }

    @Override
    public void paint(int vehicleColor, String trailerColor) {    //code smell ??
        throw new UnsupportedOperationException("Car has no trailer");
    }
}

class TruckWithTrailer implements Vehicle {

    @Override
    public void paint(int vehicleColor) {  //code smell ??
        throw new UnsupportedOperationException("What to do with the trailer?");
    }

    @Override
    public void paint(int vehicleColor, String trailerColor) {
        System.out.println(vehicleColor + trailerColor);
    }
}
Run Code Online (Sandbox Code Playgroud)

这里我使用泛型以便在接口中使用通用方法,并且在每个类实现中决定参数类型.这里的问题是我有未经检查的绘画调用.这更像是选项1中直接投射的问题.在这里我也有可能调用我不应该的方法!

public class VaryParam3 {

    static Map<VehicleType, Vehicle> list = new HashMap<>();


    public static void main(String[] args) {
        list.put(VehicleType.WITHOUT_TRAILER, new Car());
        list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());

        list.get(VehicleType.WITHOUT_TRAILER).paint(new VehicleParam());    //works but unchecked call
        list.get(VehicleType.WITH_TRAILER).paint(new TruckWithTrailerParam());    //works but unchecked call

        list.get(VehicleType.WITHOUT_TRAILER).paint(new TruckWithTrailerParam()); //works but should not!
        list.get(VehicleType.WITH_TRAILER).paint(new VehicleParam());   //ClassCastException in runtime - ok but no compile time check
    }
}

enum VehicleType {
    WITHOUT_TRAILER,
    WITH_TRAILER;
}

class VehicleParam {
    int vehicleColor;
}

class TruckWithTrailerParam extends VehicleParam {
    String trailerColor;
}

interface Vehicle<T extends VehicleParam>{
    void paint(T param);
}

class Car implements Vehicle<VehicleParam> {

    @Override
    public void paint(VehicleParam param) {
        System.out.println(param.vehicleColor);
    }
}

class TruckWithTrailer implements Vehicle<TruckWithTrailerParam> {

    @Override
    public void paint(TruckWithTrailerParam param) {
        System.out.println(param.vehicleColor + param.trailerColor);
    }
}
Run Code Online (Sandbox Code Playgroud)

所以问题是 - 这3个选项中哪一个是最好的(或者如果还有其他选项我还没找到)?在进一步维护,改变等方面

UPDATE

我更新了问题,现在我有了paint方法,只有在构造对象后才可以调用它.

到目前为止,这看起来是最好的选择,因为它在下面的帖子中建议:

public class VaryParam4 {

    static Map<VehicleType, Vehicle> list = new HashMap<>();

    public static void main(String[] args) {
        list.put(VehicleType.WITHOUT_TRAILER, new Car());
        list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());

        list.get(VehicleType.WITHOUT_TRAILER).paint(new PaintConfigObject());    //works but can pass trailerColor (even if null) that is not needed
        list.get(VehicleType.WITH_TRAILER).paint(new PaintConfigObject());    //works
    }
}

enum VehicleType {
    WITHOUT_TRAILER,
    WITH_TRAILER;
}

class PaintConfigObject {
    int vehicleColor;
    String trailerColor;
}

interface Vehicle{
    void paint(PaintConfigObject param);
}

class Car implements Vehicle {

    @Override
    public void paint(PaintConfigObject param) {
        //param.trailerColor will never be used here but it's passed in param
        System.out.println(param.vehicleColor);
    }
}

class TruckWithTrailer implements Vehicle {

    @Override
    public void paint(PaintConfigObject param) {
        System.out.println(param.vehicleColor + param.trailerColor);
    }
}
Run Code Online (Sandbox Code Playgroud)

CKi*_*ing 8

更好的选择是摆脱方法的重载版本,drive并传递构造函数中子类所需的任何信息:

interface Vehicle{
    void drive();
}

class Car implements Vehicle {
    private int numberOfDoors;

    public Car(int numberOfDoors) {
         this.numberOfDoors = numberOfDoors;
     }

    public void drive() {
        System.out.println(numberOfDoors);
    }
}


class TruckWithTrailer implements Vehicle {
    private int numberOfDoors;
    private int numberOfTrailers;

    public TruckWithTrailer(int numberOfDoors,numberOfTrailers) {
          this.numberOfDoors = numberOfDoors;
          this.numberOfTrailers = numberOfTrailers;
    }

    @Override
    public void drive() {
        System.out.println(numberOfDoors + numberOfTrailers);
    }
}
Run Code Online (Sandbox Code Playgroud)

解决有关paint在运行时决定的注释,您可以向带有paint可变参数的车辆添加方法:

interface Vehicle{
    void drive();
    void paint(String ...colors);
}
Run Code Online (Sandbox Code Playgroud)

正如评论中所讨论的,如果paint方法中要使用的参数数量因不同的车辆类型而异,请定义一个名为PaintSpecification包含诸如的属性的类vehcileColor,trailerColor并将paint方法更改为具有类型的参数PaintSpecification.

interface Vehicle{
    void drive();
    void paint(PaintSpecification spec);
}
Run Code Online (Sandbox Code Playgroud)

上述所有方法的优点是所有Vehicle实现都遵循单一合同,允许您执行操作,例如将所有Vehicle实例添加到a Listpaint逐个调用方法,而不管其类型如何.

  • @ bojanv55然后你没有创建对象,直到你有所需的参数? (2认同)
  • @ bojanv55在这种情况下,使用2个"Car"和"Truck"列表,并在它们两个上实现`paint`,而不是`Vehicule`. (2认同)