考虑一个名为Vehicle的抽象类。该车辆具有以下抽象操作
- 开始
- 休息
- 加速
class Vehicle
{
public:
virtual void Start() = 0;
virtual void Break() = 0;
virtual void Accelerate() = 0;
};
现在考虑我们有一种来自Vehicle类的特殊车辆,即VehicleA。
Class VehicleA: public Vehicle
{
private:
double speed;
double temperature;
int id;
double miles;
public:
void Start();
void Break();
void Accelerate();
void horn();
};
如果我现在拥有的车辆几乎与VehicleA类型相似,但引擎类型或颜色等其他特性略有不同,这是适应类层次设计中这么小变化的最佳方式。我应该
定义从Vehicle类派生的另一个类VehicleB?或
定义从VehicleA派生的另一个类VehicleB?或
别的什么?
由于
答案 0 :(得分:2)
在这种情况下,你真的应该考虑组合而不是继承。
取决于班级Vehicle
对你的实际意义(并且要小心),在进行这种课堂设计时,直觉不一定是你最好的朋友:想想着名的Square/Rectangle case),你可以通过以下方式声明你的VehicleA(不要忘记虚拟关键字):
class VehicleA: public Vehicle
{
private:
//Your specific private
Engine* m_engine;
Color* m_color;
//Add any "modifiable" part as long as it fits
public:
virtual void Start();
virtual void Break();
virtual void Accelerate();
void horn();
};
使用Engine
和Color
两个类(可以是抽象的或不抽象的)来保存您要实现的详细信息。
你添加了另一个抽象级别:你的VehicleA
有一个引擎(有自己的界面),但不关心它的细节(只要引擎有车辆可以与之交互的界面),并且可以轻松添加新型发动机。
作为设计层次结构的一般规则,如果您认为必须实现特定类的新派生类,请自问以下内容:
此类是否是其父类的更具体版本?
在您的情况下,感觉车辆B不会是更具体的版本的VehicleA ,尽管这仍然是一个意见问题,因为它完全取决于您想要做什么。在这种情况下,感觉就像走路一样应该是构图。
答案 1 :(得分:1)
您在这里遇到的是与“关注点分离”相关的问题。 “车辆”概念有一些基本操作,其中一些是您识别的,例如“加速”。现在“加速”的实施取决于某些参数,例如最大扭矩,制动马力等......
这些应该封装在车外...但为什么呢?好吧,因为它们代表了一个概念,而不是一个实现。无论涉及哪种类型的汽车,加速都将以相同的方式使用发动机。让我用一个真实世界的例子: 麦克拉伦F1是一辆车,实际上它是一辆汽车,它包含一个发动机,一个底盘,有一些轮胎和悬架等...... 一辆大众高尔夫GTI是一辆车,实际上它是一辆车,包含一个发动机,一个底盘,有一些轮胎和悬架等...
用户将以与另一辆汽车完全相同的方式驾驶一辆汽车,即使它具有完全不同的零部件组。用户甚至不需要知道大部分细节。这就是为什么您需要将Vehicle概念与由Vehicle的特定组件封装的实现细节分开的原因。 你也应该对你的“刹车”做同样的事情,你应该在施工时将发动机和制动器注入车辆(查找依赖注射)。
现在换颜色:我建议你把它放在你的类层次结构的顶层,在Vehicle抽象类中。它适用于所有类别的车辆,并且所有车辆都以相同的方式使用,并且不会影响任何实施。它应该通过构造函数设置,并提供一个repaint
函数来更改它(当然,必要的费用通过SalesPoint传递给Garage!)。
所以最后的课程可能看起来像......
class Vehicle
{
private:
std::unique_ptr<Engine> engine;
std::unique_ptr<Brake> brakes; // same for "Suspension", "Chassis" etc...
VehicleColour colour; // An enum defined here or elsewhere.
public:
Vehicle( std::unique_ptr<Engine> engine, std::unique_ptr<Brake> brakes, VehicleColour colour)
: this->engine(std::move(engine)),
this->brakes(std::move(brakes)),
this->colour(colour) {
}
virtual void Start(const Key& key) {
engine->ignition( key );
brakes->disengage();
}
virtual void Break( BreakPattern pattern ) {
engine->setThrottlePosition( NO_THROTTLE );
brakes->engage( pattern ); // e.g. SIMPLE_HARMONIC, or SLAM... brakes may have ABS or not but you don't need to know
}
virtual void Accelerate() {
brakes->disengage();
engine->setThrottlePosition( TO_THE_METAL );
}
};
使用它:
std::unique_ptr<Brake> absBrakes( new VwAbsDiskBrakes() );
std::unique_ptr<Engine> fastEngine( new TurboV8( FOUR_LITRE ) );
Vehicle hotrod( absBrakes, fastEngine, RED );
hotrod.start();
hotrod.accelerate();
它通过接口使用组件,因此不需要知道具体细节。然后,车辆的子类不需要担心任何非车辆特定的事物。如果车辆不符合您的通用概念车辆(例如,如果车辆没有制动器),您将只需要车辆的子类。
答案 2 :(得分:0)
如何处理略有不同的类?
这完全取决于您要解决的问题。 Vehicle
类不是真正的汽车,它是基于现实世界信息的模型,是制作工作程序所必需的。它不是一套固定的规则。
关于颜色:这与类的行为无关,所以如果可能的话,忽略它,如果没有,则创建一个额外的字段。
关于发动机的类型:这是否会对行为产生显着影响,还是只是设置一些参数(功率,耦合,油耗)?在发动机的情况下,您很可能拥有可包含在车辆中的引擎层次结构。