是否使用has-a(组合)或is-a(继承)来建模汽车对象(及其部件,如引擎)?

时间:2009-12-16 07:48:16

标签: oop inheritance design-patterns composition car-analogy

我正在开发一个包含Car对象的类库。

困境在于,汽车本身将是一个包含注册号等字段的类,以及有关汽车的其他一般信息。

但是汽车有发动机,底盘等。这些物体也需要建模。它们应该是嵌入汽车的课程吗?如果没有,嵌入式类的使用场景是什么?

我已经知道组合是“的一部分”,所以你可以模拟单独的类并使用引擎类型,例如,在汽车的现场级别实现这一点。然而,“聚合”,与ctor中传递的类型“有”关系,也适用(汽车“有”引擎)。

我该走哪条路?

编辑:我目前正在做作业因此缺乏回复。该类库适用于基于汽车的Web应用程序。我是一名专业开发人员(我以.NET开发为生,但作为一名大三学生)所以这不是一个功课问题。

由于

7 个答案:

答案 0 :(得分:4)

这实际上取决于你的申请。

例如,您可以将轮子作为单独的类来实现,包含有关轮胎在哪个轮胎上的信息,磨损程度等等。但是如果您的应用程序甚至不关心轮子那么整个课程都是浪费代码。

我可以看到三个组合用例:

  • 拥有班级过于复杂,应该分解。
  • 拥有类具有一组可以映射到类的属性的多个副本。这允许您将所有这些属性绑定在一起。
  • 可能需要与拥有它的对象分开检查或考虑所包含的对象(例如,您可能希望将Engine对象移动到另一辆车),或者可以将其替换为单个单元。

总结:使用合成作为封装复杂性或消除重复的工具。如果它不能用于其中一个目的,那么可能不值得为它做一个新的课程。

答案 1 :(得分:4)

一个类应该尽可能少地负责,并将其他功能封装并委托给其他类。许多做一件事的小而简单的类是可读,稳定的代码库的标志。

是的,一辆车将“拥有”一个引擎,但我建议使用一个接口,类似的“有”关系。再一次,根据教授的不同,你可能会因为工厂制造不同的汽车而获得奖励积分(适当的,没有?):

public class Car
{
    private Engine engine;
    public Car(Engine engine)
    {
        this.engine = engine;
    }

    public void accelerate()
    {
        this.engine.goFaster();
    }

    public void decelerate()
    {
        this.engine.goSlower();
    }

}

public interface Engine
{
    public void goFaster();
    public void goSlower();
}


public class ReallyFastEngine implements Engine
{
    public void goFaster()
    {
    // some code that goes really fast
    }
    public void goSlower()
    {
    // some code that goes slower
    }    
}

public class NotAsFastEngine implements Engine
{
    public void goFaster()
    {
    // some code that goes not as fast
    }
    public void goSlower()
    {
    // some code that goes slower
    }    
}

public class CarFactory()
{
    public static Car createFastCar()
    {
         return new Car(new ReallyFastEngine());
    }

    public static Car createNotAsFastCar()
    {
         return new Car(new NotAsFastEngine());
    }
}

答案 2 :(得分:2)

看到它是家庭作业,并且根据您的导师/教授/老师的倾向,您可能最好沿着为引擎,车轮等编写单独的课程的路线。即使它可能完全过度设计,并且您的应用程序可能不关心它们,但您的作业可能会标记为以下标准:

“他们确定了引擎类”

“它是否有合理的方法,如Start()”

“将它们标记为将所有内容集中在一个实际上更简单的大类中,因为它们显然不理解组合”

或者其他什么,而不是这个线程中更务实的人适用于他们自己的设计的那种标准。

答案 3 :(得分:2)

仅将汽车的型号分解为将作为汽车范围之外的独立实体暴露的碎片。考虑它的另一种方法是,当你转动钥匙时,你真的了解你的汽车是如何开始的吗?就典型的驾驶员而言,引擎盖下的一切都是一个大而且嘈杂的黑匣子。汽车工程师知道汽车所有者需要维护的常见部件,并明确设计了不同级别的用户互动,例如油尺或冷却液储液器补充盖。

