Design Pattern Principles for ActionScript 3.0: Favor Object Composition Over Class Inheritance
After getting slightly comfortable with the first dictum of design patterns, program to an interface; not an implementation, you may find this next one will knock the support blocks right from under you. In being advised to program to an interface, we get the idea that we type our objects to a superclass (abstract class or interface) and then we actually instantiate the object to a concrete class that is derived from the superclass. Doing so optimizes flexibility and minimizes dependency on implementations.
We assume that we are dealing with some kind of inheritance. It doesn’t matter if the inheritance is from an abstract class or pure interface. However, this next principle,
Favor object composition over class inheritance
looks like if we have a choice, we should avoid inheritance altogether. (We doan need no stinkin’ inheritance!)
Focus Here! A Little Inheritance Goes a Long Way
Instead of reassuring you that inheritance is really OK, you need to think of this new dictim as cautioning you to limit inheritance. With very few exceptions, when you use inheritance, you’re really only subclassing a few classes. Further, you want to subclass from abstract classes or implement from interfaces, and in either case, you don’t want to create some dodgy application that looks like your family tree. Each class should be focused on one task and encapsulated.
If you have the GoF book, take a close look, and you’ll see no more than three or four subclasses for any of the base classes in the examples. For instance, on page 50, you will see a very big inheritance class diagram. The base class is a Glyph interface, and it has three implementations. However, those implementations are not concrete classes. They’re abstract classes, each with three subclasses. Yes, the diagram does suggest you can add more, but that cannot be interpreted as suggesting that you should have more. Instead, think,
You shouldn’t have any more subclasses than you have siblings plus 1.
If you’re from a big family, this doesn’t work out too well, but it’s easy to remember. The key rule is that you want to maintain encapsulation by avoiding inheritance except from abstract classes or interfaces.
This can easily get to the point where you wonder why bother with inheritance at all? Other than the interface, the flexibility and direction it provides, abstract classes really don’t have a great deal to offer except a way to help insure incapsulation and perhaps a method or two that do not require overriding. (The Template Method Design Pattern [Chapter 9 in our book] is probably the best use of inheritance where you expect a good deal from the abstract parent class.)
So let’s imagine a slightly different place; one where you have little groups of classes connected to a common interface. If we try to build an application we find that we need some way to get from one archipeligo of classes to another to do anything useful.
Farming Out Requests
In this age of specialization and optimizing resource use, understanding what the Gang of Four means by favoring composition is pretty clear. Essentially, it’s saying that instead of trying to make everything using a single class or even interface, use the different existing classes to compose solutions. This point is covered on pp. 49-57 of our book, and I will not try your patience by reiterating it here. Rather, let me direct you to Figure 1-8 on page 57 where you can see how the processes of inheritance, delegation and instantiation all work together. (Also, take a look at our blog post on instantiation, inheritance and delegation.) Alternatively, go over the material on page 20 of GoF—after about 250 reads, it’s clear as a bell.
I’ve got to admit that when I’ve been in a hurry, I’ll put some giant hack of a class together, but I also know that when I do that, it’s the last time I’ll ever use that class. (Actually, I flatter myself by referring to such blobs as classes.) However, I’ve begun to think,
Hack = Death
and can effectively scare myself from doing that. Now I think,
One solution at a time.
So, instead of staggering to a task, I find it very easy to write an interface with just what I need and then implement it with the needed concrete classes. This process is made easier by Flex builder’s option of allowing you to pick the superclass of a class when creating the class file. If you use Flash, copy and paste works pretty well too. (I am most grateful to the recovering alcoholics among our readers who have pointed out the one-day-at-a-time method for writing design patterns.)
After assuaging my conscience by creating my interface and subclasses to get something done, I come to some point where I realize that I want to add functionality. Figure 1 shows how functionality can be (or must be in some cases) added when using inheritance as the primary building block of a project.
Figure 1: Adding Functionality in an inheritance hierarchy
If you want to add functionality, all you have to do is to either override a method and add the new functionality, or if you need a current set of methods, add a new method as shown in Figure 1. Of course you can always add a new class, but whether you can do anything unique with it depends of the flexibility of your superclass. That’s not too bad, but you know this will not end well. As you add more functionality, your design becomes both cumbersome and susceptible to failure, as shown in Figure 2.
Figure 2: The more you rely on inheritance alone, the more susceptible to error your program becomes as you add functionality
Before you know it, your program looks more incestuous than the House of Hapsburg. The more functionality you add, the greater the possibility of failure and the less clarity. If you’re very careful, you can still get it working right, but you might was well just plop all of your code in the Client and be done with it if you’re going to try and tip-toe your way through an inheritance minefield. Pretty soon, you look like the guy in Figure 3.
Figure 3: Programmer who tried to write it all using inheritance
Because clarity and re-use are essential to good OOP, we definitely want to avoid confusion and single-use code.
Alternative to Pure Inheritance
One of the ironies of the principle, favor composition over inheritance, is that most of the building blocks that make up a composed program are made up of components using inheritance. Figure 4 shows a class diagram illustrating where composition is favored over inheritance—even though all of the components use inheritance.
Figure 4: Favoring Composition
As you can see in Figure 4, favoring composition does not mean abandoning inheritance. The functionality of the program is centered in the components, each with concrete classes joined by a common interface. Instead of adding functionality to each component by adding more classes or methods, functionality is accessed through references to a component that has the desired functionality—in other words through delegation.
Composition Made Easy
You may be thinking,
That’s exactly why I don’t use these darned things at work! I’d spend all my time trying work out how to link the functionality between components! It’s just a waste of Red Bull! Sheeeze!
The Gang of Four recognized this dodgy state of affairs when using delegation, and note,
Delegation works best when used in highly stylized ways—that is in standard patterns. (GoF, 21)
So instead of starting in a design desert and trying to work out a composition-based OOP design, GoF recommends using one of the many patterns that use delegation. Included in these patterns are the State (Chapter 10 our book), Strategy (Chapter 11 our book) and the Visitor. In fact, these three patterns depend on composition. Lots of other design patterns use delegation as well, and both MVC and PureMVC are models of delegation in designs. (Also, take at look at different kinds of delegation here.)
If there’s a Swiss Army knife design pattern, it’s the Strategy pattern, and that’s a good place to start. The State pattern is a close second, and something about state machines is just plain cool; so it too should be studied to better understand composition. Keep in mind that GoF considers delegation an extreme form of object composition. Nevertheless, they also consider it to be a better choice than class inheritance because it is the only way to provide a structure that is as powerful as inheritance without the accompanying problems of inheritance. And finally remember that you can use composition and inheritance together in a framework. They are not mutually exclusive as Figure 4 clearly shows.
Composition Lunch Bucket Rule
The second principle espoused by GoF is pretty clear—favor composition over inheritance. For something to put into your lunch bucket and take to work, though, we need something a bit more specific. First off, Mickey Mouse and most other cartoon characters only have three fingers and a thumb. Like the following:
This will come in handy when remembering this second design pattern principle. To wit:
If you have more subclasses for a single base class than Mickey has fingers, delegate your next function.
Just look at your fingers and remember Mickey and most of his friends have one less finger than you—your fingers minus 1. (Naturally, if you have more or fewer fingers than the standard, you’ll need to make adjustments.)