Czym jest wzorzec Rejestr(Registry) i kiedy może się nam przydać?

Wzorzec Rejestru jest przydatny gdy chcemy mieć globalny dostęp do obiektów znajdujących się w jednym miejscu.  Jaka jest więc różnica pomiędzy Rejestrem, a zastosowaniem zmiennych globalnych? Otóż dzięki użyciu Rejestru mamy kontrole nad dostępem do obiektów. Podstawowymi metodami rejestru będą metody rejestrujące obiekt oraz pobierające go. Można jeszcze, a wręcz powinno się dodać metodę, która wyrejestruje obiekt, ale w tym prostym przykładzie to pominę. Rejestracja obiektu i jego pobieranie zostaną zaimplementowane poprzez funkcje magiczne __get() oraz __set(). Są to funkcje wywoływane w klasie w momencie kiedy próbujemy dostać się do nieistniejącej zmiennej w klasie. Na potrzeby rejestru, stworzyłem 3 proste klasy o nazwach A, B i C, których zadaniem jest coś wyświetlić.

 Implementacja wzorca Rejestru w PHP

class A
{
   public function sayA()
   {
     echo 'a';
   }
}
class B
{
   public function sayB()
   {
     echo 'b';
   }
}
class C
{
   public function sayC()
   {
     echo 'C';
   }
}

Następnie przechodzę do tworzenia rejestru. W poprzednim artykule opisywałem wzorzec Singleton, z którego teraz skorzystam. Ponadto będzie nam potrzebna tablica, która przechowa obiekty w rejestrze. Tablica zadeklarowana jest jako static ponieważ potrzebujemy wyłącznie jednej tablicy.

Najważniejszymi metodami są metody __get oraz __set, które odpowiadają za rejestrację obiektu i jego pobranie.

Metoda __get() sprawdza czy obiekt istnieje w rejestrze i jeśli tak to go zwraca, a jeżeli nie istnieje to wyrzuca wyjątek, że obiekt nie istnieje.

Metoda __set() zapisuje obiekt($value) w rejestrze pod nazwą $object. W tej metodzie powinniśmy dodać sprawdzanie czy obiekt już istnieje w tablicy, na podobnej zasadzie jak w metodzie __get(), ale w tym prostym przykładzie to pominę.

Implementacja klasy Rejestr w PHP

class Registry
{
   private static $instance;

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

   private static $objects = array();

   private function __construct()
   {

   }

   public function __get($key)
   {
      if(isset(self::$objects[$key]) == false)
	  {
	     throw new Exception('<br>Brak obiektu w rejestrze<br>');
	  }
	  return self::$objects[$key];
   }
   public function __set($object, $value)
   {
      self::$objects[$object] = $value;
   }
}

Przykładowe użycie wzorca

Gdy już mamy zaimplementowany wzorzec rejestru możemy przetestować jak on działa.  Tworzymy blok try catch ponieważ jedna z metod wyrzuca wyjątek. W bloku try tworzymy obiekty klas A, B oraz C. Pobieramy instancję rejestru.
Następnie zapisujemy pod nazwą a obiekt klasy A i analogicznie robimy to samo dla klas B oraz C. W następnych linia pobieramy obiekt B z rejestru do zmiennej  $objectToTest oraz wywołujemy na nim jego metodę sayB(). Następnie wywołujemy funkcję doSomething(), która pobiera z Rejestru obiekt A i wywołuje jego metodę sayA(). Na końcu pobieramy obiekt D, który nie istnieje i zostanie rzucony wyjątek, który wyświetlamy.

try
{
   $objA = new A();
   $objB = new B();
   $objC = new C();

   $registry = Registry::getInstance();

   $registry->a = $objA;
   $registry->b = $objB;
   $registry->c = $objC;

   $objectToTest = $registry->b;
   $objectToTest->sayB();
   doSomething();

   //dostep do niezarejsowanego obiektu
   $registry->d; 
}
catch(Exception $e)
{
   echo $e->getMessage();
}

function doSomething()
{
    Registry::getInstance()->a->sayA(); 
}

Podsumowowanie

Wzorzec rejestru bardzo ułatwia programowanie jednakże, nie powinien być nadużywany. Kod, który napisałem można rozwinąć poprzez implementacje interfejsów: Iterator, ArrayAccess, Countable, Serializable oraz lepszą obsługę wyjątków, powinniśmy też dodać metodę wyrejestrowującą obiekt z rejestru. Jako bardzo dobrą implementacje rejestru mogę polecić klasę Zend_Registry. Wchodzącą w skład frameworka Zend.

Działający kod