Friday, April 02, 2010

Practical Php Patterns: Strategy

This post is part of the Practical Php Pattern series.

The pattern of the day (and of the week) is one of the most important ones in object-oriented programming: the Strategy pattern. Its intent is encapsulating an algorithm into a specific object, defining a clear input and output exposed to the Context where the Strategy is used, to let them both vary independently.
The Strategy used in a Context object can change for configuration purposes, thus allowing the selection of a specific behavior. Other use cases comprehend the tuning of non-functional requirements such as fast switching between performance or memory usage trade-offs of different algorithms that implement the same behavior (a classical example is sorting).
Finally, the use of Strategy objects simplify unit testing. For example, injecting in-memory Strategies instead of disk-related ones is the standard way to test classes that depend on infrastructure.
Participants
  • Context: uses a Strategy object, outsourcing part of its behavior.
  • Strategy: contract that Context sees.
  • ConcreteStrategy: implementation of Strategy as a particular behavior.
Switch statements or if-else chains are candidates for refactoring to a Strategy pattern (as they are for the State pattern). The difference between the two is their intent: State encapsulates data and possibly the transition to other States; a Strategy object usually does not produce other Strategy implementations and hides complex behavior.
The implementation of the Strategy pattern usually follows the classical composition paradigm: Context has a private field reference to a Strategy, while Strategy may be shareable as a Flyweight if the Context passes to it the necessary parameters (or even itself) when calling its methods.
The composition of a Strategy object is a valuable alternative to inheritance: in my opinion Strategy can be think of as a generalization of many patterns that gain their power from favoring composition. An Abstract Factory is in fact a Strategy dedicated to the creation of objects; an Adapter allow retrofitting an object as a Strategy for another one; and so on.

The code sample uses hidden Strategy objects for the sorting process of a Collection, in particular for comparing two values.
<?php
/**
 * The Strategy. Defines a behavior for comparing two objects
 * of the Collection.
 */
interface Comparator
{
    /**
     * @return integer  -1 if $a should precede $b
     *                  1 if $b should precede $a
     *                  0 if considered equal
     */
    public function compare($a, $b);
}

/**
 * The Context where the Strategy is employed.
 * Strategy is stored as a private field which can
 * be initialized only one time.
 */
class Collection implements Countable
{
    private $_elements;
    private $_comparator;

    public function __construct(array $elements = array())
    {
        $this->_elements = $elements;
    }

    public function initComparator(Comparator $comparator)
    {
        if (isset($this->_comparator)) {
            throw new Exception("A Comparator is already present.");
        }
        $this->_comparator = $comparator;
    }

    public function sort()
    {
        $callback = array($this->_comparator, 'compare');
        uasort($this->_elements, $callback);
    }

    /**
     * A representation for a clear output.
     */
    public function __toString()
    {
        $elements = array();
        foreach ($this->_elements as $value) {
            if (is_array($value)) {
                $value = 'Array with ' . count($value) . ' elements';
            }
            $elements[] = $value;
        }
        return '(' . implode(', ', $elements) . ')';
    }

    public function count()
    {
        return count($this->_elements);
    }
}

/**
 * A ConcreteStrategy that compares via the native operator.
 */
class NumericComparator implements Comparator
{
    public function compare($a, $b)
    {
        if ($a == $b) {
            return 0;
        }
        return $a < $b ? -1 : 1;
    }
}

/**
 * A ConcreteStrategy that compares via the result
 * of the count() function.
 */
class CountableObjectComparator implements Comparator
{
    public function compare($a, $b)
    {
        if (count($a) == count($b)) {
            return 0;
        }
        return count($a) < count($b) ? -1 : 1;
    }
}

// ordering numbers
$numbers = new Collection(array(4, 6, 1, 7, 3));
$numbers->initComparator(new NumericComparator);
$numbers->sort();
echo $numbers, "\n";

// ordering Countable objects
$first = array(1, 2, 3);
$second = array(1, 2, 3, 4);
$third = new Collection(array(1, 2, 3, 4, 5));
$objects = new Collection(array($third, $second, $first));
$objects->initComparator(new CountableObjectComparator);
$objects->sort();
echo $objects, "\n";

10 comments:

e.s.t said...

Good example, as usual.

Unknown said...

personally I use very often Strategy pattern becouse is realy simple to implement and can save a lot of time and encourage the composition instead of inheritance. Strategy pattern allows to adapt to changes becouse it's primary focus is point out what can change and encapsulate into an object.
I feel I have to add only one consideration in your discussion: instead of check if the strategy is set it may be helpful to add a NullStategy object to handle the lack of initialization situations.
It encapsulates the implementation decisions of how to do nothing and hides those details from the Context.

Giorgio said...

Or move the Strategy in the constructor params so that it's clear that it is needed. :)

fqqdk said...

...but that would couple collections with their one-and-only sorting mechanism. i think the sorting algorithm should be injected to the sort method (maybe making it optional, using some kind of a default ordering)

function sort(Comparator $c = null) { if(null === $c) $c = new DefaultComparator; /*etc*/ }

For a constructor injected Strategy one can imagine some kind of a DatabaseConnection / DatabaseEngine example.

Unknown said...

ciao giorgio, intanto ti faccio i complimenti per i tuoi articoli che seguo anche su dzone, ormai sei uan celebrità ;)

Ti volevo chiedere un consiglio: sto facendo un refactoring di un progetto in cui ho una classe che nel costruttore ha una catena di if su un solo parametro avviando metodi init che inizializzano lo stato dell'oggetto.

Una soluzione per togliere la catena di if è usare la reflection, ma volevo sapere se esiste (sicuramente perchè è un problema ricorrente) un pattern specifico.

Giorgio said...

Se puoi modificare quel codice, ti suggerisco di introdurre un controllo che nel caso in cui il parametro è null non fa nulla. Il pattern specifico sarebbe costruire lo stato dell'oggetto dall'esterno, in quanto meno lavoro il costruttore fa, meglio è (perché non deve essere ripetuto in tutti i test ad ogni new).

English is better however. :)

Unknown said...

ies , bat ai dont spik veri uel

;)

tencs

Kells said...

Well I also use the strategy design  pattern since its much simpler to keep track.There are some cases that when you have several behaviors together in one class, its too difficult to choose among them without resorting to conditional statements.

Anonymous said...

I'm curious too...what pattern do you use to determine which strategy to use? Wont you then need a switch or if chain somewhere?

Giorgio said...

The decision is moved at instantiation time, so you have only one conditional [chain] in a single place, instead of one for each time a Strategy method would be called.

ShareThis