【问题标题】:Distribute responsibilities between related interfaces在相关接口之间分配职责
【发布时间】:2018-11-19 00:42:34
【问题描述】:

我有两个相关的接口,但是我不确定如何分配一些职责(方法)。

假设一个接口是 Truck,另一个是 ParkingLot。

可以有不同的 Truck 实现,例如 DumpTruck、BoxTruck、FireTruck...,以及 ParkingLot 的不同实现,例如 TruckParkingLot、CarParkingLot、MotorcycleParkingLot...

我有一个程序基本上是一个 TruckParkingLot,它包含以下内容:

 private Map<String, Truck> trucks = new ConcurentHashMap();

 public void registerTruck(String plate, Truck truck) {
      // Code to put a new Truck in the ParkingLot
 }

 public void removeTruck(String plate) {
      // Code to remove a Truck from the ParkingLot
 }

 public Truck getTruckByPlate(String plate) {
      // Code to get Truck with key (plate) from the Map trucks
  }

然后是 Truck 接口,它有几个实现。每个实现都不同,因为它们有不同的实例变量。

我还想实现另外两种方法:更新和打印。前者更新选中卡车的实例变量;后者打印所选卡车的实例变量。

我的疑问是这些方法应该在 ParkingLot 接口中还是在 Truck 接口中。

一方面我觉得这些方法应该在 ParkingLot 接口中,因此客户端只需访问 TruckParkingLot y 类即可获取所需的所有信息并进行管理。此外,使用反射,您可以轻松管理类的任何实例(包括私有),而无需知道给定对象有多少实例变量或哪些实例变量。

另一方面,通过将这两个方法放在 ParkingLot 接口中,该接口管理的数据不是 TruckParkingLot 内置的,而是来自给定卡车的实例变量,这违反了责任驱动的设计原则 (私有类实例只能在它们所属的类中处理)。另外,如果更新和打印到 ParkingLot 接口,那么 Truck 接口就会变成一个空接口(没有更多的 Truck 方法)。

另一方面,如果我在 Truck 接口中实现这两个方法,那么我将为该接口的每个实现复制代码,因为更新或打印 BoxTruck 和 DumpTruck 的实例变量的方式几乎使用 getter 和 setter 时相同(使用反射时完全一样)

另一种可能的解决方案是为卡车创建一个抽象类并实现更新和打印方法,其余卡车只是继承这个类。 (我还需要为这个抽象类创建一个接口吗?)。

总之,当我第一次写这篇文章时,我想到了这些选项:

A) 带有所有方法的 ParkingLot 接口;没有方法的卡车接口(空)。

 interface ParkingLot {
     public void registerVehicle(String plate, Vehicle vehicle);
     public void removeVehicle(String plate);
     public Vehicle getVehicleByPlate(String plate);
     public void updateVehicle(Vehicle vehicle);
     public void printVehicleData(Vehicle vehicle);
 }

 public class TruckParkingLot implements ParkingLot {
     private Map<String, Truck> trucks = new ConcurentHashMap();

     public void registerVehicle(String plate, Vehicle truck) {
         // Code to put a new Truck in the ParkingLot
     }

     public void removeVehicle(String plate) {
          // Code to remove a Truck from the ParkingLot
     }

     public Truck getVehicleByPlate(String plate) {
         // Code to get Truck with key (plate) from the Map trucks
     }

     public Truck updateVehicle(Vehicle truck) {
         // Code to update the inst vars of a Truck using reflection
     }

     public Truck printVehicleData(Vehicle truck) {
         // Code to print the inst vars of a Truck using reflection
     }
 }

 interface Truck extends Vehicle {
     // Nothing here
 }

 public class DumpTruck implements Truck {
     // Private instance variables
     // Constructor
     // Setters and getters
 }

 public class BoxTruck implements Truck {
     // Other private instance variables
     // Another constructor
     // Setters and getters
 }

B) 带有方法的 ParkingLot 接口,带有更新和打印方法的卡车接口。

 interface ParkingLot {
     public void registerVehicle(String plate, Vehicle vehicle);
     public void removeVehicle(String plate);
     public Vehicle getVehicleByPlate(String plate);
 }

 public class TruckParkingLot implements ParkingLot {
     private Map<String, Truck> trucks = new ConcurentHashMap();

     public void registerVehicle(String plate, Vehicle truck) {
         // Code to put a new Truck in the ParkingLot
     }

     public void removeVehicle(String plate) {
          // Code to remove a Truck from the ParkingLot
     }

     public Truck getVehicleByPlate(String plate) {
         // Code to get Truck with key (plate) from the Map trucks
     }
 }

 interface Truck extends Vehicle {
     public void updateVehicle(Vehicle vehicle);
     public void printVehicleData(Vehicle vehicle); 
     // Both methods should actually be inherited from Vehicle
     // interface, but this doesn't affect the point here.
     // Technically the Truck methods would still be empty.
 }

 public class DumpTruck implements Truck {
      // Private instance variables
      // Constructor
      // Setters and getters

      public Truck updateVehicle(Vehicle truck) {
          // Code to update the inst var using setters and getters
      }

     public Truck printVehicleData(Vehicle truck) {
         // Code to print the inst var using setters and getters
     }
 }

 public class BoxTruck implements Truck {
     // Other private instance variables
     // Another constructor
     // Setters and getters

     public Truck updateVehicle(Vehicle truck) {
         // Code to update the inst vars using setters and getters
     }

     public Truck printVehicleData(Vehicle truck) {
         // Code to print the inst vars using setters and getters
     }
 }

