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ę.
Witam wszystkich w prologu kursu. Czym będziemy się tutaj zajmować? Postaram się przekazać w przyjazny oraz co najważniejsze - w praktyczny sposób wiedzę na temat programowania w PHP 5. Kurs ten skierowany jest raczej do średnio-zaawansowanych programistów, nie będziemy tutaj omawiać podstawowej składni języka, operatorów etc. Zakładam, że osoby zainteresowane tym kursem posiadają podstawową wiedzę na ten temat. Zatem - zaczynajmy!

Na wstępie postaram się wyjaśnić, dlaczego obiektem naszego zainteresowania będzie napisanie własnego frameworka. Pisząc różnorodne aplikacje, wyrabiamy sobie pewne nawyki programistyczne - dotyczy to zarówno nazewnictwa plików, budowania struktury katalogów czy sposobu przechowywania danych konfiguracyjnych. Przystępując do pracy nad każdym nowym projektem, niejako automatycznie sięgamy do tych samych rozwiązań, przez co możemy wyodrębnić je i stworzyć na ich podstawie tzw. szkielet aplikacji. Dopełnienie go odpowiednim dla danego projektu kodem, pozwoli na stworzenie pełnej aplikacji. Tak oto w kilku słowach można najprościej zdefiniować pojęcie frameworku.

Wielu w tym miejscu mogłoby zadać pytanie - po co pisać własny framework, jeżeli dostępnych mamy wiele darmowych frameworków, napisanych przez zespoły programistów i przetestowanych przez społeczności ich używających? Odpowiedź na to pytanie jest prosta - nie będziemy tutaj wyważać otwartych drzwi. Celem tego kursu jest przedstawienie pewnej koncepcji programowania obiektowego w języku PHP 5, jak i również m.in. zapoznanie czytelnika z wzorcami projektowymi. Kurs ten należy traktować wyłącznie jako praktyczny trening. Po tym dość długim wstępie, czas najwyższy przejść do konkretów.

Przygotowujemy warsztat pracy

Zanim przejdziemy do implementacji, zajmiemy się kilkoma, czysto technicznymi sprawami:
  • strukturą katalogów i nazewnictwem plików,
  • przygotowaniem wirtualnego hosta do testowania kodu,
Struktura katalogów została zaprezentowana na poniższym obrazku:


Katalog /framework jest naszym katalogiem nadrzędnym, zawiera dwa podkatalogi - apps oraz core. W pierwszym z nich będą przechowywane katalogi aplikacji, tworzonych na bazie frameworka (na powyższym obrazku mamy przedstawioną strukturę katalogów przykładowego projektu - first_app). Każda aplikacja zawierać będzie katalogi:
  • cache (tutaj będą przechowywane pliki cache),
  • config (pliki konfiguracyjne),
  • controllers (kontrolery aplikacji),
  • models (modele aplikacji),
  • views (widoki aplikacji),
  • web (pliki dostępne z poziomu przeglądarki),
  • templates (pliki szablonów)
Dodatkowo katalog web zawierać będzie dwa podkatalogi - frontend i backend, dzielące aplikację na część użytkową oraz administracyjną, odpowiednio. Ponadto znaczenie każdego z tych katalogów zostanie dokładnie wyjaśnione w dalszej części, gdy będziemy zajmować się implementacją odpowiednich elementów.

Mając przygotowaną strukturę katalogów, zajmiemy się stworzeniem wirtualnego hosta. Zakładam, że serwer Apache jest już zainstalowany na komputerze czytelnika, przejdziemy zatem do opisania procedury tworzenia wirtualnego hosta (jeżeli serwer Apache nie jest jeszcze zainstalowany, odsyłam do źródeł lub skorzystania z ulubionej wyszukiwarki). Jako użytkownik linuxa, opiszę metodę dla tego systemu, a dokładniej - dystrybucji Ubuntu:

Krok 1: tworzymy plik [nazwa_wirtualnego_hosta].conf w lokalizacji /etc/apache2/sites-available (w naszym przykładzie utworzymy host o nazwie front-framework):

