Archived

This topic is now archived and is closed to further replies.

General OO question

This topic is 5532 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

When is it better to se inheritance, and when is it better to use composition? I'm trying to design a RTS game, and have been trying to abstract the concept of a unit as much as possible. Here's what I've gotten down to. You can do it one of two ways. The first is using inheritance...define a very basic unit, and then subclass more specific examples. For example, if I have: class Unit { public: virtual move(); virtual attack(); setHealth(int); private: int health; Order* pOrder; } then to create a tank I could do the following: class Tank: protected Unit { public: move(); attack(); setHealth(int); private: int health; Order* pOrder; } Wherein the Tank due to its more specialized nature has a different method to move(), and attack(). If I wanted to, I could further subclass Tank into TrackedTank, or WheeledTank. But as I got to thinking, it seemed like it would be easier to use composition....like the famous car analogy. A car 'has-a' engine object, a wheel object, a suspension object, etc. This seems to be a bit more fluid actually. Instead of making the entire class itself more or less generalized, you make the specific characteristics of it more or less specialized. For example, I could define the base characteristics of ALL units like this:
  
////////////////////////////////////////////////////////////////

/*
  UNIT: this class will determine the characteristics of each individual unit within a specified Container class.
  It is actually the Container class that is given orders and executes functions that the unit is capable of.  The Unit
  must have a pointer to the Container class in which it belongs.  To avoid confusion between Unit as an individual 
  instance and a group of units, the term CLUSTER will be used for aggregates of individual units.
  *NOTE* it is very important to distinguish that the units' methods are what it GIVES the cluster for its functionality.
  In other words, the unit does not move(), the cluster does, the unit does not attack(), the cluster does.  Even detached 
  units are assigned temporary container class of ONE.  The container class reads the unit's methods and creates a pool of
  methods which it in turn can use.  The cluster itself only does what the command class object tells it to do, barring   its standing orders (self-preservation for example)
*/
///////////////////////////////////////////////////////////////


class Unit
{
public:
    Scriptfile* getScriptfile(Scriptfile UnitStats);  //this method returns pointer to Scriptfile and uses it to obtain unit stats by dereferencing

    
private:
    float size;              //the physical size of the 

    float cost;              //how expensive the unit is

    OUClass* uOU;            //this pointer points to which container class it belongs to

    
///////////////////////////////////////////////////////////////////unit CLASSES:  The following are all classes that are embedded in a unit Class.  They are composited into the definition of a 

//class much like a car has an engine object, wheel, object, etc.  all of these class are defined in the header file unit.h

    
    unitSilhouette uSH;      //class that determines how easily spottable the unit is

    unitEngine uET;          //what kind of engine does the unit use?  Fusion, MHDT, ICE?        

    unitMobility uMT;        //what kind of locomotion does the unit use?  Tracked, hover, wheeled, foot, VTOL. etc?    

    unitOffense uOC;         //Offensive systems onboard the unit.  Attack capabilities defined in the instance of this class       

    unitDefense uDC;         //Defensive capabilities defined in this class: armor level, deflection, reactive, PDS

    unitSensors uSC;         //capability for unit to sense other units.  Passive and Active rating

    unitMonitor uMC;         //this class finds and retrieves data about internal and external data. Examples, tracking, fuel, ammo

                             //morale for internal data, and tracking its location for external data


//////////////////////////////////////////////////////

  
**Note, this is an incomplete class declaration, it's not final, just something I came up with in a couple of minutes** Using a method like this, I can create a script that the player uses to determine the individual units characteristics. while I could do this with inheritance too, it seems much more cumbersome. Is there a way I could duplicate this with inheritance in a more elegant fashion? [edited by - Dauntless on October 16, 2002 8:17:56 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I would go with the inheritance there. It seems more logical to say that "a truck IS A unit" and not "a truck HAS something that knows how to move...". I think the code would be a little bit cleaner in the first case and you could still customize attributes through a script could you not?

Share this post


Link to post
Share on other sites
Check out www.BruceEckel.com for his c++ book that goes into these topics. When you want your derived class to expose base class interface to outside world then you want inheritance, otherwise you want composition or "has a". You can actually have three 1)"is a" 2)"is like a" 3)"has a"

The difference between "is a" and "is like a" is under "is like a" you extend the immediate base class by making new methods while under "is a" you don''t. Like when you want to override/overload base class methods only. Since your unit class exposes interface in its derived class the tank should inherit from unit. Tank does not have a unit but it "is a" unit or "is like a" unit but does more than a unit. You most likely want to use polymorphism so you can move a unit i.e a tank, airplane, soldier, etc. Later you can add more units to the program without recoding your unit manager layer since it uses pointers to base class to manipulate various specific units(thru virtual methods).

