Next chapter is quite complex. Literally complex. And I treat it as a last chapter of the book since it is a last chapter when you learn a new design pattern in a detailed way. And the last one of them is quite popular in many PHP frameworks. It is called MVC (Model-View-Controller). Maybe this is why it took me so long to write about it
Frankly, I’ve been quite busy for last months and we released our new feature in Wikia: Wikia Maps. Additionally, I contributed to few other side projects, visited Velocity EU conference in Barcelona and just couldn’t find time to write about almost last chapter in the book I’ve been describing here.
Compound patterns
Enough of excuses! Let me continue describing the chapter. It is quite long one and I will split it into two posts. However, it does not mean the posts are going to be short. Let’s start with the first one.
There is quite artificial example at the beginning demonstrating how design patterns can work together. And this post will describe it. It is somehow back to the past — together with authors we are implementing a duck simulator! Almost the same one from beginning of the book.
First we implement a Quackable
interface which is implemented by all other classes such as: MallardDuck
, RedheadDuck
, DuckCall
and RubberDuck
. Finally we implement the simulator itself and…
$ php app.php Duck Simulator Quack Quack Kwak Squeak
…done.
And here comes the first change: there are also geese around the place where ducks are. The simplest way is to implement a Goose
class and make it quack. But geese can not quack! They can with a help of an adapter. We modify a little bit our simulator so it creates a GooseAdapter
class instance. The adapter just overwrites the quack()
method of Quackable
interface which makes the simulator still work properly.
// GooseAdapter.class.php class GooseAdapter implements Quackable { private $goose; public function __construct(Goose $goose) { $this->goose = $goose; } public function quack() { $this->goose->honk(); } }
Another change is a request from a quackologist to count all the quacks done by ducks. We create a decorator which gives ducks a new behavior — counting behavior. New QuackCounter
class wraps Quackable
objects and delegate the quack()
call to the instance it is decorating but also increases the counter.
// QuackCounter.class.php class QuackCounter implements Quackable { private $duck; private static $numberOfQuacks = 0; public function __construct(Quackable $duck) { $this->duck = $duck; } public function quack() { $this->duck->quack(); static::$numberOfQuacks++; } public static function getQuacks() { return static::$numberOfQuacks; } }
It provides a new static method to return the count. It is pretty straightforward and clean because we do not modify the Quackable
class itself. The last step with this change is to wrap each Quackable
object which we instantiate in a QuackCounter
decorator and call QuackCounter::getQuacks()
method at the end of our simulator.
$ php app.php Duck Simulator Quack Quack Kwak Squeak Honk The ducks quacked: 4 times
What the Quackologist notice is the inconvenience of decorating each instance. He is right and this is another change in our program we want to make. To hermitize the process of wrapping each Quackable
object in a decorator we use a factory. The process is quite easy and we do not need to focus more on it (you can check it later in attached files).
The next requirement is to give our Quackologist possibility to manage a flock of ducks. Here comes the composite and itterator patterns for help. We create Flock
class which implements our Quackable
interface. It has add()
method and quack()
method.
// Flock.class.php class Flock implements Quackable { private $quackers = []; public function add( Quackable $quacker ) { $this->quackers[] = $quacker; } public function quack() { $iterator = new ArrayIterator( $this->quackers ); while( $iterator->valid() ) { $quacker = $iterator->current(); $quacker->quack(); $iterator->next(); } } }
The first one adds a Quackable
objects into an array. The last one iterates through the objects and calls quack()
method on them. In the simulator we just create flock of ducks and flock of mallards only and execute quack()
on them.
Duck Simulator: with composite - flocks Duck Simulator: whole flock simulation Quack Kwak Squeak Honk Quack Quack Quack Quack Duck Simulator: mallard flock simulation Quack Quack Quack Quack The ducks quacked: 11 times
The simulator works, so does the counting of ducks’ quacks.
Now, the Quackologist wants to be able to “track the individual ducks”. He wants to observe a duck which with Observer
and QuackObservable
interfaces is easy to achieve.
// Quackologist.class.php class Quackologist implements Observer { public function update(QuackObservable $duck) { echo 'Quackologist: ' . get_class($duck) . ' just quacked.' . PHP_EOL; } }
The Quackable
interface extends QuackObservable
, new Quackologist
class implements Observer
. After adding few more methods to our classes which implements Quackable
interface and two lines to the simulator — it is done.
$ php app.php Duck Simulator: with observer Quack Quackologist: RedheadDuck just quacked. Kwak Quackologist: DuckCall just quacked. Squeak Quackologist: RubberDuck just quacked. Honk Quackologist: GooseAdapter just quacked. Quack Quackologist: MallardDuck just quacked. Quack Quackologist: MallardDuck just quacked. Quack Quackologist: MallardDuck just quacked. Quack Quackologist: MallardDuck just quacked. The ducks quacked: 7 times
The example, as I mentioned at the beginning of this post, is quite artificial. The authors tell us about it by themselves:
You should consider the
DuckSimulator
to be forced and artificial. You should never start out with the intention of using design patterns just for the sake of it.
The example was presented only to demonstrate how design patterns can work together. You only want to apply patterns when and where they make sense. You can download the Java code of the example “translated” into a PHP code.
The example was also a preludium to second part of the chapter: The King of Computed Patterns. This is the name given by authors to Model-View-Controller design pattern which we will get to in my next blog post.