Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

We use inheritance when A (derived class) "is a" B (base class). What do we do when A "can be" B or C?

Sorry for this ugly question, but I didn't know how to word it. I'll give an example of what I mean:

A Human can be a Mage or a Warrior, so Mage and Warrior could inherit from Human. But what if Orc can be a both too? We can't say "a Human is a Warrior" or "a Warrior is a Human". Do Orc and Human (or a parent class, Humanoid) inherit all the skills, and then choose what to use?

I don't know if I should tag a specific language, since it's a general question about oop, but since different languages can have different approaches to the same problem, I would prefer answers from a C++ perspective.

like image 344
Ciscool Avatar asked Jun 23 '14 22:06

Ciscool


2 Answers

Improve your modelling

  1. Abstract class Race, concrete classes Human, Orc, etc...
  2. Abstract class Class, concrete classes Mage, Warrior, etc...

When calculating the stats for your Mage, for instance, ask Race (not Human, or Orc) for things like int get_intelligence_bonus() (abstract function in Race). If at all possible, interactions should be between Race and Class, not the concrete counterparts.

Use composition to actually construct your character:

Character::Character( std::unique_ptr<Race> r, std::unique_ptr<Class> c ) 
{
  // calculate stats, etc...
}

You can use pure OOP for dynamic binding or if you prefer, you can use templates for static-time binding, but that's an orthogonal implementation issue. Either way, you need to get your class hierarchy right.

If you are new to C++ or if you only need race and class in the constructor, you may use const& instead of std::unique_ptr<>. But if you are serious about learning C++, do read about ownership semantics and variable lifetimes so you can better understand std::unique_ptr<>.

http://en.cppreference.com/w/cpp/memory/unique_ptr

like image 53
kfmfe04 Avatar answered Nov 15 '22 09:11

kfmfe04


That's when I break out policy-based design. Then I can have Human<Warrior> or Orc<Warrior> or Orc<Mage>. (Or turn it around if it makes more sense: Warrior<Human> etc.) But that's if I know what's going on at compile time.

If I don't know until run time, I'd use a class Humanoid which has a Race (subclasses would be Orc, Human, etc.) and has a RPGClass (subclasses Mage, Warrior, etc.)

In other languages, there are things such as protocols which can define the interface to an object, so that you don't have to define a base class full of abstract virtual methods and such. So RPGClass and Race would be protocols used to interface to classes such as Mage and Human respectively. Policies are just protocols resolved at compile time.

P.S. I have no idea how metaphorical (or not) your examples are...

like image 23
Mike DeSimone Avatar answered Nov 15 '22 07:11

Mike DeSimone