# sudo vim /etc/apache2/sites-available/front-framework.conf

Krok 2: uzupełniamy utworzony plik konfiguracyjny:

<VirtualHost *>
ServerName front-framework
DocumentRoot /home/simons/framework/apps/firs_app/web/frontend
</VirtualHost>

Uwaga: w sekcji DocumentRoot podajemy poprawną, lokalną ścieżkę!

Krok 3: ładujemy plik konfiguracyjny:

# sudo a2ensite front-framework.conf

Krok 4: dodajemy wirtualnego hosta do pliku /etc/hosts

# sudo vim /etc/hosts

Do pliku dodajemy na końcu:

127.0.0.1 front-framework

Zapisujemy i zamykamy plik.

Krok 5: przeładowujemy Apache:

# sudo /etc/init.d/apache2 reload

Po przejściu przez te 5 kroków możemy cieszyć się nowo utworzonym hostem - wpisujemy w przeglądarce adres http://front-framework, aby sprawdzić poprawność konfiguracji. Jeżeli wszystko poszło dobrze, w oknie przeglądarki powinniśmy zobaczyć pusty listing plików katalogu frontend.

Podsumowanie

We wstępie do kursu udało nam się przygotować dwie, bardzo istotne rzeczy - strukturę katalogów oraz wirtualnego hosta, na którym będziemy testować kod.
Na koniec jeszcze kilka słów wyjaśniających naszą strukturę katalogów. Jednym z głównych czynników, jakie kierowały mną przy jej ustalaniu było bezpieczeństwo. Zwróćmy uwagę, że z poziomu przeglądarki użytkownicy nie będą mieć możliwości odczytania zawartości katalogów innych niż ten, na który wskazuje domena - w naszym przykładzie jest katalog frontend. Dzięki temu pliki konfiguracyjne i źródłowe pozostają bezpieczne.
Drugą rzeczą, na którą chciałbym zwrócić uwagę, to struktura katalogu apps. Znajdować się w nim będą katalogi aplikacji, ułożone w odpowiednich katalogach. Dzięki temu rozwiązaniu, wiele aplikacji będzie mogło współdzielić pliki źródłowe naszego frameworka w ramach jednego serwera. Dla przykładu wyobraźmy sobie, że napisaliśmy 10 aplikacji na bazie frameworka. Wszystkie aplikacje przechowywane będą na jednym serwerze, zatem struktura katalogu apps będzie prezentować się następująco:

apps/
   app_1/
   app_2/
   app_3/
   ...
   app10/

Jak łatwo zauważyć, nie trzeba będzie dziesięciokrotnie kopiować katalogu core/ z plikami źródłowymi frameworka. Zyskujemy dzięki temu jeszcze jedną ważną rzecz - w przypadku wprowadzenia istotnych zmian w kodzie źródłowym, zmiany będą widoczne od razu we wszystkich aplikacjach współdzielących kod.

W następnym odcinku zajmiemy się już implementacją podstawowych elementów frameworka. Skupimy naszą uwagę na programowaniu obiektowym, wykorzystamy możliwości jakie daje nam PHP w wersji piątej. Do zobaczenia wkrótce!
Od dłuższego już czasu nosiłem się z zamiarem rozpoczęcia redagowania własnego bloga - do tej pory brakowało niestety odpowiedniej ilości czasu, aby słowa wprowadzić w czyn. Jednak z tą oto chwilą, a właściwie tym pierwszym wpisem, rozpoczynam swoją przygodę z blogowaniem.

O czym będę pisać?

Przede wszystkim chciałbym podzielić się swoją wiedzą z zakresu programowania aplikacji webowych (głównie w języku PHP 5), jednak postaram się na tym nie kończyć i poruszyć kilka około-programistycznych tematów.

Kończąc ten krótki, kilku zdaniowy wstęp, chciałbym zaprosić wszystkich do czytania i komentowania treści, jakie będą się tutaj pojawiać. Mam nadzieję, że znajdzie się choć jedna osoba, która znajdzie tutaj coś ciekawego dla siebie.
Pozdrawiam!