Composition allows you to specify very different kinds of relationships between classes. This is sometimes also called containment. If you’re building a racing game and have a car class and a wheel class, you wouldn’t want to say that cars are wheels but instead that cars have wheels. Proper use of composition will even allow you to substitute what kind of wheels your cars have. This can not only simplify your designs but make your code easier to test.
You want to make sure that inheritance is right for your situation before using it and this episode will give you another option to consider.
One reason to use composition is to hide certain methods on another class and just expose a limited set of functionality. Or maybe you want to be able to perform some extra validation.
You can also expose a collection of instances of the other class.
You can also use containment to simplify your class hierarchies and move what used to require extra classes into data. This allows you to extend your application at runtime and can be used to modify your program after it’s been written.
In the end, the choice is yours. There is no one right answer for all scenarios. Listen to the full episode or read the full transcript below.
When you declare a class that derives from another class, that relationship is fixed in code. This might be exactly what you need. But make sure that inheritance is right before you start using it. This episode will help you make that decision by explaining another option you have.
Composition allows you to reduce the number of classes you need and is often a good choice when you just want to reuse some code that’s in another class. Instead of inheriting from that other class with the useful code, you just declare a member variable of that type and use it whenever you need to.
Let’s take the armor example that’s been mentioned in previous episodes and assume for a moment that the armor class has all kinds of methods that you don’t really need but it does have some really good ones that you’d like to make use of. You want a simpleArmor class and public inheritance is out of the question because that would not only make your simpleArmor class just another armor type but it would also expose all the methods of the armor class that you don’t want.
You could use private inheritance if this is C++ but unless you need to override methods or gain access to protected members, I’d advise against this. If all you want is to expose just a few of the public members of the armor class in your simpleArmor class, then you can declare a private data member of type armor in your simpleArmor class. Then you just need to declare the methods you want to expose and in your method implementation, all you need to do is call into the embedded armor data member methods to do the actual work.
This approach keeps the fact that you’re using another class private. But sometimes, you want it to be widely known that you’re using other types inside of your class. The introduction to this episode described a racing game where a car class contains wheels. This approach allows you to expose not only that you’re using another class but that you actually have a specific number of these other classes if that’s what you want.
You have many options available to you when taking this approach.
Here are just 4.
- You could create a single wheel class, if all your wheels are the same and you just want to reuse the methods that the wheel class provides. You probably wouldn’t need to expose this publicly if you take this approach. As far as outside code is concerned, your car is able to move and it doesn’t care how.
- You could create several wheels as part of creating a new car instance and either expose this fact publicly or not. This allows you to treat each wheel separately and may be good if your game allows flat tires.
- You could allow other code to provide instances of the wheels when creating a new car instance. Or you could simplify this by just exposing a few choices and then creating the wheels inside the car class to match the selection. If the caller can provide the specific type of wheels, then this adds a lot of flexibility for testing your code because there could be special wheels that just the test code uses. These special wheels might record extra information or even be designed to fail much faster than any wheel your customers would use.
- You could allow outside code full control over wheel selection at any time. This could be used for upgrade scenarios or even to put different front vs. rear wheels on your cars.
This sounds great so far.
Why not just use composition all the time instead of inheritance? While you can use composition a lot and it is very useful, remember that it doesn’t give you access to protected members, it doesn’t allow you to override methods, and it doesn’t allow you to treat derived types as if they were base types.
You don’t end up with different and specific types with the composition approach. Maybe that’s exactly what you want or maybe not. If your racing game has a race that requires special offroad tires, then you have to make sure yourself that all the cars in the race meet this requirement. The inheritance route would allow you to create a special offroadCar class and then the compiler could help you by making sure that all the cars in the race were either offroadCars or types that derived from offroadCar.
This requires that you know ahead of time when writing your code that there will be a need for a special offroadCar class. But what if you don’t know that? Maybe you want to allow your game to be extended or modded in unexpected ways. This requirement is now bringing you away form using inheritance and more towards using composition. How?
Let’s use the armor example with derived classes helmet and boots. You’re building your game with just helmets and boots when during one or your regular meetings with your customer, the customer asks, “What about shields?” Okay, you didn’t think about those, so you create another class called shield that derives from armor. Then later you realize you need arm guards, breast plates, and grieves. Your list of classes is growing and you have to create individual classes for all of these. The final argument against this design comes when the customer wants a richer experience and asks for torn or dented helmets, rusted boots, and broken shields. And we haven’t even started talking about the previous problems with different materials.
You could instead adopt a simpler design that just has a single armor class that contains another class that describes the type of armor. This other class would be almost entirely based on data instead of methods. Let’s call this the armorType class. The armor class can then refer to the armorType class whenever it needs to figure out what kind of armor it actually is, what it’s made of, and what its condition is. You just took your software design from hundreds of classes down to just two.
And the really cool part is that because the armorType class is based on information instead of code, then this data can be read from a file. You now have a way to extend your game after the code has been written. That’s pretty powerful.
Just be aware that you accomplished this by removing individual classes and replacing them with data. Data is not always reliable and could lead to a dented glass helmet. It may not always make sense. But you’ve lost another aspect as well. When you had all those classes using inheritance, you had the ability to override behavior. In other words your helmet class could behave in code differently than your boots. Once you have just a single armor class that refers to data to know if it’s a helmet or a boot, it’s much more difficult to get that behavior modification back.
So what approach should you take? We’ve talked about inheritance and even multiple inheritance now as well as composition. There is no one right answer that fits all scenarios. In many ways, software design is still an art. You have choices and are free to change your decisions too.