Tak więc przyszedł czas by omówić klasę dispatcher’a (dyspozytora), jest to pewien wzorzec projektowy, dzięki któremu będziemy mogli zarządzać zdarzeniami. Tak projektowanie oparte na zdarzeniach, jak pewnie łatwo jest się Wam domyślić, gdy piszę o zdarzeniach myślę o przesyłanych przez url get’ach. Kiedy nasza fantazja prowadzi nas ku lepszym i co raz to abstrakcyjnym rozwiązaniom, będziemy mieli umysł otwarty na nowe być może lepsze oraz na pewno ciekawe rozwiązania.
Wracając do tematu, dispatcher, odpowiada za:
- sprawdzenie czy klasa istnieje
- załadowanie tej że klasy
- wywołanie metody z klasy
- łapanie błędów – to akurat u mnie [;
Akurat dzisiaj nie przedstawię Wam żadnego kodu, tylko odeślę do zbioru klas, na stronie phpclasses. Link Dispatcher
Nie zachęcam do jakiegoś głosowania czy coś. Zachęcam do działania!
Jako iż jeszcze moderatorzy z phpclasses.org nie zaakceptowali mojej klasy (nie wiadomo czemu) wrzucam tutaj kod:
Rdzeń systemu jest oparty o plik Dispatcher.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | <?php /** * @li gnu/agpl v3 or later * @code utf8 * @version 0.1 * @author cojack from Aichra.pl * @date 22.09.09 * **/ /** * We require a event handler */ require_once('HandlerShow.php'); /** * @class Dispatcher * @throw Exceptions * @access final * * Can't extend from it, it's a finall class. * Class auto call a handled class "name" from event. * */ final class Dispatcher { private $_handle; private $_response; /** * @method __construct * @access public * @param string a event name * @return void * * Method set a event. * */ public function __construct($event) { $this->_handle = $event; } /** * @method handleEvent * @access public * @param void * @return void * * That event handle is a class "name". * Method check that handle exist. * */ public function handleEvent() { try { $name = 'Handler'.ucfirst($this->_handle); if ( class_exists("$name") ) { $handObj = new $name($this->_handle); $this->_response = $handObj->handledEvent(); } else { throw new Exception ('Can\'t be handled this Event'); } } catch (Exception $e) { printf ('Error: %s',$e->getMessage()); } } /** * @method getResponse * @access public * @param void * @return plain text etc.. * * Method return a _response. * */ public function getResponse() { return $this->_response; } } |
Jak widać nie zwracam wyniku z załadowanego eventu wprost do głównego pliku, dlaczego?
A no dlatego że ostatnio metaxy zaciekawił mnie systemem szablonów OPT 2, i w oparciu o niego i jego filozofie a jest dosyć ciekawa tworzę aichra lite cms oraz w oparciu o Doctrine, ale nie teraz o tym mowa, chodzi o to że nie muszę od razu w pliku wynikowym zwracać i generować treści, tylko dopiero na końcu, czyli tam gdzie chce, a nie tam gdzie być powinna. Dlatego odpowiedź jest przypisana do zmiennej a nie zwracana.
Klasa jest b.prosta wczytujemy w niej 1 plik bo tylko mamy w tym przykładzie jedną akcję, chodź ja osobiście bym ten plik wczytywał gdzieś indziej i było by to bardziej dynamicznie, ale na potrzeby tej klasy zrobiłem to tak.
A teraz interfejs oraz klasa abstrakcyjna dla eventów:
Plik: Handled.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?php /** * @li gnu/agpl v3 or later * @code utf8 * @version 0.1 * @author cojack from Aichra.pl * @date 22.09.09 * **/ /** * @interface Handled * */ interface Handled { /** * @method handedEvent() * @param void * @access abstract * */ public function handledEvent(); } |
Nie ma co tu dużo gadać, interfejs to interfejs, implementując go, zgadzamy się na przestrzeganie jego warunków jakimi są zaimplementowanie w identyczny sposób metod w jaki są przedstawione w interfejsie.
Idźmy dalej, klasa abstrackcyjna:
Plik: EventHandler.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <?php /** * @li gnu/agpl v3 or later * @code utf8 * @version 0.1 * @author cojack from Aichra.pl * @date 22.09.09 * **/ /** * We require a Handled interface */ require_once('./Handled.php'); /** * @class EventHandler * @implements Handled * @access abstract * * We'll extending from this class, * so for example we can here storage a function with access protected to connect to db * and we have to have a abstract function implemented from interface Handled a handledEvent() * */ abstract class EventHandler implements Handled { /** * @method dbConn * @access protected * @param void * @return resource - db handle * * Method to connect to db * */ protected function dbConn() { // some connection to db $dbHandle = true; // only for example return $dbHandle; } /** * @method handedEvent() * @param void * @access abstract * */ public function handledEvent() {} } |
Co tutaj mamy, w tejże klasie abstrakcyjnej, możemy utworzyć sobie metody takie jak powyżej mamy, dobrym przykładem jest zastosowanie połączenia w ten że sposób z bazą danych, ja tego nie używam ponieważ Doctrine robi to za mnie i jest mi to zbyteczne, nie każdy pracuje na Doctrine i może mu się to spodobać. Co ciekawe klasa implementuje interfejs i musi posiadać wszystkie metody z interfejsu, tak też jest zrobione, metoda w klasie abstrakcyjnej nie może być abstrakcyjna oraz musi zawierać ciało, czyli treść, sprytnym sposobem parsera php omineliśmy to dzięki {} pustemu ciału.
Teraz mamy już główne pliki dla naszego dyspozytora, ot cała filozofia, a teraz do dzieła, jak to odpalić:
Plik: index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?php /** * @li gnu/agpl v3 or later * @code utf8 * @version 0.1 * @author cojack from Aichra.pl * @date 22.09.09 * **/ require_once ('Dispatcher.php'); $disObj = new Dispatcher($_GET['event']); $disObj->handleEvent(); //tpl etc... // ... // ... // using some templates (Smarty here) //$tpl->assign('content',$disObj->getResponse()); // or plain text echo $disObj->getResponse(); // set the ulr to index.php?event=show // else code // ... |
W komentarzach wyjaśnienie co do czego i dlaczego
A teraz przykładowy event,
plik: HandlerShow.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <?php /** * @li gnu/agpl v3 or later * @code utf8 * @version 0.1 * @author cojack from Aichra.pl * @date 22.09.09 * **/ /** * We require a Event Handler abstract class */ require_once('EventHandler.php'); /** * @class HandlerShow * * An example of usage a dispatcher, ofcourse it's not good idea to static require a event handler in dispatcher * */ class HandlerShow extends EventHandler { private $_handle; public function __construct($event){ $this->_handle = $event; } public function handledEvent(){ // so for example we can here get a db handle from extended abstract class, like it: $dbHandle = parent::dbConn(); // here we can also trows exceptions, and it'll be catched in Dispatcher // also we can call a method from model to get a some data from db /* $articlesObj = new ArticlesModel($dbHandle); return $articlesObj->getAllArticles(); */ // or just for example we will print some text return ('Hello World'); } } |
To o czym mówiłem, napisałem również tutaj w komentarzu, o ładowaniu eventów w dispatcher (dyspozytorze).
A teraz o co chodzi, w pliku index.php przekazujemy event jako $_GET do konstruktora dyspozytora, oczywiście pierw powinniśmy sprawdzić czy owy event działa, to wszystko można by jeszcze pięknie oprzeć o router, który jest w planach rozwojowych. Ale wszystko w swoim czasie, w metodzie handleEvent() sprawdzamy czy dana klasa istnieje, jeżeli tak to tworzymy jej instancję oraz wywołujemy metodę: handledEvent() gdzie od razu wynik jej jest zapisywany do $_response, czyli w tej że metodzie musimy posiadać return.
Ot cała filozofia, powiem Wam że pomocne i ujednolicające proces tworzenia aplikacje narzędzie.


%H:%i
Fajnie… Napisałeś się, ale chyba nigdzie nie zdradziłeś do czego Ci jest potrzebny _TAKI_ dispatcher? Do czego go wykorzystujesz? Pamiętaj, że opisujesz wzorce, czy mało konkretny, ale w pełni dojrzałe rozwiązanie stawianego problemu. Konkretny kod wykracza poza definicję wzorca i narzuca implementację, a jeśli narzuca to trzeba wyjaśnić dlaczego. Dispatcher nie jest wzorcem pokroju singletona, którego ilość implementacji jest bardziej ograniczona, Tak samo jak ilość zastosowań dispatchera jest nie współmiernie większa. Dlaczego korzystasz tutaj z „event’ów” i czym one są w praktyce? Dlaczego zarządzasz nimi bezpośrednio z pod dispatchera? Wbrew pozorom odpowiedzi nie są oczywiste, a dlaczego? Przemyśl to…