2

考虑一个名为 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 类型几乎相似但在引擎类型或颜色等其他一些特性方面略有不同的车辆,这是适应类层次结构设计中如此小的变化的最佳方式。我是不是该

  • 定义另一个类 VehicleB 派生自 Vehicle 类?或者

  • 定义另一个派生自 VehicleA 的类 VehicleB ?或者

  • 别的东西?

谢谢

4

3 回答 3

2

在这种情况下,您确实应该考虑组合而不是继承。

根据类Vehicle对您的实际意义(请注意,在进行此类设计时,直觉不一定是您最好的朋友:想想著名的Square/Rectangle案例),您可以让您的 VehicleA 声明为以下方式(不要忘记 virtual 关键字):

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();
};

withEngineColor两个类(可以是抽象的,也可以不是抽象的),它们包含您要实现的细节。

您添加了另一层抽象:您VehicleA有一个引擎(有自己的接口),但不关心它的细节(只要引擎有一个车辆可以与之交互的接口),并且可以轻松添加新型发动机。

作为设计层次结构时的一般规则,如果您认为必须实现特定类的新派生类,请问自己以下问题:

这个类是它的父类的更具体的版本吗?

在您的情况下,感觉 VehicleB 不会是 VehicleA 的更具体版本,尽管这仍然是一个见仁见智的问题,因为它完全取决于您想要做什么。在这种情况下,感觉应该走的路应该是组合。

于 2013-06-05T15:55:53.077 回答
1

您在这里遇到的是与“关注点分离”相关的问题。“车辆”概念有一些基本操作,您可以识别其中一些,例如“加速”。现在“加速”的实现取决于某些参数,例如最大扭矩,制动马力等......

这些应该封装在车外……但为什么呢?好吧,因为他们的 Vehicle 代表了一个概念,而不是一个实现。无论涉及哪种类型的汽车,加速都将以相同的方式使用发动机。让我用一个真实的例子:麦克拉伦 F1 是一辆汽车,实际上它是一辆汽车,它包含一个引擎、一个底盘、一些轮胎和悬架等......大众高尔夫 GTI 是一辆汽车,在事实上它是一辆汽车,它包含一个发动机,有一个底盘,有一些轮胎和悬架等......

用户会以与另一辆车完全相同的方式驾驶一辆车,即使它具有截然不同的组件集。用户甚至不需要知道大部分细节。这就是为什么您需要将您的车辆概念与由您的车辆特定组件封装的实现细节分开的原因。你也应该为你的“刹车”做同样的事情,你应该在构造时将引擎和刹车注入车辆(查找依赖注入)。

现在来看看颜色:我建议你把它放在你的类层次结构的顶层,在 Vehicle 抽象类中。它适用于所有类型的车辆,并且所有人都以相同的方式使用,并且不影响任何实施。它可能应该通过构造函数进行设置,并repaint提供用于更改它的函数(当然,一旦必要的费用通过 SalesPoint 传递到车库!)。

所以最后的课程可能看起来像这样......

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();

它通过它们的接口使用组件,所以它不需要知道细节。Vehicle 的子类不需要担心任何不是 Vehicle 特定的东西。如果有车辆不符合您的通用概念车辆(例如,如果有一辆没有刹车的车辆),您将只需要车辆的子类。

于 2013-06-05T16:06:17.507 回答
0

如何处理略有不同的类?

这完全取决于您要解决的问题。该类Vehicle不是真正的汽车,它是基于现实世界信息的模型,需要制作工作程序。它不是一套固定的规则。

关于颜色:这与类的行为无关,所以如果可能的话忽略它,如果没有,做一个额外的字段。

关于发动机的类型:这是否会对行为产生显着影响,还是只是设置一些参数(功率、耦合、油耗)的问题?对于发动机,您很有可能拥有可以包含在车辆中的发动机层次结构。

于 2013-06-05T15:52:46.490 回答