Sometimes when you think about key OOP concepts such as inheritance, you have to consider its larger consequences. The upside of inheritance is that once a superclass has been established, all of the subclasses inherit its features. However, you may not want or need all of the "stuff" for every subclass from a superclass. If you want to add functionality to an object, subclassing may not be the way to go, because everything else subclassed from the same superclass may be unnecessarily burdened by unused functionality.
At the end of this chapter, you will see an example of a car dealership where users select models of cars with a choice of options for those cars. If all the options were derived from a superclass, that would mean that every car would have all the options, whether or not you wanted them. One option in the example is a rear view camera used with minivans and large SUVs with limited rear vision. Such an option would be superfluous on a sports car such as a Chevrolet Corvette, Morgan, or Ferrari. However, with subclassing, that's exactly what would happen. Every subclass gets everything from the superclass. So here's a case where we'd have to look beyond simple inheritance. Depending on how the application's written, even the components would all have the same features. A pickup truck would be subclassed from the same component as a luxury car—both inheriting features they don't want and wouldn't use.
The Decorator design pattern is also known as the Wrapper pattern. The concept of "wrapping" is at the heart of the Decorator design pattern. So what does it mean to "wrap" one object in another? One way to think about wrapping is to imagine wrapping a gift. The wrapper transforms the gift, but the gift does not inherit the characteristics of the wrapper—it only uses the wrapping. Unlike subclassing, which extends one class into another, wrapping allows one object to use another's characteristics without extending either the wrapped object or the object doing the wrapping.
The wrapping with which most ActionScript programmers are familiar is that
used for transforming data types from one type to another. For example, the
Number class can wrap a string variable,
and then that variable has the characteristics of the
Number class. Figure 4-3 illustrates a common wrapper function:
When we look at wrapping one object with another object, we can think of
it as a class intercepting API calls intended for a specific instance of
another class. The example in Figure 4-3 shows that the
Number class is intercepting a call to an
instance of the
String class. The result
is that the
l variable, an unsigned
integer, is able to accept the assignment of the
s variable as a number. That's because the
s variable is wrapped in a
Number class, and is treated as a
Using the Decorator class, components are wrapped in decorators. The wrapping class is a concrete instance of a decorator, and the wrapped class is an instance of the concrete component class. Thus, the concrete component now "contains" the characteristics of the wrapper, but the characteristics are not inherited. Figure 4-4 shows an example.
The concrete component that's wrapped by another class borrows characteristics of the wrapping class. When those characteristics are not needed, the instance of the concrete class is simply instantiated without being wrapped.
One good OOP practice is to create your classes so that they can be extended but not changed. If a class is changed, especially one with subclasses, you can quickly destroy an application. So the trick is to set up your classes so that it's easy to extend them, yet keep them safe from alteration.
The Decorator design pattern allows you to do this. The model is grounded in a
single class that is the superclass to all others. This core class is an
abstract component class. An abstract decorator class is subclassed from this
class, re-implementing the core methods. (In the context of ActionScript 3.0,
the abstract nature of the class is simulated by using the
override statement when re-implementing
methods in subclasses.) All the other concrete component and decorator classes
have the same root superclass, even though the decorator classes are subclassed
from the abstract decorator class, and the component classes are directly from
the abstract component class. So, when the concrete component objects are
wrapped in concrete decorator objects, both objects share the same root
superclass. Thus, the decorated objects can keep the core object unmodified
while changing its responsibilities.
In a larger OOP framework, the Decorator pattern can add and subtract functionality from an object by wrapping selected components with selected decorators. Second, it can add more than a single functionality to an object by wrapping one wrapper inside another wrapper. As a result, the design pattern can change functionality without changing structure.