O Singletonie raz jeszcze

piątek, 12 września 2008

O Singletonie napisano już chyba wszystko. Ten, jakby nie patrzeć, wzorzec projektowy wzbudza bardzo wiele kontrowersji - ma tylu samo przeciwników co i zwolenników. Osobiście zaliczam się do tej drugiej grupy, choć niektóre argumenty jego przeciwników uważam za całkiem słuszne. No ale do rzeczy.

Singleton - filozofia

Wyobraźmy sobie sytuację, w której piszemy klasę obsługi bazy danych. W jej konstruktorze wywoływana jest metoda odczytująca z pliku konfiguracyjnego dane, potrzebne do nawiązania połączenia z bazą. Dla lepszego zobrazowania problemu, załóżmy że plik konfiguracyjny ma strukturę dokumentu XML, którego odczytanie wymaga odwołania się do odpowiednich klas (np. DOMDocument). Dodatkowo niech nasz konstruktor nawiązuje połączenie z bazą, od razu po odczytaniu potrzebnych danych. Jak widzimy, operacji do wykonania jest całkiem sporo.
W sytuacji, gdy będziemy musieli odwoływać się do obiektu tej klasy w wielu miejscach, dodatkowo w różnych od siebie plikach naszej aplikacji, każdorazowe tworzenie tego obiektu będzie bardzo nieefektywne. Z logicznego punktu widzenia, najlepszym rozwiązaniem byłoby utworzyć raz instancję tej klasy i używać jej później w miarę potrzeb. Takie rozwiązanie umożliwia właśnie Singleton.

Singleton - implementacja



class Singleton
{
private static $_oInstance = NULL;

private function __construct() { }

private function __clone() { }

public static function getInstance()
{
$className = get_class();

if (is_null(self::$_oInstance)) return self::$_oInstance = new $className;

return self::$_oInstance;
}
}

Implementację Singletonu można opisać w kilku punktach:

  • zablokowanie modyfikatorem private konstruktora i metody kopiującej obiekt,
  • przygotowanie prywatnego statycznego pola, które będzie przechowywać instancję klasy,
  • przygotowanie publicznej statycznej metody getInstance(), która zwracać będzie obiekt - jeżeli jest to pierwsze wywołanie, tworzy obiekt i zwraca go, zapisując do pola statycznego; jeżeli pole statyczne przechowuje już obiekt, metoda zwraca ten właśnie obiekt,
Jak widać, zamiast tworzyć w tradycyjny sposób obiekty (przy pomocy operatora new), instancje klas pobieramy przy pomocy naszej publicznej metody getInstance(). W przypadku powyższej klasy, byłoby to tak:

$singleton = Singleton::getInstance();


Dzięki temu mamy zagwarantowane, że zawsze będziemy mieć jedną jedyną instancję określonej klasy (poprzez modyfikator private przy konstruktorze, niemożliwe będzie utworzenie obiektu przy pomocy operatora new).

Plusy i minusy

Główną zaletę Singletonu opisałem w powyższym przykładzie - nasz kod zyskuje na wydajności, dzięki ograniczonej liczbie wywołań konstruktora klasy. Przeciwnicy tego rozwiązania, przedstawiają zawsze jeden główny kontrargument - Singleton ukrywa pod obiektową otoczką zmienną globalną (a jak wiadomo, stosowanie zmiennych globalnych w programowaniu obiektowym jest bardzo nieeleganckie). Dobrze, załóżmy że zgodzimy się z tym argumentem. W tym miejscu zawsze na myśl przychodzi mi bardzo prosta riposta - Ci sami przeciwnicy Singletonu, używają w swoim kodzie zmiennych sesyjnych, które z logicznego punktu widzenia są zmiennymi super globalnymi (!). Nie mam nic przeciwko zmiennym sesyjny (gwoli wyjaśnienia), ich stosowanie jest bardzo powszechne (m.in. w mechanizmach uwierzytelniania). Z tego właśnie punktu widzenia, argumenty przeciwników Singletonu moim zdaniem tracą na znaczeniu.

Wzorcem zbliżonym swoją filozofią do Singletonu, jest wzorzec Fabryka, o którym tutaj jeszcze napiszę.

0 komentarze: