2

Consider an abstract class called Vehicle. This vehicle has following abstract operations
- Start - Break - Accelerate

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

Now consider that we have a special kind of vehicle derived from Vehicle class namely VehicleA.

Class VehicleA: public Vehicle
{
   private: 
     double speed;
     double temperature;
     int id;      
     double miles;

   public: 
     void Start();
     void Break();
     void Accelerate();
     void horn();
};

If I now have a vehicle which is almost similar to VehicleA type but slightly differs in say the type of engine or some other characteristics like color, which is the best way to accommodate such a small change in the class hierarchy design. Should I

  • define another class VehicleB derived from Vehicle class ? or

  • define another class VehicleB derived from VehicleA ? or

  • something else ?

Thanks

Geert Bellekens
  • 12,788
  • 2
  • 23
  • 50
nurabha
  • 1,152
  • 3
  • 18
  • 42
  • 4
    Make an engine class and have engine as a member of VehicleA. Now you can change the engine without making a new class. – andre Jun 05 '13 at 15:34
  • @andre Can't I just create engine type as Enum and then use it inside VehicleA class to accomodate vehicles with slightly different engines ? My question is how do I decide whether engine should be another class or an enum etc. ? – nurabha Jun 05 '13 at 15:51
  • @nurabha: why not have an engine class with the needed parameters, so when you need information about the engine, you can get it straight away instead of going via an enum and probably a switch-case? – stefaanv Jun 05 '13 at 15:54
  • 1
    Do different engines provide different functionality? If yes, you need classes, if not enums are fine. – Marc Claesen Jun 05 '13 at 15:55

3 Answers3

2

In this case, you really should consider composition over inheritance.

Depending on what the class Vehicle actually means to you (and be careful with that, intuition isn't necessarily your best friend when going through this kind of class design : think about the famous Square/Rectangle case), you could have your VehicleA declared the following way (don't forget the virtual keyword):

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

with Engine and Color two classes (that can be abstract or not) that hold the details you want to implement.

You add another level of abstraction : your VehicleA has an engine (with its own interface), but don't care about its details (as long as the engine has an interface the vehicle can interact with), and makes it possible to add easily a new type of engine.

As a general rule when designing a hierarchy, if you think you must implement a new derived class of a specific class, ask yourself the following :

Is this class a more specific version of its parent class ?

In your case, it feels like a VehicleB wouldn't be a more specific version of a VehicleA though that's still a matter of opinion, as it completely depends on what you want to do. In this case, it feels like the way to go should be composition.

JBL
  • 12,588
  • 4
  • 53
  • 84
1

What you have here is a problem related to "Separation of Concerns". The "Vehicle" concept has a few basic operations some of which you identify, for example "Accelerate". Now the implementation of "Accelerate" is dependent on certain parameters, such as maximum torque, brake-horsepower etc...

These should should be encapsulated outside of the vehicle... but why? Well because they the Vehicle represents a concept, not an implementation. Accelerating will use an engine in the same manner, no matter what that type of car involved. let me use a real-world example: A McClaren F1 is a Vehicle, in fact it is a car, which contains an engine, has a chassis, has some tyres and suspension etc... A Volkswagon Golf GTI is a Vehicle, in fact it is a car, which contains an engine, has a chassis, has some tyres and suspension etc...

The user will drive one car in the exact same manner as another car, even if it has hugely different sets of component parts. The user does not need to even be aware of most of the details. This is why you need to separate out your Vehicle concept from the implementation details that are encapsulated by the specific components of your Vehicle. You should do the same for your "Brakes" as well, and you should inject the Engine and Brakes into the Vehicle at construction (look up Dependency Injection).

Now for colour: I would recommend that you place this at the top level of your class hierarchy, in the Vehicle abstract class. It is something that applies to all classes of vehicles, and is used in the same way by all, and does not affect any implementation. It should be set via the constructor probably, with a repaint function offered for changing it (once the necessary fees are passed to the Garage via the SalesPoint of course!).

So the class in the end might look like this...

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

Using it:

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

It uses the components via their interfaces, so it doesn't need to know specifics. The sub-classes of Vehicle then do not need to worry about anything that is not Vehicle specific. You will only need a subclass of Vehicle if there is a vehicle that does not fit your generic concept a vehicle (for example if there is a vehicle out there with no brakes).

Dennis
  • 3,683
  • 1
  • 21
  • 43
0

How to handle classes that are slightly different?

This depends completely on what you are trying to solve. The Vehicle class isn't a real car, it is a model based on information of the real world, needed to make a working program. It is not one set of fixed rules.

About the color: this has nothing to do with the behavior of the class, so if possible ignore it, if not, make an extra field.

About the type of engine: does this make a noticeable difference to the behavior or is it just a matter of setting some parameters (power, couple, fuel consumption)? In case of the engine, there is a good chance that you can have a hierarchy of engines that can be contained in the vehical.

stefaanv
  • 14,072
  • 2
  • 31
  • 53
  • Lets say type of engine has characteristics of horse power, rpm, mileage, cc, number of gears etc. which influences characteristics of vehicle such as max acceleration, max speed when it breaks, accelerates,etc. I agree about color that it has no behavior and can be accomodated as a field. – nurabha Jun 05 '13 at 17:53
  • If you keep it too close to the real world, you make it too complicated. That's usually the problem with OO examples like animals or vehicles. The focus should remain on what added value your program should give. – stefaanv Jun 05 '13 at 18:05