Share this post


Link to post
Share on other sites
Just a quick thought:

Tank is a unit.
Tank has an engine.

Marine is a unit.
Marine has a...nope.

Make sure you've had a good look at your possible unit types. Personally, I'd use inheritance, but then, I don't always use composition quite enough.

The Tyr project is here.


[edited by - OctDev on October 20, 2002 6:31:01 AM]

Share this post


Link to post
Share on other sites
OctDev-
I thought about the infantry problem with engines, but I figured I could just as easily have a "null" object. I''ve been toying with both types of object relationships and I can see the advantages and disavantages of each a little better now. But I''m still wondering which one I can use.

Actually, trying to figure out the essence of all possible concrete unit types, and then making them more and more abstract is what made me think about the composition method of OO design. Almost all vehicles, when you really get down to it, are very similar in essence. The major defining difference is really what mode of locomotion it uses. Infantry on the other hand are much different, and may wind up with a subclass of its own. I could, for example,just make the Unit class without the engine class as part of its declaration. Then I could subclass an VehicleUnit from that by adding an engine object as part of its declaration.

There is one thing though that''s still making me take a good look at inheritance methodology though....my need for a Container class and an Officer class. Both are related to units, and by a slightly different definition, are units as well (the definition of a unit being a orderable, playable entity that can be physically represented on the world map). These two classes however are radically different from the Unit class I proposed above. So I''m still on a quest to find out everything that everyone has in common to see if I can use an inheritance method....or the "is like a" method.

Share this post


Link to post
Share on other sites
A marine in this case has no engine, it has legs.

You could compose a unit from ist locomotion, behavoiur, weapons, looks and that kind of thinks. Basically you compose a unit from its properties.

Is this a good idea?

I mean, it would be quite easy to call Unit->Locomotor->Move() for example.

Share this post


Link to post
Share on other sites
quote:
Original post by Dauntless
When is it better to se inheritance

When there''s an IS-A relationship.
quote:

and when is it better to use composition?

Anytime that you can''t convincingly demonstrate IS-A. Composition should be the default choice.

Share this post


Link to post
Share on other sites
Unit should be an abstract base class encapsulating a common interface of all objects that inherit from unit. Look at it this way: each unit can move right? Who cares how they move. You know they can move so you put move() into common base class i.e. an interface. After that you will worry about each unit''s movement code i.e. implementation since tank moves differently than airplane or a soldier. You might even put common data members like position into abstract base class since all units need to know their position in the world. Make the move() pure virtual in the base so you can do this: Unit *pUnit = new Tank; pUnit->Move() and unit whether it''s a tank, soldier or airplane will move. Know the behavior of your objects but keep the "how" out in early design stage. This will create a virtual hierarchy that''s easy to extend whenever you think of new unit type, like a helicopter or motorcycle. You can even split this up into airborne or landmoving vehicles or units. Airborne and landmoving inherit from Unit interface then each landmoving derived type will override base class move() method. You can then create class engine as base class and make gasoline or diesel sub types then you include them as members into specific types. Like a tank has diesel engine but a truck has gasoline engine, etc. Engine should not inherit from Unit since engine doesn''t know how to move in the world, the tank is moving and the engine is attached to the tank. This works nicely in animation hierarchies.

Share this post


Link to post
Share on other sites
Sure way for "is a" versus "has a" is this:

Use "is a" if you need to expose the object''s interface to outside world. So you inherit in this case, thus base class interface is also visible in its derived class and other objects can access base interface by doing derived->base->DoSomething()

Use "has a" when the enclosing object doesn''t need to expose the enclosed object''s interface to outside world. Like in Tank->Move(); void Move(){engine.Start(), transmission.PutIntoFirstGear(), pedal.ToTheMetal();}

The Move() message encapsulates various steps that the tank must do in order to move. You wouldn''t want to call every startup function just to be able to move. Like tank->putkeyintokeyhole(); tank->turnthekey(); tank->moveintofirstgear(); tank->etc(); Also, what happens if you upgrade existing engine to superduper1000 type? You would have to change tank''s public interface and change lots of other object''s code that communicate with tank.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
ok I''ve tried it both ways and here is my conclusion: Inheritance is the easy way out. There is an IS-A relationship (a tank is a unit) so it will work. Composition far more flexible, you can mix and match better. You end up using subclassing too, but instead of sublclasses of Unit you end up with subclasses of Engine and Weapon.

Ok what I recommend: use inheritance and get your game done. Then make another game and use more composition in it.

Share this post


Link to post
Share on other sites