views:

134

answers:

3

I'm having trouble deciding on a way to model this type of relationship...

All bosses can do certain things and have certain things (velocities, health, etc.) so these are part of the "main" abstract boss class.

class Boss // An abstract base class
{
    //Stuff that all Bosses can do/have and pure virtual functions
};

Now I want to specify a few more pure virtual functions and members for bosses that can shoot. I'm wondering how I should model this? I've considered deriving a ShootingBoss Class from the Boss class, but specific bosses are classes in themselves (with Boss just being an abstract base class that they are derived from.) Thus if ShootingBoss is derived from Boss, and a specific boss derives from ShootingBoss, that boss won't be able to access the protected data in the Boss class.

Boss(ABC) -> ShootingBoss(ABC) -> SomeSpecificBoss(can't access protected data from Boss?)

Basically, I'm wondering what the recommended way to model this is. Any help is appreciated. If more information is needed, I'd be happy to offer.

+1  A: 

Why not start using interfaces? So, rather than simply uber base class, you spread out your things into capabilities.

struct IBoss : public IObject
{
}
struct ICanShoot : public IObject
{
}

Generally to implement this you derive your interfaces from another interface which allows you to query for an interface.

struct IObject
{
 int getId(); // returns a unique ID for this interface.
 int addRef();
 int release();
 bool queryInterface(int id, void** pp);
}

That way, you implement your Boss more easily:

class Boss : public IBoss, public ICanShoot
{
};

It might be overkill for some, but if your class heirachy is all screwed up, this is the best way out of the mess.

Have a look at M$'s IUnknown interface.

cmroanirgo
The child classes of IObject should declare the parent as virtual. Otherwise multiple inheritance won't be possible. This is going to get really complex if the need arises to use a non default constructor with IObject
Gayan
+2  A: 

I think you need to look into Mixin classes.

For example, you could create the following classes:

class Boss {
    // Here you will include all (pure virtual) methods which are common
    // to all bosses, and all bosses MUST implement.
};

class Shooter {
    // This is a mixin class which defines shooting capabilities
    // Here you will include all (pure virtual) methods which are common
    // to all shooters, and all shooters MUST implement.
};

class ShootingBoss : public Boss, public Shooter
{
    // A boss who shoots!
    // This is where you implement the correct behaviour.
};

Mixins require multiple inheritance to be used, and there are many pitfalls and complexities to doing so. I suggest you look at answers to questions like this one to ensure that you avoid these pitfalls.

LeopardSkinPillBoxHat
Thanks, I'm not sure if this will work with what I'm trying to do, but useful advice nonetheless.
Anonymous
+1  A: 

There are two different ways of doing this:
1) Mixin classes (already explained)
2) Role playing classes.
Role playing has it's advantages and disadvantages. Roles, that object can play (boss, shooter, whatever) are implemented using containment. They must be derived from the common base interface class, which will have to be downcasted dynamicaly (argh..). Caller will ask object of your class for the role pointer (this is where downcast will come in) and if object can play the role (returned non-NULL pointer) client will call appropriate function of the role. Main advantage of role playing approach (appart from avoiding multiple inheritance) - it is dynamic. Object can accept new roles at runtime, as opposed to mixin that has to be defined at compile time.
Also it is scalable. In multiple inheritance (mixin) approach if you decide to expand your hierarchy with "Protector" and say that boss can be simple Boss, ShootingBoss, ProtectingBoss, ShootingProtectingBoss, and later expand it ufrther with Сoward (Boss, ShootingBoss, ProtectingBoss, ShootingProtectingBoss, CowardBoss, CowardShootingBoss, CowardProtectingBoss, CowardShootingProtectingBoss) - you see your hierarchy explodes. This is when you need to switch to role playing model, where object will simply have to accept new role Coward. But until you are sure that you need it - stick with mixin classes. Below is hierarchy sketch for role playing lcases:

class IRole
{
 // some very basic common interface here
public:
 virtual ~IRole() {}
};

class IBoss : public IRole
{
};

class IShooter : public IRole
{
};

class IProtector : public IRole
{
};

class MultifunctionalPerson
{
public:
 bool AcceptRole(IRole* pRole); // pass some concrete role here
 IRole* GetRole(int roleID);
};


// your clinet will be using it like that
MultifunctionalPerson& clPerson = ... (coming from somewhere);

// dynamic_cast below may be replaced with static_cast if you are sure
// that role at PROTECTOR_ROLE location is guaranteed to be of IProtector type or NULL
IProtector* pProtector = dynamic_cast<IProtector*>(clPerson.GetRole(PROTECTOR_ROLE));
if( 0 != pProtector )
{
 pProtector->DoSomething();
}
BostonLogan