面向对象设计中处理类型指示器的最佳方法是什么?

5
我们有一个业务模型,需要识别不同类型的车辆,例如摩托车、船只、游艇、农用设备、雪地摩托、重型卡车、轻型卡车、乘用车和“其他”。
我认为有两种处理方式:创建一个Vehicle类,其中包括几个子类(如船只、卡车、汽车),并有一个单一的枚举指示实际类型。这样可以表示具有共同特征的车辆,并使其具有匹配的类和枚举。但是,它允许您使用不匹配的枚举与类。
我认为传统的处理方式是为每种类型创建一个单独的子类,可能是车辆的子类或车辆的子类。例如,重型和轻型卡车可能是卡车的子类;船只和游艇可能是船只的子类;雪地摩托可能是车辆的子类。问题是,它并不能很好地表示其他概念。
有没有人对这些情况有什么模式或最佳实践?
彼得
编辑:对象的目的是传达有关车辆的信息,并能够以用户友好的方式显示该信息。例如,从数据库中提取数据并在屏幕上显示以供查看/编辑。
这导致传统的面向对象方法的另一个缺点是,它倾向于不使用实际类型的类型指示符,因为要显示类型,需要执行某种实例化并将该值转换为用户友好的形式。
(另外,澄清我谈论的是单个枚举,而不是每个子类的枚举。)

你能创建子类吗?当你想知道正在处理哪种类型时,请使用 instanceof。 - onesixtyfourth
1
它取决于您的系统行为 - 简单起步,通过实现实际使用功能来演化领域模型,在必要时进行重构。 - Nick Holt
你还没有暴露任何用例。只是描述你所需的数据结构。你需要从设计一个场景开始。然后你会想出有效的数据结构。 - Damian Leszczyński - Vash
1
@Rusher 这个问题是关于类型层次结构的最佳实践。它要求比较两种方法:通过字段区分类型(枚举类型,列举不同的车辆类型),或创建完整的类层次结构。 - Joffrey
是的,我在考虑为“车辆”创建一个通用枚举,而不是为“车辆”的子类创建单独的枚举。但我也可以接受更小、更具体的枚举。 - Risser
显示剩余9条评论
5个回答

6

我建议从一个具体的交通工具开始,不要添加过多的抽象概念。

不要使用类层次结构来描述可以使用字段描述的内容。

这些类型的代码是否如此不同?

例如,假设您有一个之前没有想到过的类型,比如水陆两用车(可以在水上行驶的汽车)、平衡车或独轮车。您可以为每种类型创建一个新类,也可以完全以数据驱动的方式使用字段。

name: aqua-car
type: Exotic Car
travelsOnWater: true
travelsOdLand: true
wheels: 4

我建议您阅读You Ain't Gonna Need It。最好只添加您需要的抽象,而不是您可以想象的抽象。


我同意。这基本上听起来像是“使用接口和组合”,我认为我倾向于这种方法。然而,它并不能解决识别“实际”类型的问题,我不想使用instanceof来完成。 - Risser
@Risser 如果你的类型有限,可以使用枚举,或者使用字符串来描述类型。 - Peter Lawrey

4

真正的问题

在两个“事物”有多不同之前,它们需要拥有自己的类吗?

答案

这完全取决于您将如何使用这些对象。

  • 您是否将根据它们具有的某些属性进行比较(例如,它是否浮动)?如果是这样,则使用一个具有区分属性的单个类是有意义的。
  • 您是否要在界面上显示对象?在您的应用程序中,在单个表中显示轻型卡车和重型卡车是否有意义?如果它们不能有意义地存在并作为一个数据集显示,则有两个类是有意义的。

这只是两个例子。关键是,如果创建新类,它应该是有用的。Java特别容易过度抽象化。


我该怎么处理其他

其他听起来像是一组既不是卡车也不是汽车的车辆。听起来你想捕捉所有其他车辆并将它们放在一个类中。为什么不使用Vehicle类?不要创建另一个从Vehicle派生但添加了零功能的类。同样的有用性测试也适用于此。

  • Vehicle是否足够有用以满足我对所有“其他”车辆的需求?如果不是,我需要创建更多的类。

我听从了您的建议并创建了两个有用的类。现在,我无法区分它们,而且我拒绝使用instanceof。我该怎么办?

无论您的类有多抽象化,我始终可以将其实例添加到List<Object>中,然后无法区分它们。您无法使设计完全安全。

让我换句话说 - 如果您将Eggs和Cars添加到列表中,并且稍后需要区分Eggs和Cars,则列表存在问题,而不是Eggs和Cars。