C) ParkingLot 接口及其方法,Truck 抽象类。

interface ParkingLot {
     public void registerVehicle(String plate, Vehicle vehicle);
     public void removeVehicle(String plate);
     public Vehicle getVehicleByPlate(String plate);
 }

 public class TruckParkingLot implements ParkingLot {
     private Map<String, Truck> trucks = new ConcurentHashMap();

     public void registerVehicle(String plate, Vehicle truck) {
         // Code to put a new Truck in the ParkingLot
     }

     public void removeVehicle(String plate) {
          // Code to remove a Truck from the ParkingLot
     }

     public Truck getVehicleByPlate(String plate) {
         // Code to get Truck with key (plate) from the Map trucks
     }
 }

 public abstract class Truck implements Vehicle {
     // private common instance variables to all trucks
     // Constructor with all common instance variables
     // Setters and getters

     public Truck updateVehicle(Vehicle truck) {
         // Code to update the inst vars using setters and getters
     }

     public Truck printVehicleData(Vehicle truck) {
         // Code to print the inst vars using setters and getters
     }
 }

 public class DumpTruck implements Truck {
     // Private unique instance variables to DumpTruck
     // Inherit constructor from superclass
     // + initialize unique instance variables for this subclass
     // Setters and getters for the unique inst vars of this subclass

     @override
     public Truck updateVehicle(Vehicle truck) {
         // inherit code from superclass 
        // + update the unique instance variables of this subclass
     }

     @override
     public Truck printVehicleData(Vehicle truck) {
         // inherit code from superclass
         // + print the unique instance variables of this subclass
     }
 }

 public class BoxTruck implements Truck {
     // Private unique instance variables to DumpTruck
     // Inherit constructor from superclass
     // + initialize unique instance variables for this subclass
     // Setters and getters for the unique inst vars of this subclass

     @override
     public Truck updateVehicle(Vehicle truck) {
         // inherit code from superclass
         // + update the unique instance variables of this subclass
     }

     @override
     public Truck printVehicleData(Vehicle truck) {
         // inherit code from superclass
         // + print the unique instance variables of this subclass
     }
 }

就个人而言,我更喜欢将所有方法都放在 ParkingLot 接口中并使用反射(选项 A),将 Truck 接口留空;或为卡车使用抽象类(选项 C)

但是,真的,我什么都不确定。

提前致谢。

【问题讨论】:

    标签: java class methods reflection interface


    【解决方案1】:

    我相信真正的 OOP 设计要求类型密切模仿它们所代表的真实世界对象。我假设当 update() 被调用时,您打算从数据存储中检索一些值,然后依次更新实例变量。如果这是正确的,它会使您的对象可变,这可能是糟糕的设计。

    在我看来,卡车从车库出来,然后进入现实世界中的车库,这也是你的对象结构应该做的。您可以通过将卡车传递到车库界面来“更新”您的卡车。为了使 Truck 不可变,该方法会传回新 Truck 的实例。例如:

    interface Garage {
            Truck service(final Truck truck);
    }
    

    服务方法是您的“更新”方法,但也更接近于模仿现实世界的领域。就打印而言,我建议完全是另一个类,并且该类的构造函数将针对各种类型的卡车进行重载,因此您可以以不同的方式分别处理它们。

    interface TruckPrinter {
            void print();
    }
    class BasicTruckPrinter implements TruckPrinter {
            private String output;
            BasicTruckPrinter(final DumpTruck truck) { /* convert truck to output */ }
            BasicTruckPrinter(final FireTruck truck) { ... }
            BasicTruckPrinter(final BoxTruck truck) { ... }
    
            void print() {
                  System.out.println(output);
            }
    }       
    

    希望对你有帮助……

    【讨论】:

    • 嗨,Brian,感谢您的回答,特别是关于打印的部分。我注意到我在导致误解的问题中犯了一个巨大的错误。车库我的意思是停车场(在西班牙语中,地下或有盖停车场的词是“garaje”,因此我的误译)。我在原帖中更正了。这意味着车库本身不提供更新任何东西的服务。
    • 我仍然认为在您的对象模型中为您的卡车对象以一种对现实世界领域有意义的方式“更新”的位置很重要。他们将更新什么样的内部数据?你能解释一下吗?
    • 我对主帖做了很大的修改,我相信现在更容易理解了。他们将更新品牌、电机功率、高度等数据......基本上是任何给定卡车的私有实例变量
    猜你喜欢
    • 2010-12-11
    • 1970-01-01
    • 2020-08-13
    • 2019-02-10
    • 1970-01-01
    • 1970-01-01
    • 2011-02-10
    • 2021-12-31
    • 1970-01-01
    相关资源
    最近更新 更多