Claudiu Persoiu

Blog-ul lui Claudiu Persoiu


Factory method pattern folosind PHP 5.3 si late static bindings

without comments

Factory method design pattern introdus de GoF (Gang of Four) pe care l-am abordat si eu intr-un blog anterior, are la baza ideea unei metode care genereaza obiecte. In general implementarea este destul de “slaba” pentru ca “factory”-ul este destul de hardcodat (la fel ca mine in blogul anterior).

PHP 5.3 ofera totusi posibilitatea unei implementari foarte interesante folosind “Late static bindings“.

Clasele pe baza carora se vor genera obiectele:

// clasa abstracta de baza care urmeaza sa fie mostenita
abstract class Drink {

    // ingrediente
    protected $ingredients;

    // metoda publica care genereaza bautura
    abstract public function MakeDrink();
}

// o clasa derivata pentru ceai
class Tea_Drink extends Drink {

    // ingrediente pentru ceai
    protected $ingredients = array('tea', 'sugar', 'mink', 'water');

    // generare ceai
    public function MakeDrink() {

        // fa ceai
    }
}

// clasa derivata pentru Bloody Mary
class BloodyMary_Drink extends Drink {

    // ingrediente Bloody Mary
    protected $ingredients = array('votka', 'salt', 'tomato juice');

    // generare Bloody Mary
    public function MakeDrink() {

        // fa BloodyMary

    }
}

Idea este de a avea o clasa abstracta care va fi extinsa intr-un mod cat mai simplu pentru a genera noi clase factory.

PHP 5

In PHP 5 clasa ar arata cam asa:

// clasa Factory abstracta
abstract class absFactory {

    // numele clasei de baza
    static protected $base_class = '';

    // metoda factory
    public static function getInstance($type) {

        // numele clasei rezultat
        $class_name = $type . '_' . self::$base_class;

        // se testeaza daca clasa exista
        // aici se poate adauga si un autoloader eventual
        if(!class_exists($class_name)) {
            throw new Exception( 'Class ' . $class_name . ' not loaded!');
        }

        // se verifica daca clasa mosteneste clasa de baza
        if(!is_subclass_of($class_name, self::$base_class)) {
            throw new Exception(
                'Class ' . $class_name . ' is not a child of ' . self::$base_class
            );
        }

        // noul obiect
        return new $class_name;

    }

}

Pentru ca metoda getInstance este statica si proprietatea este statica.

Daca incercam:

class DrinkFactory extends absFactory {

    static protected $base_class = 'Drink';
}

try {

    $obj = DrinkFactory::getInstance('Tea');

} catch (Exception $e) {

    echo $e->getMessage();
}

Output-ul va fi:

Class Tea_ not loaded!

Din pricina “self”, nu putem apela pur si simplu metoda getInstance() din clasa copil pentru ca valoarea lui $base_class va fi “” in loc de “Drink”, trebuie suprascrisa metoda getInstance(). Lucru prea “complicat”.

O versiune functionala in PHP 5 ar fi:

class DrinkFactory extends absFactory {

    public static function getInstance($type) {

        self::$base_class = 'Drink';

        // metoda factory a clasei factory de baza
        parent::getInstance($type);

    }

}

try {

    $obj = DrinkFactory::getInstance('Tea');

} catch (Exception $e) {

    echo $e->getMessage();
}

Dar nu mi se pare tocmai “elegant”.

PHP 5.3

Aici avem “Late static bindings”, care in principiu este introducerea cuvantului “static”.

Clasa de baza factory arata cam asa:

// clasa Factory abstracta
abstract class absFactory {

    // numele clasei de baza
    static protected $base_class = '';

    // metoda factory
    public static function getInstance($type) {

        // numele clasei rezultat
        $class_name = $type . '_' . static::$base_class;

        // se testeaza daca clasa exista
        // aici se poate adauga si un autoloader eventual
        if(!class_exists($class_name)) {
            throw new Exception( 'Class ' . $class_name . ' not loaded!');
        }

        // se verifica daca clasa mosteneste clasa de baza
        if(!is_subclass_of($class_name, static::$base_class)) {
            throw new Exception(
                'Class ' . $class_name . ' is not a child of ' . static::$base_class
            );
        }

        // noul obiect
        return new $class_name;

    }

}

Diferentele fata de PHP 5 sunt marcate cu bold. O schimbare atat de mica permite totusi o clasa factory mult mai “atragatoare”:

class DrinkFactory extends absFactory {

     static protected $base_class = 'Drink';

}

try {

    $obj = DrinkFactory::getInstance('Tea');

} catch (Exception $e) {

    echo $e->getMessage();
}

Practic in aceasta varianta nu se inlocuieste decat proprietatea statica relevanta in acest context.

Written by Claudiu Persoiu

24 January 2010 at 5:46 PM

Posted in Design patterns,PHP

Tagged with , , ,

Leave a Reply