我认为这是我对这个问题的首选答案,但它仍然没有解决在重型卡车和轻型卡车共享同一类别时区分它们的最佳方法,以及当它们不共享同一类别时与乘用车的区别。 - Risser
@Risser 编辑了答案以解决您关于“其他”的问题。同时回复了您的评论。 - Rainbolt
在代码中,我不需要太过区分它们。我可以将它们全部视为车辆,或者使用访问者模式或策略模式来帮助区分。但是,在这种情况下,我需要向用户显示类型,并可能根据类型选择关键的UI选项。此外,用户可以选择类型。经过深思熟虑,我认为枚举类型字段是解决该问题的方法。如果您使用工厂从枚举创建适当的车辆子类型,则可以减少创建车辆类类型不匹配的能力。 - Risser

2

对于你的问题,没有简单的答案。因为这取决于您将使用该数据结构执行的业务逻辑。

我会从一个接口 Vehicle 开始,它将解析标识符以获取具体代表的描述。

interface VehicleIdentyficator {
  String identyficator();
}

interface Vehicle {

  Identyficator getIdentyficator();

}

使用这种方法,您不会受到类、枚举或其他设计类型的限制。
然后,我将使用类层次结构来描述每辆车的属性。
接下来,添加一些逻辑/行为方面的代码,并确保我的数据结构对任务有效。
最重要的是执行一些操作。例如,创建三个类Truck、LightTruck和HavyTruck没有意义。
您可以创建一个根据它们执行的任务来描述它们的类。
它们可以在地面上运输。轻型卡车可以承载较少的负载,但用于在小城市旅行。重型卡车与轻型卡车相反。具有较大的范围和承载更多的负载。
当我们将其与船进行比较时,我们发现船更像轻型卡车,唯一的区别是它在水上行驶。
因此,尝试理解对象不是它是什么,而是它能做什么。可以执行哪些任务以及有哪些限制。然后,您的设计将非常接近您所需的内容。
希望这句话是正确的,我会再说一遍。看起来您正在使用这些车辆,而不是构建它们。因此,专注于实现它们的任务,而不是它们的物理方面。
但是您可以做类似这样的事情。
 enum VechicleType implements VehicleIdentyficator {
   TRUCK("TRUCK"),
   HEAVY_TRUCK("HEAVY_TRUCK")

   private final String indentyficator;

   private VechicleType(String indetyficator) {
     this.indentyficator = identyficator;
   }

   public String identyficator() {
       return indentyficator; 
   }

 }

但是,每当您添加新的车辆时,您必须覆盖所有使用该新硬编码类型的点。


0
在面向对象开发中处理类型指示器的最佳方法是不要有类型指示器。类型指示器是结构化编程(认为早于面向对象编程)的概念。在面向对象编程中,类型是通过类型而不是人工指示器来识别的。
编辑:
如果您必须用对象表示一组固定领域的事物(例如:拖拉机、快艇、划艇、摩托车、马自达626、皮卡车、两栖卡车),那么您可以(也许应该)使用具体类来表示每个事物。有趣的设计问题将基于您需要对对象执行什么操作。
如果您只需要知道“这是一种水上交通工具”或“这有多少个轮子”,则使用带有isWaterVehicle()、getWheelCount()、isTruck()方法的接口。在您的对象上实现接口并硬编码答案。例如:
class SpeedBoat implements VehicleInfo
{
    public int getWheelCount()
    {
        return 0;
    }

    public boolean isTruck()
    {
       return false;
    }

    public boolean isWaterVehicle()
    {
       return true;
    }
}

如果您有大量相似的对象,请使用抽象类来设置默认值。
abstract class AbstractTruck implements VehicleInfo
{
    public final int isTruck()
    {
        return true;
    }
}

你能举个例子详细说明一下吗? - Damian Leszczyński - Vash
我同意。这是“传统”的解决方案,但它并不一定适用于“其他”概念,并且如果我需要知道它的类型以在屏幕或表单上显示,我将不得不执行某种instanceOf操作并将其转换为用户友好的内容。 - Risser

0

正如其他人所说,我认为这取决于您将如何使用您的车辆。

  • 如果您只需要在某些点上区分类型,但大多数情况下以相同的方式处理所有车辆,请选择枚举类型。

  • 如果您确实需要指定不同的行为,并且利用共享的特性/行为,请选择完整的类层次结构。这也是我的首选方法,因为我觉得它提供了更多的进化空间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接