PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1
CHAPTER 10 ■ PATTERNS FOR FLEXIBLE OBJECT PROGRAMMING

Often you really see the benefit of a pattern only from the client’s perspective, so here are a couple
of armies:


// create an army
$main_army = new Army();


// add some units
$main_army->addUnit( new Archer() );
$main_army->addUnit( new LaserCannonUnit() );


// create a new army
$sub_army = new Army();


// add some units
$sub_army->addUnit( new Archer() );
$sub_army->addUnit( new Archer() );
$sub_army->addUnit( new Archer() );


// add the second army to the first
$main_army->addUnit( $sub_army );


// all the calculations handled behind the scenes
print "attacking with strength: {$main_army->bombardStrength()}\n";


I create a new Army object and add some primitive Unit objects. I repeat the process for a second
Army object that I then add to the first. When I call Unit::bombardStrength() on the first Army object, all
the complexity of the structure that I have built up is entirely hidden.


Consequences


If you’re anything like me, you would have heard alarm bells ringing when you saw the code extract for
the Archer class. Why do we put up with these redundant addUnit() and removeUnit() methods in leaf
classes that do not need to support them? An answer of sorts lies in the transparency of the Unit type.
If a client is passed a Unit object, it knows that the addUnit() method will be present. The
Composite pattern principle that primitive (leaf) classes have the same interface as composites is
upheld. This does not actually help you much, because you still do not know how safe you might be
calling addUnit() on any Unit object you might come across.
If I move these add/remove methods down so that they are available only to composite classes, then
passing a Unit object to a method leaves me with the problem that I do not know by default whether or
not it supports addUnit(). Nevertheless, leaving booby-trapped methods lying around in leaf classes
makes me uncomfortable. It adds no value and confuses a system’s design, because the interface
effectively lies about its own functionality.
You can split composite classes off into their own CompositeUnit subtype quite easily. First of all, I
excise the add/remove behavior from Unit:


abstract class Unit {
function getComposite() {
return null;
}


abstract function bombardStrength();
}

Free download pdf