如果我要为各种品牌的汽车建模,我会使用继承层次结构,还是只使用不同的构造函数参数?
是使用继承来关联对象还是仅通过重用同一个类来关联对象的一般规则是什么?
对于汽车,我可以只做类似 new Car("Porsche","991","3.8") 之类的事情,或者我可以有一个整体抽象的 Car 超类,其中包含像“Porsche”这样的抽象子类制造商,然后可能每个模型都有一个类保时捷?
如果您有一些由所有汽车共享的属性(或作用于对象的方法),然后是每个品牌/模型的唯一属性(或方法),那么您会想要使用继承。否则,只是不同的实例是可以的。
假设您想要所有汽车的这些属性:
在这种情况下,您不会想要创建类层次结构,因为它不会给您带来任何好处。
相反,如果您有两种“类型”的汽车:普通汽车和赛车,并且只有赛车可以启用一氧化二氮(可能是一种这样做的方法),那么您需要一个 Car 类,继承 RegularCar 和 RaceCar从中。
如果您只是害怕必须始终将相同的参数传递给您的构造函数,您可以创建代表您调用构造函数的静态方法。这被称为工厂方法模式。
PS:我的例子真的只是从我的脑海中浮现出来。但我希望你明白我想说什么:)
创建子类是关于管理复杂性并将您的问题拆分为更小、更简单的不同且不重叠的案例。如果您必须为所有汽车解决一些问题,并且您认为,这个问题对保时捷来说“特别”,因为他们有一个普通汽车没有的额外备用引擎,您可以创建类似的东西
PorscheCar : Car
{
Engine engine;
Engine backupEngine;
}
NonPorscheCar : Car
{
Engine singleEngine;
}
因此,在您决定创建一个可以解决问题的类之后,如果您在该问题中检测到多个案例,您可以并决定独立解决,您可以为每个案例创建一个子类。您总是可以在不使用子类的情况下解决所有问题。问题在于,如果在应该创建子类时不创建子类,我将很难正确管理和组织代码。
您必须小心选择每个对象或类解决的每个问题。如果您希望您的 Porsche 像Porsche和没有*的普通汽车一样打印,这并不意味着 Porsche “行为”不同,您需要创建一个特定的子类。实际表现不同的是您打印 Porsche 的方式,因此您应该创建如下内容:
Car
{
Brand brand;
}
CarPrinter
{
Car carToPrint;
static CreatePrinter(Car car)
{
return car.Brand() == PorscheBrand() ? new PorscheCarPrinter(car) : new DefaultCarPrinter(car);
}
}
PorscheCarPrinter
{
Print();
}
DefaultCarPrinter
{
Print();
}
所以分为两种情况的问题是打印汽车,而不是建模汽车本身。
以更抽象的方式,您应该创建一个类来对您领域的每个概念进行建模。当您检测到由该域的特定概念建模和解决的责任很复杂,并且您希望将其拆分为更小、不同且不重叠的案例时,您应该为每个较小的问题创建一个子类。
在您的特定情况下,我不知道您的全部问题,但是您不太可能需要为每个品牌的汽车创建子类,因为它们通常是一个单一的概念,只需解决一个案例。
Car
单类方法的问题在于,对于所有不同类型的s ,您都被相同的实例变量和方法所困扰。
例如,汽车人可能拥有transform()
大多数其他汽车所没有的方法。
我的意思是,理论上你可以transform()
为所有 s 定义一个方法,Car
并且让不支持它的汽车抛出异常。但这是一个比使用继承更混乱的设计。
继承还可以让你做一些花哨的事情,比如多态性。例如,每辆车可能都有一个parallelPark()
方法。但更高档的汽车可能具有与所有其他汽车不同的自动平行停车机制。在这种情况下,您只需覆盖该parallelPark()
方法,Java 就会选择正确的方法。
我想说这取决于有多少逻辑取决于制造商和型号等属性。如果没有,那么这样做是有意义的new Car("Porsche","991","3.8")
,因为制造商和型号只是属性。但是,如果制造商和模型定义了一组其他属性或逻辑,那么类可能是有意义的。这样,您不必new Car("Porsche","991","3.8", gearingRatios, topSpeed, price, etc)
每次想要保时捷 991 时都通过,您只需定义一个Porsche991
定义所有这些属性的类。
或者,更多地扩展它的逻辑部分,如果Porsche
和Volkswagen
汽车在方法中会有非常不同的逻辑Accelerate()
,那么你可以使用继承,而不是Accelerate()
使用不可扩展的开关/案例方法。或者如果Porsche
s 需要一个Race()
方法,而Volkswagen
s 不需要,等等。
如果Car
不知道您打算如何使用Car
.
对于像汽车这样的超类,更合适的子类是双门轿车、轿车和敞篷车。汽车型号应该是一个参数。
看看类型对象模式 (pdf)。