几乎没有差异的类的类设计

时间:2013-06-05 15:29:15

标签: c++ oop design-patterns

考虑一个名为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?或

  • 别的什么?

由于

3 个答案:

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

使用EngineColor两个类(可以是抽象的或不抽象的)来保存您要实现的详细信息。

你添加了另一个抽象级别:你的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类不是真正的汽车,它是基于现实世界信息的模型,是制作工作程序所必需的。它不是一套固定的规则。

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

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