类的类设计几乎没有什么不同

Class Design for Classes with few differences

本文关键字:没有什么不同      更新时间:2023-10-16

考虑一个名为Vehicle的抽象类。该车辆具有以下抽象操作
- - - - - -开始- - - - - -休息——加快

class Vehicle
{
  public:
     virtual void Start() = 0;
     virtual void Break() = 0;
     virtual void Accelerate() = 0;
};

现在考虑从vehicle类派生出一种特殊类型的车辆,即vehicle - lea。

Class VehicleA: public Vehicle
{
   private: 
     double speed;
     double temperature;
     int id;      
     double miles;
   public: 
     void Start();
     void Break();
     void Accelerate();
     void horn();
};

如果我现在有一辆几乎与vehicle - lea类型相似的车辆,但在引擎类型或其他一些特征(如颜色)上略有不同,这是适应类层次设计中如此小的变化的最佳方式。我应该

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

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

谢谢

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

根据类Vehicle对你的实际意义(注意,在进行这种类设计时,直觉不一定是你最好的朋友:想想著名的Square/Rectangle案例),你可以用以下方式声明你的vehicle - lea(不要忘记虚拟关键字):

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有一个引擎(有自己的接口),但不关心它的细节(只要引擎有一个车辆可以交互的接口),并且可以很容易地添加一个新的类型的引擎。

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

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

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

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

这些应该封装在车外…但是为什么呢?因为运载工具代表的是一个概念,而不是一个实现。加速将以同样的方式使用引擎,无论涉及哪种类型的汽车。让我举一个现实世界的例子:迈凯轮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();

它通过组件的接口使用组件,所以它不需要知道细节。然后,Vehicle的子类不需要担心任何与Vehicle无关的东西。只有当有一辆车不符合你的通用概念时(例如,如果有一辆车没有刹车),你才需要Vehicle的子类。

如何处理略有不同的类?

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

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

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