Here at HauteLook,we’re almost constantly interviewing engineers. Not because we have high turnover,but because we’re always growing the team and it’s difficult to find engineers that have the skill level required to join our team. Part of our interview includes a “virtual whiteboard” using a Google doc. And part of that virtual whiteboard exercise includes representing a complex object,showing its properties and methods. We don’t ask for (or care about) valid UML; all that we care about is seeing that the candidate understands object-oriented design (OOD).
Many of our candidates do a wonderful job of constructing a complex hierarchy of inheritance. This gets the job done,but it doesn’t really allow a system to be extensible. When you overuse inheritance,you are basically designing according to assumptions which may seem true at the time,but which will likely not be true as the application is required to change. A key design principle isEncapsulate What Varies. The design principle covered in the post you’re currently reading,Favor Composition Over Inheritance,is one way of following the Encapsulate What Varies principle.
The overuse of inheritance in OOD is common,and is understandable. Many code examples in textbooks show inheritance astheway to make code reusable. And a design that utilizes a nice inheritance hierarchy does appear to make good sense. However,when you need to make a system do something that you hadn’t originally designed it for,the benefits you thought you were getting from an inheritance hierarchy suddenly become the sole reason to refactor your code. When subclasses inherit multiple behaviors from a parent class,they are locked into having those behaviors. The solution is simple,right? Just override those methods and implement the desired behavior for that subclass. But what if another subclass of the same parent has the same issue? Now,if you override the behavior in that subclass too,you have duplicated code.
Let me provide an example. (Note that I’m not going to design classes as we ask for in our whiteboard exercise. Instead,I’ll demonstrate this principle with code.) Let’s say that we want to design a class that plays music. The two concrete examples that we are required to implement are a record player (to play all the awesome 70’s music) and an 8-track player (just because). So we design a base AbstractPlayer class. It might look something like this:
abstract class AbstractPlayer { public function play() { echo "I'm playing music through my speakers!"; } public function stop() { echo "I'm not playing music anymore."; } }
Looks simple enough,right? And our RecordPlayer and EightTrackPlayer classes would inherit from AbstractPlayer and would therefore inherit the play() and stop() methods:
class RecordPlayer extends AbstractPlayer { } class EightTrackPlayer extends AbstractPlayer { }
Here’s some client code using the RecordPlayer:
$record_player = new RecordPlayer; $record_player->play(); // echoes "I'm playing music through my speakers!"