Factory method pattern using PHP 5.3 and late static bindings

Citește postarea în română

Share on:

Factory method design pattern introduced by GoF (Gang of Four) which I’ve talked about in a previous blog, has the base idea of a method that generates objects. Most implementations are “poor”, because they use a lot of hard-coding for the factory (just like me in the previous blog).

PHP 5.3 offers the possibility of a very interesting implementation using “Late static bindings“.

The classes from which the objects will be generated are:

 1// abstract base class that will be inherited
 2abstract class Drink {
 3
 4    // ingredients
 5    protected $ingredients;
 6
 7    // public method for producing the drink
 8    abstract public function MakeDrink();
 9}
10
11// a child class for tea
12class Tea_Drink extends Drink {
13
14    // ingredients for tea
15    protected $ingredients = array('tea', 'sugar', 'mink', 'water');
16
17    // make tea
18    public function MakeDrink() {
19
20        // make tea
21    }
22}
23
24// another class for Bloody Mary
25class BloodyMary_Drink extends Drink {
26
27    // ingredients for Bloody Mary
28    protected $ingredients = array('votka', 'salt', 'tomato juice');
29
30    // make Bloody Mary
31    public function MakeDrink() {
32
33        // make BloodyMary
34
35    }
36}

The idea is to have an abstract factory class to extend as simple as possible when creating each new factory class.

PHP 5

In PHP 5 the class will look something like this:

 1// abstract Factory class
 2abstract class absFactory {
 3
 4    // name of the base class
 5    static protected $base_class = '';
 6
 7    // factory method
 8    public static function getInstance($type) {
 9
10        // name of the resulting class
11        $class_name = $type . '_' . self::$base_class;
12
13        // check if class exists
14        // here you can add an autoloader
15        if(!class_exists($class_name)) {
16            throw new Exception( 'Class ' . $class_name . ' not loaded!');
17        }
18
19        // check to see if the class inherits the base class
20        if(!is_subclass_of($class_name, self::$base_class)) {
21            throw new Exception(
22                'Class ' . $class_name . ' is not a child of ' . self::$base_class
23            );
24        }
25
26        // new object
27        return new $class_name;
28
29    }
30
31}

Because the getInstance() method is static the property will be static too.

If we try:

 1class DrinkFactory extends absFactory {
 2
 3    static protected $base_class = 'Drink';
 4}
 5
 6try {
 7
 8    $obj = DrinkFactory::getInstance('Tea');
 9
10} catch (Exception $e) {
11
12    echo $e->getMessage();
13}

The output will be:

1Class Tea_ not loaded!

Because of the “self”, we can’t just call the method using the child class because the value of $base_class will be “” and not “Drink”, we must overwrite the getInstance() method. Which is quite “complicated”.

A working version in PHP 5 will be:

 1class DrinkFactory extends absFactory {
 2
 3    public static function getInstance($type) {
 4
 5        self::$base_class = 'Drink';
 6
 7        // factory method of the base factory class
 8        parent::getInstance($type);
 9
10    }
11
12}
13
14try {
15
16    $obj = DrinkFactory::getInstance('Tea');
17
18} catch (Exception $e) {
19
20    echo $e->getMessage();
21}

But is not exactly “elegant”.

PHP 5.3

Here we have “Late static bindings”, which is basically introducing the work “static”.

The base factory class will look something like this:

 1// abstract Factory class
 2abstract class absFactory {
 3
 4    // name of the base class
 5    static protected $base_class = '';
 6
 7    // factory method
 8    public static function getInstance($type) {
 9
10        // name of the resulting class
11        $class_name = $type . '_' . static::$base_class;
12
13        // check if class exists
14        // here you can add an autoloader
15        if(!class_exists($class_name)) {
16            throw new Exception( 'Class ' . $class_name . ' not loaded!');
17        }
18
19        // check to see if the class inherits the base class
20        if(!is_subclass_of($class_name, static::$base_class)) {
21            throw new Exception(
22                'Class ' . $class_name . ' is not a child of ' . static::$base_class
23            );
24        }
25
26        // new object
27        return new $class_name;
28
29    }
30
31}

A change so small allows us to create a much “nicer” factory class:

 1class DrinkFactory extends absFactory {
 2
 3     static protected $base_class = 'Drink';
 4
 5}
 6
 7try {
 8
 9    $obj = DrinkFactory::getInstance('Tea');
10
11} catch (Exception $e) {
12
13    echo $e->getMessage();
14}

Basically in this version only the relevant property in this context is overwritten.