PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1

CHAPTER 10 ■ PATTERNS FOR FLEXIBLE OBJECT PROGRAMMING


will determine the kinds of assumptions you can make when you design methods like getComposite()
and addUnit().
These contortions are symptomatic of a drawback to the Composite pattern. Simplicity is achieved
by ensuring that all classes are derived from a common base. The benefit of simplicity is sometimes
bought at a cost to type safety. The more complex your model becomes, the more manual type checking
you are likely to have to do. Let’s say that I have a Cavalry object. If the rules of the game state that you
cannot put a horse on a troop carrier, I have no automatic way of enforcing this with the Composite
pattern:


class TroopCarrier {


function addUnit( Unit $unit ) {
if ( $unit instanceof Cavalry ) {
throw new UnitException("Can't get a horse on the vehicle");
}
super::addUnit( $unit );
}


function bombardStrength() {
return 0;
}
}


I am forced to use the instanceof operator to test the type of the object passed to addUnit(). Too
many special cases of this kind, and the drawbacks of the pattern begin to outweigh its benefits.
Composite works best when most of the components are interchangeable.
Another issue to bear in mind is the cost of some Composite operations. The Army ::
bombardStrength() method is typical in that it sets off a cascade of calls to the same method down the
tree. For a large tree with lots of subarmies, a single call can cause an avalanche behind the scenes.
bombardStrength() is not itself very expensive, but what would happen if some leaves performed a
complex calculation to arrive at their return values? One way around this problem is to cache the result
of a method call of this sort in the parent object, so that subsequent invocations are less expensive. You
need to be careful, though, to ensure that the cached value does not grow stale. You should devise
strategies to wipe any caches whenever any operations take place on the tree. This may require that you
give child objects references to their parents.
Finally, a note about persistence. The Composite pattern is elegant, but it doesn’t lend itself neatly
to storage in a relational database. This is because, by default, you access the entire structure only
through a cascade of references. So to construct a Composite structure from a database in the natural
way you would have to make multiple expensive queries. You can get round this problem by assigning
an ID to the whole tree, so that all components can be drawn from the database in one go. Having
acquired all the objects, however, you would still have the task of recreating the parent/child references
which themselves would have to be stored in the database. This is not difficult, but it is somewhat messy.
While Composites sit uneasily with relational databases, they lend themselves very well indeed to
storage in XML. This is because XML elements are often themselves composed of trees of subelements.


Composite in Summary


So the Composite pattern is useful when you need to treat a collection of things in the same way as you
would an individual, either because the collection is intrinsically like a component (armies and archers),
or because the context gives the collection the same characteristics as the component (line items in an
invoice). Composites are arranged in trees, so an operation on the whole can affect the parts, and data
from the parts is transparently available via the whole. The Composite pattern makes such operations

Free download pdf