«

Dec 07

Print this Post

Design patterns: compound patterns

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.

Permanent link to this article: https://blog.lukaszewski.it/2014/12/07/design-patterns-compound-patterns/

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>