Registry Design - PHP - Miért kedveltem meg?

img

Eddig a blog főleg kisebb dolgokról szólt, css trükkökről, sitebuild megoldásokról, kisebb-nagyobb php problémákról. Most megpróbálok olyan dolgokat is leírni, amik kicsit mélyebben belenyúlnak a PHP világába, hiszen mindig azt hangoztatom, hogy nem vagyok egy túl erős sitebuilder, mégis a blogom nagyobb része erről szól.

Tehát a mai téma: Registry Design Pattern

Amikor nagyobb projecteken dolgozol gyakran szembesülhetsz egy problémával, ugyanazt az osztályt kell használnod másik osztályokon belül. Legjobb példa erre az adatbázis osztály. Használod felhasználók kezelésekor, termékek, cikkek lekérdezésekor, szóval szinte bárhol. Ha a kódod objektum orientált, és minden feladat a saját osztályában foglal helyet, akkor általában mindig azzal kezded az osztályt, hogy:

public function __construct(){
    $this->db=new DataBase();
}

Vagy másik általános megoldás, hogy globális változókban tárolod az osztályt, így mindenhol elérhetővé válik. Bármelyik megoldást is használod, rossz. Ha minden alkalommal újra inicializálod az adatbázis osztályt, akkor baromi sok memóriát fogsz elhasználni.

A megoldás: Registry Design

A Registry Pattern egy gyors és egyszerű felületet biztosít nekünk a fenti problémára az előbb említett megoldásokat elkerülve. Az osztályunk egy tömböt fog tartalmazni, amiben tárolunk minden egyéb osztályt és get()-set() metódusokkal tudjuk majd elérni őket.

class Registry 
{

    private $data = array();

    public function get($key)
    {
        return (isset($this->data[$key]) ? $this->data[$key] : null);
    }

    public function set($key, $value)
    {
        $this->data[$key] = $value;
    }
}

Persze ezt ki tudjuk egészíteni akár hibakezeléssel, hogy ne tudjunk egy már létező kulcsot felülírni, vagy ne csak null értékkel térjen vissza, ha nincs ilyen kulcsunk:

class Registry 
{

    private $data = array();

    public function get($key)
    {
        if (isset($this->data[$key])) {
            return $this->data[$key];
        } else {
            throw new Exception('A(z) '.$key.' kulcs nem létezik!');
        }
    }

    public function set($key, $value)
    {
        if (isset($this->data[$key])) {
            throw new Exception('A(z) '.$key.' kulcs már létezik!');
        }
        $this->data[$key] = $value;
    }
}

Tehát ez az alapja a Registrynek. De ezzel még mindig nem oldottuk meg az alap problémánkat, miszerint ennek bárhonnan elérhetőnek kell lennie.

Erre megoldás a Singleton Pattern. Most hallom a felháborodást, hogy "a Singleton Pattern használata nem javasolt!" Ami igaz, nem ajánlják a használatát, mert boldog-boldogtalan rosszul használja, de ha jól használjuk, nincs vele gond.

Tehát elsőként a megszokott módon létrehozunk egy statikus változót, ami tárolja az aktuális változatot. Ha már létezik visszatér vele, ha nem, akkor létrehozza, így érhetjük el, hogy csak egyetlen egyed létezzen az osztályból. A konstruktort és a klónozást privátra állítjuk, hogy ne tudja meghívni a programunk.

class Registry
{

    private static $instance = null;

    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new Registry();
        }

        return self::$instance;
    }

    private function __construct() {}
    private function __clone() {}

    [...]
}

Nagyjából készen is vagyunk. Most már képes a programunk használni a Loggert, az adatbázis osztályt, vagy akármelyik másik osztályunkat.

//Betöltjük a megfelelő fájlokat
include 'Leoamros/Classes/Registry.php';

try {

    include 'Leoamros/Config/Config.php';

    $registry=Registry::getInstance();

    //betöltjük a beállításokat és letároljuk a Registryben
    $registry->set('config', new Config('Config.Ini.php'));

    //töltsük be az adatbázis kapcsolathoz szükséges beállításokat
    $database = $registry->get('config')->get('database');

    $pdoConnect = 'mysql:host=' . $database['host'] . ';dbname=' . $database['name'];

    //tároljuk az adatbázis kapcsolatot is a Registryben
    $registry->set('database', new PDO($pdoConnect, $database['user'], $database['pass']));

} catch(Exception $e) {
    echo $e->getMessage();
}

Inenntől kezdve bárhonnan egyszerűen elérhető az adatbázis kapcsolatunk is, anélkül, hogy folyamatosan újra kellene inicializálni.

$registry=Registry::getInstance();

$pdo=$registry->get('database');

Így már tökéletesen használható. Még van 1-2 apróság, amit megcsinálhatunk, ezt mindjárt leírom, előbb nézzük meg az egész osztályt egyben:

class Registry
{

    private $data = array();  
    private static $instance = null;

    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new Registry();
        }

        return self::$instance;
    }

    private function __construct() {}
    private function __clone() {}

    public function get($key)
    {
        if (isset($this->data[$key])) {
            return $this->data[$key];
        } else {
            throw new Exception('A(z) '.$key.' kulcs nem létezik!');
        }
    }

    public function set($key, $value)
    {
        if (isset($this->data[$key])) {
            throw new Exception('A(z) '.$key.' kulcs már létezik!');
        }
        $this->data[$key] = $value;
    }
}

Néhány sornyi kód, és ráadásul baromi egyszerű használni is.

Szóval említettem, hogy lehet még bővíteni is az osztályt. A PHP rengeteg interface-szel rendelkezik, amiket könnyen implementálhatunk a programunkba. Ezeket a Standard PHP Library (SPL) tartalmazza. Néhány programozónak könnyebb az osztályokat tömbként kezelni. Erre megoldást nyújt az ArrayAccess Interface. Ekkor az osztály a következőképp módosul:

class Registry implements ArrayAccess
{

    private $data = array();  
    private static $instance = null;

    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new Registry();
        }

        return self::$instance;
    }

    private function __construct() {}
    private function __clone() {}

    public offsetExists($key)
    {
        return isset($this->data[$key]);
    }

    public offsetGet($key)
    {
        if (isset($this->data[$key])) {
            return $this->data[$key];
        }

        return null;
    }

    public offsetSet($key, $value)
    {
        return $this->data[$key] = $value;
    }

    public offsetUnset($key) {
        return unset($this->data[$key]);
    }
}

Használata pedig ugyanolyan egyszerű:

try {

    include 'Leoamros/Config/Config.php';

    $registry=Registry::getInstance();

    //betöltjük a beállításokat és letároljuk a Registryben
    $registry['config'] = new Config('Config.Ini.php');

    //töltsük be az adatbázis kapcsolathoz szükséges beállításokat
    $database = $registry['config']->get('database');

    $pdoConnect = 'mysql:host=' . $database['host'] . ';dbname=' . $database['name'];

    //tároljuk az adatbázis kapcsolatot is a Registryben
    $registry['database'] = new PDO($pdoConnect, $database['user'], $database['pass']);

} catch(Exception $e) {
    echo $e->getMessage();
}

Remélem segített ez a kis kódrészlet valakinek. Én már nagyjából 1 éve használom a Registry Designt, sok felesleges munkától mentett eddig meg.