Specializing classes on the fly with the Decorator design pattern

PHP DEsign Patterns

Introduction

Ideally, we’d like to get away with writing all-in-one monolithic pieces of code in which problems are solved by extending existing functionality, but the reality of execution environments is often more convoluted and structural design patterns provide convenient ways to manage relationships between entities in our code.

The problem

Let’s imagine a program that displays information about a European citizen such as his name, address and occupation. We are expected to see slight variations in the behavior of our citizen classes depending on nationality, so we could start thinking about an inheritance scheme where different “national” classes would inherit global properties and methods, very much like in this example:

/**
 * The contract mapping out some basic attributes 
 * that should be visible in all citizen classes
 *
 */
interface IAmACitizen
{
    public function getName();

    public function getAddress();

    public function getOccupation();
}

interface ICanDoWork
{

    public function doWork();
}

/**
 * Our main citizen class
 */
abstract class EuropeanCitizen implements IAmACitizen, ICanDoWork
{
    protected $occupation;
    protected $name;
    protected $address;

    function __construct( $occupation, $name, $address )
    {
        $this->occupation = $occupation;
        $this->name       = $name;
        $this->address    = $address;
    }

    public function getAddress()
    {
        return $this->address;
    }

    public function getOccupation()
    {
        return $this->occupation;
    }

    public function getName()
    {
        return $this->name;
    }
}

/**
* Citizen class with German specific attributes
*/
class GermanCitizen extends EuropeanCitizen
{
    public function doWork(){
    
    }

    public function germanSpecificAttribute(){

    }
}

/**
* Displays the citizen's properties
*
*/
class Display
{

    public static function display( IAmACitizen $citizen )
    {
        echo $citizen->getName() . "
"; echo $citizen->getAddress() . "
"; echo $citizen->doWork() . "
"; if ($citizen instanceof GermanCitizen) { echo $citizen->germanSpecificAttribute() . "
"; } } } //Instantiating a German citizen $citizen = new GermanCitizen(); //Displaying the citizen Display::display($citizen); }

All is good, we can instantiate our German citizen object and display its specific attributes. We also define contracts through the IAmACitizen and ICanDoWork interfaces that map out the methods to be implemented by citizen objects.

Nothing’s wrong with that code, but it turns out that in our “real world” environment, we don’t know the nationality of our citizen when the app is initialized; that information is collected later.

The code example above breaks down because we have to define our specific type from the beginning to get its specific behavior. We have to find a way to trigger specific citizen related behavior dynamically if and when we get the information we need. That’s where the Decorator pattern comes in.

Using the pattern

In our design, we’ll create worker objects that can grab citizen information (not modifying citizen classes) and have specific worker behavior, as for example a German citizen having German worker behavior. The EuropeanCitizen class we defined above will remain intact.

interface ICanDoWork
{
    public function doWork();
}

abstract class EuropeanWorkerStereotype implements ICanDoWork
{

    protected $citizen;

    public function __construct( IAmACitizen $citizen)
    {
        $this->citizen= $citizen;
    }

    public function getAddress()
    {
        return $this->citizen->getAddress();
    }

    public function getOccupation()
    {
        return $this->citizen->getOccupation();
    }

    public function getName()
    {
        return $this->citizen->getName();
    }
}

The decorator pattern consists in wrapping objects within other objects so that their behavior can be emulated and even enhanced (decorated) without boxing oneself in an inheritance model.

In our example, we feed a EuropeanCitizen to our class (or rather any class that implements the IAmACitizen interface), thereby having access to all of its public attributes. Our worker classes will still be able to fetch citizen information just like a child citizen class would.

The downside of such an approach is that this forces us to wrap all the methods we want to use, since we can’t take advantage of inheritance. As you can see in the above code, we rely on the citizen object to get any citizen related information.

The advantage is that we can now “decorate” our citizen object with worker behavior without touching the citizen class. In our example, we even delegate all worker stuff to worker classes.

Let’s define our specialized worker classes:

class GermanWorkerStereotype extends EuropeanWorkerStereotype
{

    public function doWork()
    {
        return $this->makeCarsForEurope();
    }

    public function makeCarsForEurope()
    {
        return "BMW, Volkswagen, Audi...";

    }
}


class GreekWorkerStereotype extends EuropeanWorkerStereotype
{
    public function doWork()
    {
        return $this->lazeAllDay();
    }

    public function lazeAllDay()
    {
        return "ZZZZZZZZ";

    }
}

class FrenchWorkerStereotype extends EuropeanWorkerStereotype
{

    public function doWork()
    {
        return $this->goOnStrike( null, 5 );
    }

    public function goOnStrike( $reason = null, $numberOfWeeks )
    {
        return "What do we want? I don't know! When do we want it? Now!";
    }
}

Inheriting the EuropeanWorkerStereotype class forces us to implement the doWork method. It looks like we’re duplicating code, but this leads to methods being callable directly without having to test for types.

Moving on to displaying the citizen/worker:

class DisplayWorker
{

    public static function display( EuropeanWorkerStereotype $worker )
    {
        echo $worker->getName() . "
"; echo $worker->getAddress() . "
"; echo $worker->doWork() . "
"; } } $worker = new EuropeanCitizen( "W", "X", "Y" ); $needAGreekWorker = true; if ($needAGreekWorker) { DisplayWorker::display( new GreekWorkerStereotype( $worker ) ); } /* * Displays: * * X * Y * ZZZZZZZZ * */

From the display method’s perspective, it looks like all the work is carried out by a single object, but we actually have two: one that initializes citizen information and another that gets the citizen object to work. We did a good job of hiding the details of our implementation to clients while having the flexibility to decorate our objects dynamically depending on context.

Further reading