Pewnego dnia, jadąc w nocy na rowerze zamyśliłem się na temat klas, funkcji i obiektów. Przemyślenia te zawiodły mnie do pewnych ciekawych wniosków.
Nie będę ich tutaj w tej chwili przytaczał, natomiast wpadł mi do głowy pewien ciekawy pomysł jak zrealizować wzorzec singleton w alternatywny sposób, opierając się na funkcjach anonimowych.
Czym jest wzorzec Singleton ? Pokrótce mówiąc, stosujemy go jeżeli a) chcemy mieć tylko jedną instancję obiektu, b) jeżeli chcemy aby nasz obiekt był globalnie dostępny.
Jednym z wzorców, który korzysta z singletona jest rejestr, większość z Was pewnie używała go w Zendzie gdzie jest nagminnie wykorzystywany. Dla przypomnienia:
//inicjalizacja singletona
$oReg = Zend_Registry::getInstance();
//ustawienie zmiennej w rejestrze
$oReg->jakis_klucz = 'jakas wartosc';
//pobranie
echo $oReg->jakis_klucz;
Jedną z rzeczy, którą uważam za złą w tej implementacji od strony filozoficznej jest to, że klasy generalnie definiujemy po to by tworzyć wiele instancji danego obiektu. W tym przypadku potrzebujemy tylko jeden egzemplarz, stosując klasy trzeba używać więc dodatkowych sztuczek. Natomiast zamiast tworzyć klasę i bawić się w Singletona z klasą rejestru stworzyć taką oto zgrabną funkcję:
function Registry($key, $value = null){
static $reg;
if($value === null){
return $reg[$key];
} else {
$reg[$key] = $value;
}
}
//ustawianie
Registry('a','b');
Registry('d','e');
//pobieranie
echo Registry('a');
echo Registry('d');
Jak widać implementacja jest prosta jak budowa czołgu t-52 i realizuje wszystkie założenia rejestru – jedna instancja i globalna dostępność elementów które przechowuje.
Przejdźmy dalej i spróbujmy zaimplementować Singletona w podobny sposób, ale już jako konkretny obiekt z metodami.
Klasyczne podejście wygląda mniej więcej tak:
class My_Singleton {
protected static $_instance = null;
private $_prywatna1 = 'aaa';
private $_prywatna2 = 'bbb';
protected __construct(){
}
public getInstance(){
if(self::$_instance === null){
self::$_instance = new self;
}
return self::$_instance;
}
public function method1(){
echo $this->_prywatna1.' <br/>';
$this->_prywatna2 = 'ccc';
}
public function method2(){
echo $this->_prywatna2.' <br/>';
}
}
My_Singleton::getInstance()->method1();
My_Singleton::getInstance()->method2();
Alternatywne podejście:
class Proxy {
function __call($name, $arguments) {
if(is_callable($this->$name)){
return call_user_func_array($this->$name, $arguments);
}
}
}
function Singleton() {
static $self;
//konstruktor pol
if($self == null) {
$self = new stdClass();
$self->_prywatna1 = 'aaa';
$self->_prywatna2 = 'bbb';
}
//konstruktor metod
static $oObj;
if($oObj == null) {
$oObj = new Proxy();
$oObj->method1 = function() use ($self) {
echo $self->_prywatna1.' <br/>';
$self->_prywatna2 = 'ccc';
};
$oObj->method2 = function() use ($self) {
echo $self->_prywatna2.' <br/>';
};
}
return $oObj;
}
Singleton()->method1();
Singleton()->method2();
Technika przedstawiona powyżej jest bardzo podobna do programowania opartego na prototypach, z jakim mamy np do czynienia w JavaScript’cie. Jedyną różnicą w ciele metod „metoda1″ i „metoda2″ jest zastąpienie zmiennej $this, zmienną $self, spowodowane tym, że $this jest zastrzeżoną nazwą zmiennej. Przykład spełnia założenia Singletona – jedna instancja i dostępność w kontekście globalnym. Jedyną wadą tego rozwiązania jest to, że nie da się dziedziczyć po takim obiekcie, mam wrażenie jednak, że nie jest to koniecznie potrzebne.
Ciekawy jestem Waszego zdania o tej technice, zapraszam do komentowania
P.S przykłady działają tylko w PHP 5.3.x
Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...
Zwiń
Czytaj na blogu autora...