I’ve come across a lot of cases lately where programmers have made poor use of inheritance. A common situation I’ve seen are classes inheriting from other classes to gain some small set of functionality which results in a lot of special case code in both the child and the parent to deal with their relationship. The relationship between the classes may have started off well, but over time and many iterations by different programmers the relationship has become obfuscated. As a result it’s hard for yet another programmer to go in and make improvements. I guess you could say this situation is a lot like a kid that’s all grown up and needs to move out of the parent’s house: things may have started out well, but now the kid is different and the relationship is becoming disfunctional.
You can’t place the blame for the development of this situation on an individual programmer or a poor initial design decision. Usually, these sorts of relationships made a lot of sense at one time, and they probably continued to make sense for a quite a while. It’s only after many last-minute iterations or a few late night frantic programming sessions do these things start to appear. You might be saying, “well I would never let that happen,” or “if I saw something like that I’d fix it,” and that may be good and noble of you to say, but wouldn’t it be better if things were designed from the beginning to avoid it from ever happening?
This problem is especially apparent in game programming. The classic example is in game objects. Say you have a cHuman class and a cVehicle class. These both share some common functionality: they are both “moveable” and they are both game objects. The inheritance hierarchy might look something like this:
class cMoveable : public cGameObject…
class cVehicle : public cMoveable…
class cHuman : public cMoveable…
OK, now what happens when you want to add physics to the game? Does that go in cMoveable, or do you make a new class that handles the physics? Does cHuman get the physics functionality too?
What if you want to add a crate class, and this crate is not “moveable” by the player, but it is deformable (destroyable) by the physics system? Where would that go?
These are simple examples, but you can see how things may get quickly out of hand. Following this inheritance design after a few years of development you’ll end up with a big mess.
One alternative to a tangled inheritance web is to build classes by composition. Instead of having a cVehicle inherit from a cMoveable, have cVehicle own a cMoveable. cMoveable can then be made into the smallest set of functionality necessary. If you later need to add physics, have cVehicle own a cPhysics too. How does cPhysics communicate with cVehicle? cVehicle requests the interface cPhysics interface from it’s owner.
By compositing objects together through “ownership” relationships rather than “parent/child” relationships you can also build your classes with very strict interfaces. This is where the “component-oriented” programming comes in: cVehicle may not even know it owns a cPhysics–it may only have a reference to componenent interface (cComponent, perhaps). Each of these component classes can provide a standard interface that allows their owners, their owned components, and other objects to query for and request component interfaces from them, without the class itself having to know anything of the details.
The best part about compositing objects using a component-oriented approach is it allows you to completely data drive the object building process. You could have an XML, Lua script, or some other asset format instruct the engine how to build game objects. This puts game object development into the hands of game designers (where it should be), and puts the programmer in charge of developing the right repitiore of components.
I found these references useful while exploring component-oriented approaches to game object design…
Bjarne Rene, “Component Based Object Management,” Game Programming Gems 5
Chris Stoy, “Game Object Component System,” Game Programming Gems 6
Clemens Szyperski, Component Software: Beyond Object-Oriented Programming, Addison-Wesley 2003
Scott Bilas, A Data-Driven Game Object System, GDC 2002
The first chapter of Programming .NET Components lays out the case for component-oriented programming. It’s a great place to start, (and you can read it online)!