Professional Documents
Culture Documents
Nazwa wzorca projektowego dekorator jest nieco mylca, poniewa sugeruje, e bdziemy co wzbogaca, dekorowa czy upiksza. Nic bardziej bdnego! Omawiany wzorzec znajduje szerokie zastosowanie, niezalenie od tego, czy projektujemy warstw dostpu do bazy danych, logik biznesow lub kontroler MVC.
J
W SIECI
http://flexi.sourceforge.net/ budowany przez nas framework http://www.picocontainer.org/ Ports Pico http://www.martinfowler.com/ articles/injection.html ciekawe artykuy http://www.phppatterns.com/ ciekawe artykuy
ednym z najczciej omawianych i uywanych wzorcw projektowych jest dekorator. Z punktu widzenia programisty ma same zalety: jest bardzo uyteczny, nieskomplikowany w implementacji i atwy do przyswojenia. Obok singletona jest to prawdopodobnie jeden z pierwszych wzorcw, z ktrymi stykamy si, gdy zaczynamy studiowa przykady i literatur powicon zasadom projektowania. O ile jednak singleton jest obwiniany (czsto susznie!) o promowanie fatalnych rozwiza, to dokadne poznanie dekoratora przyczynio si do powstania wielu ciekawych rozwiza architektonicznych. Nazwa wzorca projektowego "dekorator" jest nieco mylca, poniewa sugeruje, e bdziemy co wzbogaca, dekorowa czy upiksza. Std moe powsta bdne przewiadczenie, e mamy do czynienia z tworem przydatnym jedynie w warstwie prezentacji. Nic bardziej bdnego! Omawiany wzorzec znaj-
duje szerokie zastosowanie, niezalenie od tego, czy projektujemy warstw dostpu do bazy danych, logik biznesow lub kontroler MVC. Jego istot jest moliwo wzbogacenia funkcjonalnoci obiektw danych klas w sposb dynamiczny, bez koniecznoci modyfikowania oryginalnych obiektw. Definicje jak zwykle brzmi zbyt sucho, spjrzmy wic na Listing 1, gdzie
Co naley wiedzie...
Przydatna bdzie znajomo dwch poprzednich artykuw z naszej serii Wzorce projektowe. Czytelnik powinien mie wiedz z obiektowych technik projektowania aplikacji.
Co obiecujemy...
Z artykuu dowiesz si, kiedy i dlaczego warto stosowa wzorzec projektowy dekorator. Na przykadach pokaemy, jak stosujc dekorator, bardzo atwo i w elegancki sposb doda now funkcjonalno do aplikacji bez zbdnej modyfikacji kodu.
www.phpsolmag.org
Techniki
znajduje si elementarny przykad uycia dekoratora. W przedstawionym przypadku uylimy dekoratora do wzbogacenia istniejcej funkcjonalnoci. Nie musimy jednak zbyt cile sugerowa si nazw wzorca projektowego i spokojnie moemy pozwoli sobie na cakowit zmian funkcjonalnoci dekorowanego obiektu. Dwa proste przykady, przedstawione na wspomnianych listingach, obrazuj praktycznie wszystkie istotne cechy wzorca, z ktrym si zaznajamiamy. Szczeglne istotny do odnotowania jest jeden fakt: dla klienta korzystajcego z udekorowanego (czsto mwimy rwnie - "opakowanego") obiektu obecno dekoratora jest zupenie przezroczysta! Klient nie jest w stanie stwierdzi, czy korzysta z usug oryginalnego obiektu, czy te z jego udekorowanej wersji. Jest to moliwe dziki zachowaniu przez dekorator interfejsu oryginalnego obiektu. Jeli chcielibymy zapamita jak jedn zasad czy zdanie opisujce dekoratory, mogoby to by: Dokorator zachowuje interfejs oryginalnego obiektu i najczciej przyjmuje dekorowany obiekt jako parametr w swoim konstruktorze. Kolejn bardzo mi cech dekoratora jest moliwo uycia wicej ni jednego "opakowania" dla podstawowego obiektu. Oznacza to, i moemy skada now funkcjonalno z wielu ju istniejcych elementw, bez koniecznoci ingerencji w raz stworzony kod. Jest to dokadnie ta sama filozofia, jak spotykamy przy pracy z powok w systemach uniksowych. Doskonale wiemy, jak wiele poytecznych operacji mona wykona, zaprzgajc do wsplnej pracy elementarne narzdzia. To samo odnosi si do dekoratorw: sprytnie czc wiele prostych dodatkw moemy uzyska funkcjonalno, o ktrej nie nio si nawet twrcom piszcym klasy czy te dekoratory (Listing 2). Zanim przejdziemy do omwienia bardziej skomplikowanych przykadw, zatrzymajmy si jeszcze na chwil nad wasnoci dekoratorw, dziki ktrej klient nie jest w stanie rozrni, czy ma do czynienia z obiektem podstawowym, czy te z opakowan jego wersj. Uwany Czytelnik zauway, e ta nierozrnialno jest moliwa, poniewa pomidzy dekoratorem i obiektem dekorowanym zachodzi taka sama relacja ("jest",
ang. IS-A), jak przy dziedziczeniu. Istotnie, przykad z Listingu 2 mona by zapisa tylko i wycznie przy pomocy tworzenia nowych podklas (Listing 3). Jaki jest wic sens wprowadzania zupenie nowej konstrukcji programistycznej i opisywania jej jako wzorca projektowego? Przecie to samo mona uzyska przy uyciu podstawowych konstrukcji programowania obiektowego. Jest jedna, dosy istotna rnica pomidzy dwoma wspomnianymi podejciami. Aby j atwo pokaza, przemodelujmy przykad z Listingu 2, zmieniajc kolejno zastosowanych dekoratorw (Listing 4). Prosz bardzo: caa operacja bya wyjtkowo prosta, odbya si bez koniecznoci modyfikowania istniejcego kodu i dopro-
wadzia do powstania nowej funkcjonalnoci. Niestety, w przypadku dziedziczenia mamy duo trudniejsze zadanie: nie obdzie si bez dosy istotnego przemeblowania napisanego kodu. Powodem jest fakt, i formujc hierarchi dziedziczenia tworzymy do sztywne, statyczne powizanie pomidzy poszczeglnymi klasami. W przypadku dekoratorw nie mamy takiego ograniczenia: moemy dowolnie przestawia kolejno, dodawa nowe elementy do acucha i usuwa ju istniejce. Takie zamiany s moliwe nie tylko w momencie ustalania struktury kodu, ale w dowolnym momencie, nawet w czasie wykonywania skryptu. Dziki dekoratorom mona uzyska wszystkie dobrodziej-
www.phpsolmag.org
Techniki
stwa dziedziczenia, ale w sposb bardziej elastyczny. Nie oznacza to oczywicie, i namawiamy Was, by od dzisiaj nie uywa dziedziczenia i pisa tylko dekoratory. Jak kady wzorzec projektowy, dekorator nie powinien by stosowany tylko w celu otrzymania bardziej elastycznej architektury, ale w przypadkach, w ktrych bdzie naprawd pomocny. Uzbrojeni w solidne podstawy teoretyczne moemy przyjrze si wreszcie przykadom pokazujcym, jak w atwy i efektywny sposb rozbudowa nasze aplikacje bez koniecznoci modyfikacji ich podstawowego kodu. Wrmy zatem do przykadw. Jedn z dobrych praktyk architektonicznych jest dzielenie kodu aplikacji na warstwy. W typowej aplikacji WWW, najnisz warstw stanowi baza danych (najczciej w postaci relacyjnej bazy danych lub zwykych plikw). Powyej umieszczamy wartw dostpu do danych w postaci obiektw DAO (ang. Data Access Objects). Ju ta jedna wspomniana warstwa jest okazj do zaimplementowaniu wielu funkcjonalnoci w postaci dekoratorw. Wyobramy sobie, e chcemy zapisywa czas wyszukiwania danych w DB. Nic prostszego: zamiast modyfikowa istniejcy kod, otoczmy go dodatkow klas (Listing 5). A moe mamy DAO suce do zapisu danych uytkownikw i w pewnych przypadkach chcemy szyfrowa hasa. Prosz bardzo: zamiast utrzymywa dwie zblione wersje tego samego DAO, duo efektywniej rozwiemy problem stosujc prosty dekorator bez modyfikowania i duplikowania istniejcego kodu. Przykady mona mnoy rwnie w innych warstwach sytemu. Jeli pomylimy o dowolnej logice biznesowej zapisujcej dane, to dekoratory znajd zastosowanie przy wersjonowaniu danych, zapisywaniu logw zmian, kontroli dostpu itd. Przesuwajc si jeszcze jedn warstw wyej, do kontrolerw MVC, szybko odkryjemy kolejne zastosowania: ledzenie zachowa uytkownikw (np. cieki poruszania si po serwisie WWW) czy sprawdzanie uprawnie. Zastosowania i przykady mona by mnoy w nieskoczono. Poprzestamy wic na stwierdzeniu, e wzorzec dekoratora jest niezwykle uyteczny i podpowiada eleganckie rozwizania dla czsto spotykanych problemw zwizanych z dodawaniem specyficznej funkcjonal-
woanym przykadem zastosowania dekoratorw do dodania obsugi uprawnie na poziomie warstwy kontrolerw MVC. Listing 6 pokazuje dwa przykadowe kontrolery oraz opakowanie z dekoratorw sprawdzajcych uprawnienia. Przedstawiony schemat dodawania obsugi uprawnie do aplikacji jest niezwykle efektywny. Po pierwsze, pozwala uruchamia t sam aplikacj ze sprawdzaniem uprawnie i bez, w zalenoci od wymaga klienta.
Listing 4. Zmieniona kolejno zastosowanych dekoratorw w stosunku do Listingu 2. Tworzymy now funkcjonalno bez modyfikacji istniejcego kodu.
<?php $dao = new UserDAOPasswordHashingDecorator(new UserDAOUpperCaseLoginDecorator( new UserDAOImpl())); ?>
www.phpsolmag.org
Techniki
Rysunek 1. Uproszczony graf obiektw w naszej aplikacji Z drugiej strony, taka architektura umoliwia niezalene pisania kodu biznesowego i odpowiedzialnego za bezpieczestwo. Wreszcie, moemy sobie wyobrazi zastosowanie rnych silnikw do sprawdzania uprawnie (np. wasny lub GACL). Niestety, nasze wszystkie zachwyty troch przybledn, jeli uwiadomimy sobie, e dla kadego kontrolera trzeba stworzy osobny dekorator. Nie jest to moe wielkim problemem przy piciu czy dziesiciu kontrolerach, ale staje si koszmarem przy duych aplikacjach, gdzie czsto spotkamy setki klas typu kontroler. Nasze miny stan si jeszcze bardziej nietgie, jeli uwiadomimy sobie, e kady z dekoratorw bdzie do siebie bardzo podobny. Ponowny rzut oka na Listing 6 faktycznie ujawnia, e w kolejnych dekoratorach rni si tylko nazwy klas i metod zasadnicza logika pozostaje praktycznie bez zmian. Bezmylne pisanie niemal identycznych dekoratorw nie jest zajciem szczeglnie ciekawym czy produktywnym, a dodatkowo to oczywista duplikacja kodu. Musimy wic znale jaki sposb na zautomatyzowanie mudnego zajcia. PHP5, jako jzyk niezwykle dynamiczny daje nam szerokie moliwoci generowania szablonowego kodu w locie. Spord wielu opcji dwie wydaj si najciekawsze: uycie magicznej metody __call lub generowanie caego kodu dekoratora w czasie dziaania skryptu. Obie metody maj swoje wady i zalety, ktre za chwil omwimy, ale najpierw spjrzmy na Listing 7, gdzie znajduj si przykadowe dekoratory (z Listingu 6), zaimplementowane przy uyciu obu wspomnianych metod. Dekorator stworzony przy pomocy __call jest dosy prosty i pozwala prezycyjnie regulowa, ktre metody w dekoratorze s generowane automatyczne, a ktre rcznie. Po prostu dla tych, ktre chcemy napisa sami, tworzymy konkretn metod, a wprzypadku pozostaych metod polegamy na __call. Omawiana wanie metoda ma w zasadzie tylko jedn wad w ten sposb wygenerowane dekoratory nie mog by uyte w wywoaniach funkcji i metod, dla ktrych okrelono typ argumentu (a wic dla konstrukcji $obiekt->nazwaMetody(TypArgumentu $agrument);). Jest to do powana wada, poniewa na pocztku podkrelalimy, i jedn z cech dekoratora jest zachowanie interfejsu oryginalnego obiektu. Zastosowanie __call do generowania dekoratorw wyklucza silniejsz kontrol typw, a tym samym interfejsw implementowanych przez podstawow i udekorowan klas.
Aby obej zidentyfikowany problem, posuymy si prost koncepcyjnie metod generowaniem kodu w locie. Spjrzmy na Listing 8, gdzie pokazujemy uycie specjalnie przygotowanej biblioteki (PHPProxy, dostpnej na sourceforge.net) do generowania kodu. Jak wida, samo jej uycie jest niezwykle proste sprowadza si do zaimplementowania tylko tej logiki, ktra ma si znale w dekoratorze nie musimy powiela ani jednego znaku kodu. Tak prosta implementacja jest moliwa dziki istnieniu interfejsu ProxyInvocationHandler. W interfejsie tym zdefiniowana jest tylko jedna metoda invoke($method, $args). Nie trzeba by potomkiem Sherlocka Holmesa by wydedukowa, e metoda ta jest uruchamiana w momencie wywoywania metod na dekoratorze, a w argumetach otrzymujemy wszystkie niezbdne informacje o wywoaniu tj. nazw metody i jej argumenty. Korzystajc z implementacji interfejsu ProxyInvocationHandler z opisywanej biblioteki (Listing 9), moemy atwo zdecydowa, czy chcemy tylko udekorowa oryginaln funkcjonalno (a wic wywoa j podczas uruchamiania dekoratora), czy te stworzy zupenie nowy kod: cay sekret tkwi w meto-
www.phpsolmag.org
Techniki
dzie getDelegate(), dzieki ktrej moemy pobra dekorowany obiekt i wywoa na nim dowoln metod. Uycie biblioteki PHPProxy pozwala generowa dekoratory przy minimalnym nakadzie pracy ze strony programisty i przy eliminacji powtrze w kodzie. Niestety, nie jest to metoda pozbawiona wad. Po pierwsze, generowanie kodu w czasie dziaania programu moe by zbyt czasochonne w przypadku mocno obcionych serwisw internetowych, gdzie wydajno jest rzecz krytyczn. Drugi, troch mniej uciliwy problem, zwizany jest z przypadoci nkajca wszystkie rozwizania oparte o generowanie kodu. Ot podczas wykonania programu uruchamiany jest rwnie kod, ktry... nie jest nigdzie zapisany na stae. Oznacza to, e w pewnych warunkach ledzenie wykonania programu (np. podczas wyszukiwania bdw) moe by nieco utrudnione. Tak czy inaczej, mimo przedstawionych wad, jest to zdecydowanie najatwiejsza metoda tworzenia wielu dekoratorw. Podsumujmy nasz dotychczasow wiedz o dekoratorach. Na wstpie stwierdzilimy, e jest to bardzo poyteczny wzorzec, ktry moe by stosowany praktycznie w kadej warstwie systemu. Jest to alternatywa do dziedziczenia obiektw, umoliwiajca wzbogacanie lub zmian istniejcej funkcjonalnoci, bez koniecznoci modyfikacji raz napisanego kodu. Dekorator pada jednak troch ofiar wasnego sukcesu mnogo jego zastosowa powoduje, e w bardziej rozbudowanej aplikacji moemy wykorzysta setki obiektw implementujcych wzorzec dekoratora. Aby nie zgin w tym gszczu podobnych obiektw, znalelimy dwie metody na dynamiczne generowanie dekoratorw. Niestety, to nie koniec problemw, ktre musimy rozwiza, aby efektywnie wykorzysta dekoratory jako integraln cz rozwizania architektonicznego. Zamy, e Rysunek 1 przedstawia uproszczony graf obiektw w naszej aplikacji. Obiekty te s oczywicie uoone w warstwy, natomiast obiekty s poczone midzy sob sieci zalenoci. Ju samo poprawne skonstruowanie takiej sieci obiektw moe by nie lada wyzwaniem, o czym Czytelnik mg si przekona, ledzc mj artyku Obiektowa linia montaowa, czyli przejrzyste
Rysunek 2. Dodajc dekoratory, wprowadzamy do omawianego grafu kolejny stopie skomplikowania, opakowujc wybrane obiekty aplikacji j Listing 6a. Dwa przykadowe kontrolery oraz opakowanie z dekoratorw sprawdzajcych uprawnienia
<?php class useraction { private $_userService; public function __construct(UserService $userService) { $this->_userService = $userService; } public function listall(HttpRequest $request, ModelAndView $mv){ $users = $this->_userService->findAll(); $mv->addToModel('users',$users); $mv->setView('userslist'); return $mv; } public function listwithexpensivequery( HttpRequest $request, ModelAndView $mv){ $users = $this->_userService->findUserByExpensiveQuery(); $mv->addToModel('users',$users); $mv->setView('userslist'); return $mv; } public function addform(HttpRequest $request, ModelAndView $mv){ $mv->setView('useraddform'); return $mv; } public function add(HttpRequest $request, ModelAndView $mv){ $user = new User( $request->getParam('login'), $request->getParam('pass'), $request->getParam('firstname'), $request->getParam('lastname')); $mv->addToModel('user',$user); try { $this->_userService->addUser($user); $mv->setView('useraddconfirm'); } catch (UserExistsException $e) { $mv->addToModel('adduser_error','User exists!'); $mv->setView('useraddform'); } return $mv; } } class homepage { public function show(HttpRequest $request, ModelAndView $mv){ $mv->setView('homepage'); return $mv; } }
www.phpsolmag.org
Techniki
Rysunek 3. W Pico zawarta jest caa konfiguracja zwizana z poczeniami pomidzy obiektami. Aby wprowadzi dodatkowy obiekt poredniczcy, musimy jedynie przekonfigurowa poczenia. Listing 6b. Dwa przykadowe kontrolery oraz opakowanie z dekoratorw sprawdzajcych
// Klasa pomocnicza dla dekoratorw, gdzie odbywa si // waciwe sprawdzanie uprawnie abstract class AbstractActionSecurityDecoratorImpl { private $_decoratedAction; public function __construct($decoratedAction) { $this->_decoratedAction = $decoratedAction; } public function executeTargetActionWithSecurityCheck( $targetAction, HttpRequest $request, ModelAndView $mv){ //tutaj tylko proste sprawdzanie, czy zalogowany, //ale rwnie atwo zintegrowa np. GACL session_start(); if ($_SESSION['user'] == null) { $mv->setView('loginform'); return $mv; } else { return $this->_decoratedAction->$targetAction($request, $mv); }
} } //Mimo, i obie akcje s zupenie inne, //to dekoratory s niemal identyczne! //Oczywista duplikacja kodu! class UserActionSecurityDecoratorImpl extends AbstractActionSecurityDecoratorImpl { public function listall(HttpRequest $request, ModelAndView $mv){ return $this->executeTargetActionWithSecurityCheck( 'listall', $request, $mv); } public function listwithexpensivequery( HttpRequest $request, ModelAndView $mv){ return $this->executeTargetActionWithSecurityCheck( 'listwithexpensivequery', $request, $mv); } public function addform(HttpRequest $request, ModelAndView $mv){ return $this->executeTargetActionWithSecurityCheck( 'addform', $request, $mv); } public function add(HttpRequest $request, ModelAndView $mv){ return $this->executeTargetActionWithSecurityCheck('add', $request, $mv); } } class HomepageActionSecurityDecoratorImpl extends AbstractActionSecurityDecoratorImpl { public function show(HttpRequest $request, ModelAndView $mv){ return $this->executeTargetActionWithSecurityCheck('show', $request, $mv); } } ?>
i elastyczne aplikacje w PHP5, z numeru 1/2006. Dodajc dekoratory, wprowadzamy do omawianego grafu kolejny stopie skomplikowania, opakowujc wybrane obiekty aplikacji (Rysunek 2). Warto przy tym zauway, e udekorowaniu bd podlegay gwnie obiekty infrastrukturalne (kontrolery, DAO itd.), a nie domenowe. Musimy wic znale jaki atwy sposb na dodanie wielu dekoratorw w caej aplikacji, bez koniecznoci rcznego przebudowywania pocze. Przy skadaniu skomplikowanych grafw obiektw doskonale sprawdza si wzorzec architektoniczny IoC (ang. Inversion of Control), o ktrym rwnie pisalimy w wyej wspomnianym artykule. Mamy szczcie, poniewa ten sam wzorzec rwnie znakomicie uatwia dodawanie dekoratorw. Dlaczego? Przypomnijmy, e w przypadku stosowania wzorca IoC bardzo pomocne s biblioteki typu kontener IoC. Te lekkie kontenery bior na siebie cay ciar tworzenia skomplikowanych grafw obiektw, dbajc przy tym o waciwe rozwizanie zalenoci pomidzy obiektami. Kontener IoC jest wic jednym, centralnym miejscem, gdzie powoywane s do ycia instancje obiektw infrastrukturalnych. Doskonale, o to nam wanie chodzio. To scentralizowane miejsce tworzenia obiektw pozwala nam atwo doda nasze "opakowania". Spjrzmy na Listing 10, gdzie znajdziemy przykad wykorzystania Pico dla PHP lekkiego kontenera IoC do dodania dekoratorw do obiektw aplikacji. Jak wida, caa operacja jest stosunkowo prosta wystarcz dwie linijki kodu. Przeanalizujmy dokadniej przykad z Listingu 10. Widzimy na nim pocztkowo dwa wsppracujce ze sob obiekty: serwisowy i DAO. S to obiekty infrastrukturalne, podczas dzialania aplikacji potrzebny jest zwykle tylko jeden egzemplarz takiego obiektu. W tym przypadku to Pico dba o powoanie do ycia instancji obiektw oraz ich poczenie. Aby zastosowa dekorator zliczajcy czas wykonania poszczeglnych metod, musimy w jaki sposb rozerwa cise poczenie pomidzy obiektem DAO i obiektem z DAO korzystajcym. Przy klasycznym podejciu do budowy programw, gdybymy uyli operatora new, metod statycznych lub fabryk, mielibymy mae szanse na wprowadzenie trzeciego obiektu w acuch powi-
www.phpsolmag.org
Techniki
Listing 7. Dekorator (z Listingu 6), zaimplementowany przy uyciu __call oraz generowanie caego kodu dekoratora w czasie dziaania skryptu
<?php // implementacja dekoratora z wykorzystaniem metody __call class UserActionSecurityDecoratorCallImpl extends AbstractActionSecurityDecoratorImpl { public function __call ($methodName, $args ) { $request = $args[0]; $mv = $args[1]; return $this->executeTargetActionWithSecurityCheck( $methodName, $request, $mv); }} class HomepageActionSecurityDecoratorCallImpl extends AbstractActionSecurityDecoratorImpl { public function __call ($methodName, $args ) { $request = $args[0]; $mv = $args[1]; return $this->executeTargetActionWithSecurityCheck( $methodName, $request, $mv); }} ?>
www.phpsolmag.org
Techniki
Listing 10. Przykad wykorzystania Pico dla PHP lekkiego kontenera IoC do dodania dekoratorw do obiektw aplikacji
<?php $parentPico = new DefaultPicoContainer(); $parentPico->regComponentImpl('FrontController', 'FrontControllerImpl'); $parentPico->regComponentImpl( 'ActionResolvingStrategy', //componentKey 'PicoActionResolvingStrategy', //componentClass array ('paramName' => 'action') ); $parentPico->regComponentImplWithIncFileName( dirname(__FILE__).'/lib/SmartyViewResolver.php', 'ViewResolvingStrategy', 'SmartyViewResolvingStrategy', array ( array('compile_dir' => dirname(__FILE__).'/work/templates_c'), 'file:'.dirname(__FILE__).'/templates/smarty/', '.tpl') ); $pico = new DefaultPicoContainer(null, $parentPico); $pico->regComponentInstance($pico, 'PicoContainer'); $pico->regComponentImplWithIncFileName(dirname(__FILE__). '/../shared/model/model.inc.php','UserService','UserServiceImpl'); $pico->regComponentImplWithIncFileName(dirname(__FILE__). '/../shared/model/model.inc.php','UserDAO','UserDAOPDOImpl'); $pico->regComponentImpl('PDO','PDOPicoAdapter',array ( 'pgsql:dbname=ditalkdb;host=localhost', 'postgres', 'postgres')); //actions $pico->regComponentImplWithIncFileName(dirname(__FILE__). '/actions/loginaction_action.php','loginaction','loginaction'); $pico->regComponentImplWithIncFileName(dirname(__FILE__). '/actions/useraction_action.php','useractionTarget', 'useraction'); $pico->regComponentImpl('useraction', 'UserActionSecurityDecoratorImpl', array ('decoratedAction' => new BasicComponentParameter('useractionTarget'))); $pico->regComponentImplWithIncFileName(dirname(__FILE__). '/actions/homepage_action.php','homepageTarget','homepage'); $pico->regComponentImpl('homepage', 'HomepageActionSecurityDecoratorImpl', array ('decoratedAction' => new BasicComponentParameter('homepageTarget'))); $fc = $pico->getComponentInstance('FrontController'); $fc->doService(new HttpRequest()); ?>
nam pewna refleksja. Ot w opisywanym przypadku stosujemy dekoratory do globalnych zmian w caej aplikacji. Patrzymy na dziaajce skrypty i mylimy: teraz chcielibymy doda logowanie do wszystkich obiektw DAO, eby zobaczy, gdzie jest wskie gardo wydajnociowe, albo: teraz udekorujmy wszystkie kontrolery, eby sprawdza uprawnienia. Niestety, wypracowana do tej pory metoda skazuje nas na mudne deklarowanie pojedynczych dekoratorw. Gdyby tylko istniaa konstrukcja programistyczna, pozwalajca atwo wyrazi dania typu: obiekty okrelonego typu udekoruj danym kodem, moglibymy dosownie w kilku linijkach kodu wprowadzi do aplikacji logowanie czy sprawdzanie uprawnie. Zupenie niezalenie od iloci obiektw do udekorowania. Na pocieszenie chcemy powiedzie, e takie metody ju powstaj (przykadowy Listing 11)! Jest to idea bardzo zbliona do programowania aspektowego (ang. Aspect Oriented Programming), ale to ju temat na zupenie inny artyku.
Podsumowanie
Listing 11. Przykad rozwizania, ktre nie skazuje nas na mudne deklarowanie pojedynczych dekoratorw
<?php //rejestracja poprzednich komponentw //jak na Listingu 10 $pico->regComponentImplWithIncFileName(dirname(__FILE__). '/actions/useraction_action.php','useraction', 'useraction'); $pico->regComponentImplWithIncFileName(dirname(__FILE__). '/actions/homepage_action.php','homepage','homepage'); $pico->regComponentImplWithIncFileName(dirname(__FILE__). '/actions/loginaction_action.php','loginaction','loginaction'); //security decorators $pico->regComponentImpl('SecurityMethodInterceptor'); $pico->registerAspect(new Aspect(new RegExpNameMatchingPointcut( '/^[a-km-z]+action$/'),new MatchingAllPointcut(), 'SecurityMethodInterceptor')); ?>
W artykule pokazalimy, e dekorator jest prostym i niezwykle poytecznym wzorcem projektowym. Jest tak uyteczny, e w pewnym momencie moemy mie ochot zastosowa go na bardzo szerok skal. Aby jednak zrobi to efektywnie, musimy zadba o rozwizanie dwch problemw: automatycznego generowania dekoratorw oraz ich konfiguracji w caej aplikacji. Z pierwszym problem doskonale poradzimy sobie dynamicznie generujc powtarzalny kod dekoratora. Zastosowanie kontenera IoC wpynie dodatnio na architektur naszej aplikacji i umoliwi atwe konfigurowanie dekoratorw. Jeli zrozumiemy i opanujemy przedstawione powyej triki, wiat programowania aspektowego nie bdzie mia przed nami tajemnic. n
O autorze:
Pawe Kozowski jest pracownikiem SUPERMEDIA, gdzie od roku 2000 projektuje i tworzy zoone aplikacje WWW w PHP. Obecnie zajmuje si rozwijaniem frameworkw i bibliotek ORM opartych na PHP5. Jest autorem portu PicoContainer dla PHP5 i wielu publikacji powiconych PHP. Kontakt: pkozlowski@phpsolmag.org
www.phpsolmag.org