你能为汽车的每一块造型吗?当然。对各个火花塞进行建模是否有帮助?可能不是。

您是否需要具有不同属性(如颜色或尺寸)的汽车?您是否需要具有乘客或牵引能力等不同能力的汽车?一个不同的地方是你需要不同行为的汽车。这是你真正需要考虑对具有属性的Driver对象进行建模的地方,从简单的反应时间到复杂的反应时间。

将车辆建模为面向对象或继承的示例是有问题的,因为这些示例并未真正解释定义类的基本属性之间的真正区别。这对StackOverflow来说并不陌生,但这个问题也不重复,see this SO thread。我与我的一个朋友进行了同样的讨论并发布了log of it on my blog。阅读FAA认可的不同飞机类型以及每种类型的规定如何细分。有许多不同类型的飞机,最大的分离是动力和无动力之间。

查看definitions used by the FAA

  

飞机表示使用的设备   或打算用于飞行   空气。

     

飞机表示由引擎驱动   固定翼飞机比空气重,   这是由飞行支持   空气的动态反应   它的翅膀。

     

飞艇意味着发动机驱动   比空气轻的飞机   转向。

还有比空气轻且比空气重的空气。热气球无动力且比空气轻。飞艇是动力的,比空气轻。滑翔机无动力且比空气重。波音757的动力比空气重,但增加了另一类“固定翼”,不像直升机,它也是动力的,比空气重,但是是“旋转翼”。

以下是表格中的前四个:

                 |  Powered   |    Unpowered
---------------------------------------------------
Lighter-than-air |  Blimp     |    Hot-air balloon 
Heavier-than-air |  737       |    Glider

你明白了。

你不能只说你将发动机与汽车分开建模,因为没有发动机的汽车可能是完全不同的动物。没有发动机的汽车就像拖车一样,也没有发动机,但也没有发动机。在这些情况下,'is-a'和'has-a'都不适合我们构建对象的具体方式。你没有宣称飞艇是一架“比空气更轻”的飞机,所以它是一个热气球。除了他们利用的物理学之外,它们都比空气更轻的事实并没有使它们以任何方式相关。区别很重要,因为适用的规则和规定是不同的。从另一个角度来看,我们并没有将飞艇描述为具有“发动机”的热气球。这架飞机没有物理关系,关系就是应该如何处理。

如果您不需要将对象定义到该详细程度,则可能不需要将它们建模到该详细程度。

答案 4 :(得分:0)

Car将成为顶级层次结构对象。包括数字,ID或描述等简单字段。 并且会有像Engine这样的复杂字段,它本身就是一个对象。

所以汽车看起来像是:

class Car{
     String ID;
     Engine engine;
}

这是一种关系。

答案 5 :(得分:0)

您可以决定引擎,Chasis等类的一个标准 需要作为内部类(嵌入类)存在是否是实例 这些类可以在您的应用程序的其他地方使用。在这种情况下 决定很简单,就是让这些课程分开存在 (不是内部类)。

即使这些类没有在你的应用程序的其他地方使用,那么其他的 标准可以是可测试性。将这些类嵌入内部并与您一起使用 设计是否可以进行单元测试,以便对您进行适当的测试 代码提供良好的覆盖范围。

例如,如果您创建了一个引用
的实例变量 引擎对象和此变量正在Car的构造函数中初始化 您的Engine类有一些需要测试的方法。那怎么可以 你添加单元测试来检查Engine类中的代码?可能你会 在Car类中有一些暴露行为或Engine类允许的方法 你要编写单元测试。那么问题是是否需要曝光 Engine类的行为不会比Engine类更好 它自己站着?

或者,可能不需要明确地测试方法中的方法 Car中引擎类和单元测试方法涵盖Engine类代码
同样。然后它反映了Engine类与Car类的紧密集成 并且意味着它可以保持为内在阶级。

答案 6 :(得分:0)

这取决于你想要做什么。试图在不知道用例的情况下设计一个“汽车”类(或任何其他类)是徒劳的。

您将根据您尝试启用的用例,以非常不同的方式设计类及其关系和交互。