Professional Documents
Culture Documents
PRZYKADOWY ROZDZIA
SPIS TRECI
KATALOG KSIEK
KATALOG ONLINE
TWJ KOSZYK
DODAJ DO KOSZYKA
CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK
CZYTELNIA
FRAGMENTY KSIEK ONLINE
Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl
O autorach ...................................................................................................................................... 9
Przedmowa ................................................................................................................................... 13
Rozdzia 1. Instalacja i podstawy ................................................................................................ 21
1. Instalacja PHP .................................................................................................................. 21
2. Instalacja moduw PEAR .............................................................................................. 32
Spis treci
23.
24.
25.
26.
Spis treci
58.
59.
60.
61.
62.
63.
64.
65.
66.
Spis treci
90.
91.
92.
93.
94.
Spis treci
Obserwacja obiektw
ROZDZIA SIDMY
Wzorce projektowe nie byy zbyt czsto wykorzystywane w PHP. Przed wydaniem PHP
w wersji 5 jzyk PHP i jego rodowisko projektowe nie byy traktowane w brany programistw powanie. Obecnie, dziki rozbudowanemu modelowi obiektowemu, dobrym
rodowiskom IDE i duej popularnoci jzyka wrd programistw, brana zaczyna
dostrzega PHP. Zastosowanie wzorcw projektowych w PHP opisano w niektrych
najnowszych wydawnictwach powiconych temu jzykowi. Uwaam, e warto powici
im nieco uwagi take w tej ksice.
W swej pracy postanowiem odwoywa si do ksiki Design Patterns. Wybraem z niej
podzbir wzorcw do zaimplementowania. Wzorce te, wraz z ich implementacj, daj
solidne podstawy architektury systemw. Mog by take inspiracj do opracowywania
wasnego kodu.
Wzorce projektowe
283
SPOSB
67.
SPOSB
67.
Obserwacja obiektw
SPOSB
67.
Obserwacja obiektw
Zastosowanie wzorca Obserwator do lunego wizania obiektw.
Lune wizanie obiektw (ang. loose coupling), pomimo e niewiele osb dobrze interpretuje ten termin, ma kluczowe znaczenie dla kadego projektu na du skal. Czy zdarzyo si Wam wprowadzi niewielk modyfikacj w projekcie, w ktrej wyniku trzeba
byo zmieni w nim niemal wszystko? Taka sytuacja zdarza si nader czsto, a jej przyczyn jest cise wizanie pomidzy moduami programu. Jeli jeden z nich przestaje
dziaa, podobnie dzieje si z reszt.
Wzorzec Obserwator rozlunia powizania pomidzy obiektami dziki zastosowaniu
prostszego kontraktu. Obiekt moe by obserwowany dziki udostpnieniu mechanizmu rejestracji. W przypadku, gdy obserwowany obiekt si zmienia, informuje o tym
obiekty obserwujce za pomoc obiektu powiadamiajcego. Obserwowanego obiektu
nie interesuje ani sposb, ani powd, dla ktrego jest obserwowany. Nie wie nawet tego,
jakie typy obiektw go obserwuj. Co wicej, obiektw obserwujcych zazwyczaj nie
interesuje sposb lub powd modyfikacji obiektu. Jedyne, co ledz, to zmiany.
Klasycznym przykadem wzorca Obserwator jest kod obsugi okna dialogowego, ktry
obserwuje stan pola wyboru. Dla pola wyboru nie ma znaczenia, czy obserwuje go jeden
obiekt czy tysic. Jeli zmieni si jego stan, po prostu wysya informacj. Z kolei dla okna
dialogowego nie ma znaczenia sposb implementacji pola wyboru. Istotny jest tylko jego
stan oraz uzyskanie powiadomienia w przypadku, gdy si zmieni.
W podrozdziale zademonstruj wzorzec Obserwator na przykadzie listy klientw, ktr
mona obserwowa. Obiekt reprezentuje tabel klientw w bazie danych. W przypadku
dodania nowych klientw obiekt CustomerList wysya powiadomienie. Do zaimplementowania obserwacji obiekt CustomerList wykorzystuje klas SubscriptionList.
Obiekty nasuchujce to egzemplarze klasy SubscriptionList, ktre inne obiekty
wykorzystuj do zarejestrowania si w obiekcie CustomerList. Obiekty te uywaj
metody add() w celu dodania si do listy, natomiast obiekt CustomerList wykorzystuje metod invoke() w celu wysania komunikatu do obiektw nasuchujcych. Dla
obiektu CustomerObject nie ma znaczenia, czy jest tysic obiektw nasuchujcych
czy nie ma adnego. Ciekaw wasnoci wzorca Obserwator jest fakt, i obiekty nasuchujce nie komunikuj si w sposb bezporedni ani nie zale od obiektu CustomerList.
Obiekty nasuchujce s odizolowane od klientw za pomoc klasy SubscriptionList.
W pokazanym przykadzie zdefiniujemy jeden obiekt nasuchujcy: Log, ktrego dziaanie
polega na wywietlaniu na konsoli komunikatw przesyanych przez obiekt CustomerList. Powizania pomidzy obiektami zaprezentowano na rysunku 7.1.
284
Wzorce projektowe
Obserwacja obiektw
Rysunek 7.1. Obiekty CustomerList oraz zwizany z nim obiekt SubscriptionList wraz z obiektem Log
Kod
Kod pokazany na listingu 7.1 naley zapisa w pliku observer.php.
Listing 7.1. Przykad zastosowania wzorca projektowego Obserwator
<?php
class Log
{
public function message( $sender, $messageType, $data )
{
print $messageType." - ".$data."\n";
}
}
class SubscriptionList
{
var $list = array();
public function add( $obj, $method )
{
$this->list []= array( $obj, $method );
}
public function invoke()
{
$args = func_get_args();
foreach( $this->list as $l ) { call_user_func_array( $l, $args ); }
}
}
class CustomerList
{
public $listeners;
public function CustomerList()
{
$this->listeners = new SubscriptionList();
}
public function addUser( $user )
{
$this->listeners->invoke( $this, "add", "$user" );
}
}
Wzorce projektowe
285
SPOSB
67.
SPOSB
67.
Obserwacja obiektw
$l = new Log();
$cl = new CustomerList();
$cl->listeners->add( $l, 'message' );
$cl->addUser( "starbuck" );
?>
Wykorzystanie sposobu
Powyszy kod mona uruchomi w wierszu polecenia w nastpujcy sposb:
% php observer.php
add - starbuck
Kod najpierw tworzy list klientw oraz obiekt Log. Nastpnie obiekt Log wykonuje
subskrypcj listy klientw za pomoc metody add(). Ostatnia czynno to dodanie
uytkownika do listy klientw. Powoduje to wysanie wiadomoci do obiektw nasuchujcych w tym przypadku do obiektu Log, ktry wywietla komunikat o dodaniu
nowego klienta.
Bez trudu mona rozszerzy zaprezentowany kod i skonfigurowa konto klienta po dodaniu lub, na przykad, wysa e-mail do nowego uytkownika obie te operacje mona wykona bez koniecznoci modyfikacji kodu obiektu CustomerList. Wanie na
tym polega lune wizanie obiektw i dlatego wzorzec projektowy Obserwator jest taki
wany.
Istnieje bardzo wiele zastosowa wzorca projektowego Obserwator w programowaniu.
Wykorzystuje si go, midzy innymi, w systemach okienkowych do implementacji mechanizmu zdarze (ang. events). Niektre firmy, na przykad Tibco, tworz cay model
dziaania swoich przedsibiorstw w oparciu o wzorzec Obserwator. Wykorzystuj go do
czenia duych podsystemw funkcjonalnych, takich jak Kadry i Pace. W systemach
baz danych wzorzec Obserwator mona wykorzysta do wywoywania kodu zwizanego
z wyzwalaczami, ktre uaktywniaj si w przypadku, gdy w bazie danych zostan
zmodyfikowane pewne typy rekordw. Mechanizm wykorzystujcy wzorzec Obserwator
przydaje si rwnie w sytuacjach, gdy mamy wiadomo, e zmiana stanu bdzie
istotna, ale jeszcze nie wiemy, gdzie te informacje wykorzystamy. Obiekty nasuchujce
mona zaimplementowa pniej i nie trzeba ich wiza z obserwowanym obiektem.
Potencjalnym problemem wzorca projektowego Obserwator s ptle nieskoczone. Mog
si zdarzy, gdy obiekty obserwujce system jednoczenie go modyfikuj. Na przykad
rozwijane pole kombi modyfikuje warto i informuje o tym struktur danych. Struktura
danych powiadamia rozwijane pole kombi, e warto si zmienia. Wtedy rozwijane
pole kombi modyfikuje swoj warto i wysya kolejne powiadomienie do struktury danych itd. Najprostszym sposobem rozwizania tego problemu jest wykluczenie wystpienia rekurencji w kodzie obsugi pola kombi. Obiekt powinien zignorowa komunikat od
struktury danych, jeli wanie powiadamia struktur danych o swojej nowej wartoci.
286
Wzorce projektowe
Zobacz te
Przeksztacanie dowolnych obiektw na tablice [Sposb 53.].
Tworzenie kolejki wiadomoci [Sposb 50.].
SPOSB
Wzorzec projektowy Fabryka Abstrakcyjna (ang. Abstract Factory) to maszyna do produkcji wzorcw projektowych. Wystarczy zdefiniowa to, czego chcemy, a wzorzec zadba
o utworzenie obiektw na podstawie wprowadzonych kryteriw. Zalet wzorca jest
moliwo modyfikacji typu tworzonych obiektw poprzez modyfikacj fabryki.
W prostym przykadzie zaprezentowanym w tym podrozdziale utworzymy obiekty
Record, z ktrych kady bdzie mia swj identyfikator, imi i nazwisko. Zwizki pomidzy poszczeglnymi klasami pokazano na rysunku 7.2.
W PHP nie mona rygorystycznie wymusi tworzenia obiektw okrelonego typu wycznie przez obiekt-fabryk. Jeli jednak bdziemy stosowali obiekt-fabryk stosunkowo
czsto, inynierowie kopiujc i wklejajc nasz kod, bd w efekcie stosowali obiektfabryk. Szybko stanie si on standardem de facto tworzenia rnych typw obiektw.
Wzorce projektowe
287
SPOSB
68.
SPOSB
68.
Kod
Kod pokazany na listingu 7.2 zapiszemy w pliku abs_factory.php.
Listing 7.2. Zastosowanie wzorca projektowego Fabryka Abstrakcyjna
<?php
class Record
{
public $id = null;
public $first = null;
public $last = null;
public function __construct( $id, $first, $last )
{
$this->id = $id;
$this->first = $first;
$this->last = $last;
}
}
class USRecord extends Record
{
public $addr1 = null;
public $addr2 = null;
public $city = null;
public $state = null;
public $zip = null;
public function __construct( $id, $first, $last,
$addr1, $addr2, $city, $state, $zip )
{
parent::__construct( $id, $first, $last );
$this->addr1 = $addr1;
$this->addr2 = $addr2;
$this->city = $city;
$this->state = $state;
$this->zip = $zip;
}
}
class ForeignRecord extends Record
{
public $addr1 = null;
public $addr2 = null;
public $city = null;
public $state = null;
public $postal = null;
public $country = null;
public function __construct( $id, $first, $last,
$addr1, $addr2, $city, $state, $postal, $country )
{
parent::__construct( $id, $first, $last );
$this->addr1 = $addr1;
$this->addr2 = $addr2;
$this->city = $city;
$this->state = $state;
$this->postal = $postal;
$this->country = $country;
}
}
288
Wzorce projektowe
W pierwszej czci kodu zaimplementowano klas bazow Record oraz klasy pochodne
USRecord i ForeignRecord. S to stosunkowo proste klasy opakowujce dla struktur
danych. Klasa-fabryka moe tworzy zarwno obiekty USRecord, jak ForeignRecord
w zalenoci od danych, ktre zostan do niej przekazane. Kod testujcy na kocu skryptu
dodaje kilka rekordw, po czym wywietla ich typ oraz niektre dane.
Wykorzystanie sposobu
Do uruchomienia przykadu zastosujemy interpreter PHP dziaajcy w wierszu polecenia
w nastpujcy sposb:
% php abs_factory.php
USRecord - 1 - Jack - Herrington
ForeignRecord - 1 - Maria - Kowalska
W aplikacji bazodanowej w PHP mona zastosowa wzorzec projektowy Fabryka Abstrakcyjna na kilka sposobw:
Wzorce projektowe
289
SPOSB
68.
SPOSB
69.
Zobacz te
Elastyczne tworzenie obiektw z wykorzystaniem wzorca Metoda Fabrykujca
[Sposb 69.].
SPOSB
Z wzorcem Fabryka Abstrakcyjna jest blisko zwizany wzorzec Metoda Fabrykujca. Jego
dziaanie jest do oczywiste. Jeli mamy klas, ktra tworzy du liczb obiektw, moemy wykorzysta metody chronione hermetyzujce operacje tworzenia. W ten sposb
klasy pochodne, w celu utworzenia rnych typw obiektw, mog przesoni metody
chronione klasy-fabryki.
W pokazanym przykadzie klasa RecordReader zamiast skorzystania z klasy-fabryki
wykorzystuje metod NewRecord(). W ten sposb klasy pochodne klasy RecordReader
mog modyfikowa typ tworzonych obiektw Record poprzez przesonicie metody
newRecord(). Sytuacj t graficznie przedstawiono na rysunku 7.3.
Kod
Kod pokazany na listingu 7.3 zapiszemy w pliku factory_method.php.
290
Wzorce projektowe
Wzorce projektowe
291
SPOSB
69.
SPOSB
70.
Wykorzystanie sposobu
Zaprezentowany kod uruchamia si w wierszu polecenia w nastpujcy sposb:
%php factory_method.php
1 - Jack - Herrington
2 - Lori - Herrington
3 - Megan - Herrington
Zobacz te
Tworzenie obiektw z wykorzystaniem wzorca Fabryka Abstrakcyjna [Sposb 68.].
SPOSB
Wielokrotnie odnosz wraenie, e kod, ktry co tworzy, jest najbardziej elegancki w caym systemie. Myl, e jest tak dlatego, e powiciem rok na pisanie ksiki o generowaniu kodu, ktra w caoci jest powicona kodowi konstrukcyjnemu.
Chciabym doda, e ksika Code Generation in Action jest cigle dostpna i moe
by doskonaym prezentem witecznym dla przyjaci lub czonkw rodziny.
292
Wzorce projektowe
Wzorce projektowe
293
SPOSB
70.
SPOSB
70.
Kod
Kod pokazany na listingu 7.4 zapiszemy w pliku builder.php.
Listing 7.4. Zbir przykadowych klas konstrukcyjnych i kod testowy
<?php
abstract class OutputBuilder
{
abstract function getOutput();
abstract function startBody();
abstract function endBody();
abstract function addText( $text );
abstract function addBreak();
}
class HTMLBuilder extends OutputBuilder
{
private $buffer = "";
public function getOutput()
{
return "<html>\n".$this->buffer."\n</html>\n";
}
public function startBody() { $this->add( "<body>" ); }
public function endBody() { $this->add( "</body>" ); }
public function addText( $text ) { $this->add( $text ); }
public function addBreak() { $this->add( "<br>\n" ); }
protected function add( $text ) { $this->buffer .= $text; }
}
class XHTMLBuilder extends HTMLBuilder
{
public function addBreak() { $this->add( "<br />\n" ); }
}
class TextBuilder extends OutputBuilder
{
private $buffer = "";
public function getOutput()
{
return $this->buffer."\n";
}
public function startBody() { }
public function endBody() { }
public function addText( $text ) { $this->add( $text ); }
public function addBreak() { $this->add( "\n" ); }
protected function add( $text ) { $this->buffer .= $text; }
}
function buildDocument( $builder )
{
$builder->startBody();
$builder->addText( 'Jack,' );
$builder->addBreak();
$builder->addText( 'Jeste nam winien 10 000 z. yczymy MIEGO dnia.' );
$builder->endBody();
}
294
Wzorce projektowe
Wykorzystanie sposobu
Do uruchomienia kodu wykorzystamy interpreter PHP dziaajcy w wierszu polecenia:
% php builder.php
HTML:
<html>
<body>Jack,<br>
Jeste nam winien 10 000 z. yczymy MIEGO dnia.</body>
</html>
XHTML:
<html>
<body>Jack,<br />
Jeste nam winien 10 000 z. yczymy MIEGO dnia.</body>
</html>
Tekst:
Jack,
Jeste nam winien 10 000 z. yczymy MIEGO dnia.
Wzorce projektowe
295
SPOSB
70.
SPOSB
71.
Wykorzystanie wzorca Strategia w celu oddzielenia kodu przegldajcego struktury danych od kodu,
ktry je przetwarza.
Wzorzec projektowy Strategia mona wykorzysta do wyodrbnienia kodu przetwarzajcego obiekty. Pozwala to na uniezalenienie sposobu przetwarzania kodu od jego lokalizacji.
W podrozdziale posu si aplikacj do wyboru samochodu. Skrypt bdzie poleca samochd na podstawie wprowadzonych kryteriw wyszukiwania. W przykadzie wprowadz specyfikacj samochodu idealnego, a kod wybierze egzemplarz, ktry najbardziej
pasuje do moich marze. Wielk zalet wzorca Strategia jest moliwo modyfikacji kodu
porwnujcego samochody w sposb niezaleny od kodu wybierajcego samochd.
Diagram UML dla sposobu pokazanego w tym podrozdziale pokazano na rysunku 7.5.
Obiekt CarChooser wykorzystuje obiekt CarWeighter w celu porwnania kadego
z samochodw z idealnym modelem. Nastpnie skrypt zwraca do klienta najlepszy
samochd.
Kod
Kod pokazany na listingu 7.5 zapiszemy w pliku strategy.php.
296
Wzorce projektowe
Wzorce projektowe
297
SPOSB
71.
SPOSB
71.
Na pocztku skryptu zdefiniowaem klas Car zawierajc nazw samochodu oraz oceny dla szybkoci, wygldu i przebiegu. Kada z ocen mieci si w zakresie od 0 do 100
(gwnie dlatego, aby obliczenia byy proste). Nastpnie umieciem definicj klasy
CarWeighter, ktra porwnuje dwa samochody i zwraca ocen porwnania. Na kocu
zdefiniowaem klas CarChooser wykorzystujc klas CarWeighter do wyboru najlepszego samochodu na podstawie pewnych kryteriw wejciowych. Funkcja pickCar() tworzy zbir samochodw, a nastpnie wykorzystuje obiekt CarChooser do
wyboru z listy samochodu, ktry najlepiej spenia kryteria (przekazane za pomoc
obiektu Car).
Kod testowy umieszczony na kocu skryptu to danie wyboru dwch samochodw
jednego, ktry ma wysok ocen szybkoci i drugiego, ktry adnie wyglda.
298
Wzorce projektowe
Wykorzystanie sposobu
Do uruchomienia kodu wykorzystamy interpreter PHP dziaajcy w wierszu polecenia:
% php strategy.php
rakieta
adny
Czasami trzeba pobra dane z dwch obiektw, z ktrych kady wykorzystuje inny
format. Modyfikacja jednego lub drugiego formatu nie wchodzi w rachub, poniewa
powodowaaby konieczno wprowadzania wielu dodatkowych zmian w pozostaej czci
kodu. Jednym z rozwiza tego problemu jest wykorzystanie klasy-adaptera. Jest to klasa,
ktra potrafi interpretowa obie strony transmisji danych i przystosowuje jeden obiekt
do komunikacji z drugim.
Klasa adapter zademonstrowana w tym podrozdziale przystosowuje dane pochodzce
z fikcyjnej bazy danych do wykorzystania przez mechanizm tworzenia wykresw tekstowych.
Na rysunku 7.6 pokazano obiekt RecordGraphAdapter umieszczony pomidzy
obiektem TextGraph po lewej stronie a obiektem RecordList po prawej. Obiekt TextGraph w czytelny sposb specyfikuje format danych za pomoc klasy abstrakcyjnej
TextDataSource. RecordList to klasa-kontener zawierajca list obiektw Record.
W kadym z nich s zapisane dane dotyczce nazwiska (name), wieku (age) i pensji
(salary).
W pokazanym przykadzie utworzymy wykres pensji. Zadaniem klasy-adaptera jest pobranie danych z obiektu RecordList i przeksztacenie ich na posta moliw do
przetworzenia przez obiekt TextGraph. W tym celu dane zostan zapisane jako obiekty
typu TextGraphDataSource.
Wzorce projektowe
299
SPOSB
72.
SPOSB
72.
Kod
Kod pokazany na listingu 7.6 zapiszemy w pliku adapter.php.
Listing 7.6. Przykad wykorzystania wzorca Adapter do tworzenia tekstowego wykresu
<?php
abstract class TextGraphDataSource
{
abstract function getCount();
abstract function getName( $row );
abstract function getValue( $row );
}
class TextGraph
{
private $data;
private $dmin;
private $dmax;
public function TextGraph( $data )
{
$this->data = $data;
}
protected function calculateMinMax()
{
$this->dmin = 100000;
$this->dmax = -100000;
for( $r = 0; $r < $this->data->getCount(); $r++ )
{
$v = $this->data->getValue( $r );
if ( $v < $this->dmin ) { $this->dmin = $v; }
if ( $v > $this->dmax ) { $this->dmax = $v; }
}
}
public function render()
{
$this->calculateMinMax();
$ratio = 40 / ( $this->dmax - $this->dmin );
300
Wzorce projektowe
Wzorce projektowe
301
SPOSB
72.
SPOSB
73.
Wykorzystanie sposobu
Do uruchomienia kodu wykorzystamy interpreter PHP dziaajcy w wierszu polecenia:
% php adapter.php
Janusz :
Beata : *
Stefania : ****
Jerzy : **********************
Grzegorz : ****************************************
73.
302
Wzorce projektowe
Dziki takiej implementacji mona stworzy jedn wersj kodu specyficzn dla systemu
Oracle i inn dla bazy MySQL (lub innej bazy danych). Jest to bardzo przydatne,
zwaszcza e w poszczeglnych typach bazach danych wystpuj rnice w skadni kodu
tworzcego tabele.
Kod
Kod pokazany na listingu 7.7 zapiszemy w pliku bridge.php.
Listing 7.7. Klasa bazowa wzorca Most
<?php
require( "sql.php" );
class TableCreator
{
static function createTable( $name )
{
TableCreatorImp::createTable( $name );
}
}
TableCreator::createTable( "customer" );
?>
Wzorce projektowe
303
SPOSB
73.
SPOSB
73.
Wykorzystanie sposobu
Wykorzystanie zaprezentowanego sposobu wymaga zastosowania dodatkowych parametrw w wierszu polecenia informujcych interpreter PHP o tym, e w ciece plikw
wczanych ma si znale katalog mysql lub oracle (co oznacza uycie mostu specyficznego dla okrelonego typu bazy danych). Oto wersja polecenia dla bazy danych MySQL:
%php -d include_path = '.:/usr/local/php5/lib/php:mysql' bridge.php
Wersja klasy createTable dla bazy MySQL tworzca tabel customer
Nie jest to skomplikowany przepis na zrobienie rakiety, zatem zrozumienie idei przykadu nie powinno przysporzy trudnoci. Klasa TableCreator zostaa zaimplementowana przez jedn z kilku wersji klasy TableCreatorImp umieszczonych w katalogach specyficznych dla platformy.
Oczywicie kod zamieszczony w przykadzie nie tworzy tabel. Jest to jedynie szkielet,
w ktrym w praktycznej aplikacji trzeba by byo wprowadzi odpowiedni kod. Arkana
tworzenia tabel w rnych systemach baz danych nie s jednak istotne dla zrozumienia
idei wzorca Most (mona je zatem skwitowa zdaniem prosz zapozna si z tym samodzielnie).
Jedn z powanych wad wzorca Most jest brak moliwoci rozszerzania implementacji
okrelonych klas. W tym przypadku nie stanowi to problemu, poniewa wszystkie metody klas implementacyjnych s statyczne. Jednak w przypadku obiektw zawierajcych
metody niestatyczne klasa implementacyjna dziedziczy cechy klas nieimplementacyjnych.
Na przykad klasa CButtonImp dziedziczy cechy po klasie CButton. W celu rozszerzenia implementacji trzeba by zastosowa dziedziczenie po klasie CButtonImp, ktra
jest ukryta. Problem ten dotyczy jednak w wikszym stopniu jzykw kompilowanych,
takich jak C++.
304
Wzorce projektowe
Wykorzystanie wzorca acuch odpowiedzialnoci do utworzenia szkieletu kodu w trybie Plug and Play.
Ogldanie futbolu z programistami jest zabawne. Nawet w czwartej kwarcie, kiedy wynik meczu wynosi 33:7, a pozostao zaledwie ptorej minuty do koca, w dalszym cigu
wskazuj na wiele moliwoci ostatecznego wyniku. Jest tak dlatego, e s przyzwyczajeni do przewidywania wszystkich sytuacji niezalenie od tego, jak bardzo s nieprawdopodobne (a waciwie zupenie absurdalne). Przekonaem si, e wikszo programistw, wcznie ze mn, nie znosi zamykania drzwi odnonie odpowiedzi na adne
z pyta. Lepiej napisa kod obsugujcy 100 moliwych przypadkw nawet wtedy, gdy
nasz meneder zaklina si, e jest tylko jedna moliwo.
Dlatego wanie wzorzec projektowy acuch odpowiedzialnoci (ang. Chain of responsibility)
jest tak wany. Wyobramy sobie, e do pomieszczenia, w ktrym jest wiele osb, wchodzi sprzedawca ciastek, niosc karton z pczkami o rnych smakach. Otwiera torebk
i wyjmuje pczek z marmolad. Po kolei pyta poszczeglne osoby, czy ycz sobie pczka
z marmolad, a znajdzie si kto, kto bdzie chcia. Nastpnie powtarza czynno dla
pozostaych pczkw z torebki do czasu, a bdzie pusta.
To wanie jest acuch odpowiedzialnoci. Kada osoba w pokoju rejestruje si wczeniej u dostawcy pczkw. Kiedy przychodzi nowa partia pczkw, dostawca widzi, kto
je zamawia, patrzc na list zarejestrowanych osb. Zaleta tej sytuacji polega na tym, e
dostawcy pczkw nie interesuje, ile osb zamawia pczki, nie interesuje go nawet, co
z nimi zrobi. Zajmuje si tylko zarzdzaniem rejestracj i dostawami.
W podrozdziale napisz kod, w ktrym zamiast pczkw bd posugiwa si adresami
URL. Skrypt bdzie dostarcza adresy URL do kilku procedur obsugi, ktre potencjalnie
bd je przekierowyway. Jeli adna z procedur obsugi nie obsuy adresu URL, taki
adres bdzie zignorowany.
Na rysunku 7.8 pokazano, w jaki sposb ma dziaa ten system. Klasa URLMapper to
dostawca pczkw. Ma karton peen adresw URL, ktre zamierza wrczy obiektom
o interfejsie URLHandler, ktre si po nie zgosz. W tym przypadku klasa ImageURLHandler zarzdza kierowaniem da adresw URL plikw graficznych do skryptu
obsugujcego grafik. W podobny sposb obiekt DocumentURLHandler przekierowuje dania dokumentw do odpowiednich stron PHP. Dziki temu aplikacja moe
przesa adresy URL bez specjalnego kodu obsugi, a jednoczenie modyfikowa je w miar
potrzeb.
Kod
Kod pokazany na listingu 7.10 zapiszemy w pliku chain.php.
Wzorce projektowe
305
SPOSB
74.
SPOSB
74.
Rysunek 7.8. Interfejs URLHandler, obiekt przekierowujcy i dwa obiekty obsugujce adresy URL
Listing 7.10. Przykad zastosowania w PHP wzorca projektowego acuch odpowiedzialnoci
<?php
abstract class URLHandler
{
abstract function getRealURL( $url );
}
class URLMapper
{
private $handlers = array();
private function URLMapper()
{
}
public function addHandler( $handler )
{
$this->handlers []= $handler;
}
public function mapURL( $url )
{
foreach( $this->handlers as $h )
{
$mapped = $h->getRealURL( $url );
if ( isset( $mapped ) ) return $mapped;
}
return $url;
}
public static function instance()
{
static $inst = null;
if( !isset( $inst ) ) { $inst = new URLMapper(); }
return $inst;
}
}
class ImageURLHandler extends URLHandler
{
private $base;
private $imgurl;
public function ImageURLHandler( $base, $imgurl )
306
Wzorce projektowe
= array();
[]= "http://mysite.com/index.html";
[]= "http://mysite.com/images/dog";
[]= "http://mysite.com/story/11/05/05";
[]= "http://mysite.com/images/cat";
[]= "http://mysite.com/image.php?img=lizard";
Wzorce projektowe
307
SPOSB
74.
SPOSB
74.
Wykorzystanie sposobu
Skrypt chain.php uruchomimy za pomoc interpretera PHP dziaajcego w wierszu polecenia:
%php chain.php
http://mysite.com/index.html
--> http://mysite.com/index.html
http://mysite.com/images/dog
--> http://mysite.com/image.php?img=dog
http://mysite.com/story/11/05/05
--> http://mysite.com/story.php?id=110505
http://mysite.com/images/cat
--> http://mysite.com/image.php?img=cat
http://mysite.com/image.php?img=lizard
--> http://mysite.com/image.php?img=lizard
%
Kady wchodzcy adres URL jest przesyany poprzez obiekt URLMapper, ktry zwraca
adres po jego przeksztaceniu. Pierwszy adres URL nie jest przekierowywany, zatem
obiekt URLMapper przekazuje go w niezmienionej postaci. W drugim przypadku obiekt
ImageURLHandler wykrywa, e adres URL dotyczy grafiki, zatem kieruje go do skryptu
image.php. Trzeci adres zosta rozpoznany jako dokument, zatem skierowano go do skryptu
story.php.
Doskona wasnoci wzorca projektowego acuch odpowiedzialnoci jest moliwo jego
rozszerzania bez koniecznoci modyfikacji kodu aplikacji. Wystarczy, e obiekt dostawcy bdzie wyposaony w dostatecznie rozbudowany interfejs API dla zarejestrowanych
obiektw, aby obsuy niemal kad sytuacj.
Jednym z najbardziej rozpoznawanych przykadw wzorca acuch odpowiedzialnoci jest
serwer WWW Apache, ktry dziaa jak jeden wielki dostawca pczkw, delegujc rone
dania do zarejestrowanych procedur obsugi.
Wzorzec acuch odpowiedzialnoci nie zawsze jest atwy do zastosowania.
Jest z nim zwizanych kilka powanych problemw. Trudno poprawia si
w nim bdy i nie zawsze wiadomo, w jaki sposb naley go waciwie
wykorzystywa. Wystpuj dwie odmiany wzorca: jedna, w ktrej w przypadku
znalezienia procedury obsugi danie nie jest dalej przesyane, i druga, gdzie
przetwarzanie jest kontynuowane niezalenie od tego, czy znaleziono waciw
procedur obsugi. Nie zawsze wiadomo, ktra z wersji jest wykorzystywana.
Co wicej, drugi wariant, gdzie zdarzaj si sytuacje wywoania wielu procedur
obsugi, jest szczeglnie trudny do diagnozowania. W przypadku wzorca
acuch odpowiedzialnoci potwierdza si regua, e rozbudowane moliwoci
programw komputerowych osiga si kosztem zoonoci i wydajnoci.
308
Wzorce projektowe
Kiedy sysz informacje o wielkich bazach danych, w ktrych s zapisane wszelkie informacje o osobach, ktrych ktokolwiek i kiedykolwiek mgby potrzebowa, reaguj
bardzo dziwnie. Wikszo osb myli pewnie o prywatnoci, mnie przychodzi do gowy
myl o tym, jak le zaprojektowano taki system. Jestem niemal pewien, e jest w nim jeden
megaobiekt Person zawierajcy jakie 4 000 pl i 8 000 metod.
Skd to wiem? Poniewa sam miaem do czynienia z takimi obiektami! Dla takiej klasy
koniecznie trzeba zastosowa wzorzec Kompozyt. Dziki niemu klasa Person pozostanie,
ale owe 4 000 pl zostanie podzielone na obiekty pochodne. W obiekcie klasy Person pozostanie okoo 100 obiektw, z ktrych kady bdzie zawiera inne, mniejsze obiekty
(potencjalnie zawierajce jeszcze mniejsze obiekty itd.).
Nie chc powiedzie, e zetknem si z tak le zaprojektowanymi klasami w PHP, cho
spotykaem klasy, w ktrych byo ponad 100 pl tylko dlatego, e obiekty reprezentoway zbir tabel zawierajcych wiele pl zwizanych z jednym zapisem. W podrozdziale
pokazaem sposb podziau klasy Customer (w ktrej jest o wiele za duo pl) na kilka
mniejszych klas. Na kocu procesu pozostaje jedna, zoona klasa Customer.
W pokazanym przykadzie podzieliem klas zawierajc okoo omiu pl.
Listing ten mona ekstrapolowa dla klas zawierajcych kilkaset takich pl,
z jakimi spotykaem si wczeniej.
Budow klasy Customer pokazano na rysunku 7.9. Zawiera ona po jednym obiekcie
CustomerName i CustomerAddress.
Wzorce projektowe
309
SPOSB
75.
SPOSB
75.
Kod
Kod pokazany na listingu 7.11 zapiszemy w pliku composite.php.
Listing 7.11. Obiekt Customer zoony z mniejszych obiektw
<?php
class CustomerName
{
public $first = "";
public $middle = "";
public $last = "";
}
class CustomerAddress
{
public $line1 = "";
public $line2 = "";
public $city = "";
public $state = "";
public $zip = "";
}
class Customer
{
public $id = null;
public $name = null;
public $address = null;
public function Customer()
{
$this->name = new CustomerName();
$this->address = new CustomerAddress();
}
public function Load( $id )
{
$this->id = $id;
$this->name->first = "George";
$this->name->middle = "W";
$this->name->last = "Bush";
$this->address->line1 = "1600 Pennsylvania Ave.";
$this->address->line2 = "";
$this->address->city = "Washington";
$this->address->state = "DC";
$this->address->zip = "20006";
}
public function Update()
{
// Aktualizacja rekordu w bazie danych
// lub wprowadzenie rekordu, jeli nie ma identyfikatora.
}
public function __toString()
{
return $this->name->first." ".$this->name->last;
}
}
310
Wzorce projektowe
Wykorzystanie sposobu
Powyszy skrypt uruchomimy, wykorzystujc interpreter PHP dziaajcy w wierszu
polecenia:
%php composite.php
George Bush
Wzorzec Fasada jest jednym z tych, ktre moim zdaniem powinno stosowa wicej programistw, i to nie z powodu adnie brzmicej nazwy, ale dlatego, e jeli kto stosuje
wzorzec Fasada, to znaczy, e myli o innych programistach i o tym, by uzyskali wanie
te informacje, ktrych potrzebuj (i nic wicej, dziki czemu nie mog nic zepsu).
Wemy za przykad prosty interfejs API systemu rejestrowania przedstawiony na rysunku 7.10.
Wzorce projektowe
311
SPOSB
76.
SPOSB
76.
Rysunek 7.10. Interfejs API systemu rejestrowania z prostym przykadem wzorca Fasada
Kod
Kod pokazany na listingu 7.12 zapiszemy w pliku test.php.
Listing 7.12. Kod testowy systemu rejestrowania zdarze
<?php
require( "log.php" );
log_start( "mylog" );
log_message( "Otwarcie aplikacji" );
log_message( "Zarejestrowanie komunikatu" );
log_message( "Zamknicie aplikacji" );
log_end();
?>
312
Wzorce projektowe
Wzorce projektowe
313
SPOSB
76.
SPOSB
76.
Wykorzystanie sposobu
Zaprezentowany kod uruchomimy za pomoc interpretera PHP dziaajcego w wierszu
polecenia:
% php test.php
% cat mylog.txt
Otwarcie aplikacji
Zarejestrowanie komunikatu
314
Wzorce projektowe
Nie ma specjalnie czego oglda, ale w rzeczywistoci interesuje nas kod (a nie jego wynik). W skrypcie test.php nastpuje rozpoczcie pliku dziennika, wysanie kilku komunikatw, a nastpnie jego zamknicie. Operacje te s wykonywane za pomoc zaledwie
trzech funkcji zdefiniowanych w skrypcie z wzorcem Fasada log.php. W idealnym
rodowisku log.php byby jedynym skryptem, do ktrego mieliby dostp programici
z zewntrz.
W skrypcie log.php do utworzenia dwch dziennikw zastosowano obiekt Log zaimplementowany za pomoc wzorca Singleton [Sposb 77.] w pliku log_impl.php. Skrypt
log.php wysya po jednym komunikacie do kadego z dziennikw, a nastpnie umieszcza
je w odpowiednich plikach (tekstowym lub XML).
SPOSB
77.
Spord wszystkich wzorcw projektowych opisanych w ksice Design Patterns autorstwa Gangu czterech aden nie jest wykorzystywany tak czsto jak Singleton. Czciowo przyczyn tego faktu jest jego atwa implementacja. Zreszt, czy moe by co
lepszego od zakodowania obiektu typu Singleton i dumnego owiadczenia, e taki obiekt
moe by tylko jeden? Jest w tym co z Niemiertelnego.
Singleton to typ obiektowy, dla ktrego w okrelonym momencie moe w systemie wystpowa tylko jeden egzemplarz. Wzorzec ten doskonale nadaje si do implementacji
uchwytu do bazy danych. Dla kadego egzemplarza interpretera PHP moe wystpowa
tylko jeden uchwyt do bazy danych. Wanie tak konfiguracj zaprezentuj w tym podrozdziale.
Diagram UML uchwytu do bazy danych z wykorzystaniem wzorca Singleton pokazano
na rysunku 7.11 (prawda, e proste?).
Wzorce projektowe
315
SPOSB
77.
SPOSB
77.
Rzeczywicie nie ma na co patrze. Obiekt zawiera uchwyt do bazy danych oraz dwie
metody. Pierwsza to konstruktor, ktry jest prywatny po to, by mie pewno, e kod spoza
klasy nie bdzie w stanie utworzy obiektu. Druga to statyczna metoda get_handle
zwracajca uchwyt do bazy danych.
Kod
Kod pokazany na listingu 7.15 zapiszemy w pliku singleton1.php.
Listing 7.15. Zastosowanie wzorca singleton jako klasy opakowujcej dla bazy danych
<?php
require( 'DB.php' );
class Database
{
private $dbh;
private function Database()
{
$dsn = 'mysql://root:password@localhost/test';
$this->dbh =& DB::Connect( $dsn, array() );
if (PEAR::isError($this->dbh)) { die($this->dbh->getMessage()); }
}
public static function get_handle()
{
static $db = null;
if ( !isset($db) ) $db = new Database();
return $db->dbh;
}
}
echo( Database::get_handle()."\n" );
echo( Database::get_handle()."\n" );
echo( Database::get_handle()."\n" );
?>
Ten prosty obiekt zgodny z wzorcem Singleton zawiera konstruktor obsugujcy logowanie do bazy danych oraz jedn statyczn metod dostpow, ktra tworzy obiekt, jeli nie zosta utworzony wczeniej, i zwraca odczytany z niego uchwyt do bazy danych.
Skorzystanie z tej metody w celu odczytania uchwytu do bazy danych daje pewno, e
poczenie z baz danych uzyskamy tylko raz podczas danego dania strony.
Wykorzystanie sposobu
Skrypt uruchomimy, korzystajc z interpretera PHP dziaajcego w wierszu polecenia:
%php singleton1.php
Object id#2
Object id#2
Object id#2
316
Wzorce projektowe
Modyfikacja sposobu
Z uchwytami do bazy danych poszo atwo. Zastanwmy si jednak, czy mona wykorzysta wzorzec Singleton dla bardziej skomplikowanych obiektw? Sprbujmy uy go dla
wspdzielonej listy stanw, tak jak pokazano na listingu 7.16.
Listing 7.16. Tablica stanw zaimplementowana za pomoc wzorca Singleton
<?php
class StateList
{
private $states = array();
private function StateList()
{
}
public function addState( $state )
{
$this->states []= $state;
}
public function getStates()
{
return $this->states;
}
public static function instance()
{
static $states = null;
if ( !isset($states) ) $states = new StateList();
return $states;
}
}
StateList::instance()->addState( "Florida" );
var_dump( StateList::instance()->getStates() );
StateList::instance()->addState( "Kentucky" );
var_dump( StateList::instance()->getStates() );
?>
Powyszy kod tworzy klas StateList zawierajc list stanw. Do listy mona dodawa stany, a take odczyta stany, ktre s ju na licie. Do uzyskania pojedynczego,
wspdzielonego egzemplarza tego obiektu trzeba skorzysta ze statycznej metody
instance() (zamiast bezporedniego tworzenia egzemplarza).
Do uruchomienia skryptu wykorzystamy interpreter PHP dziaajcy w wierszu polecenia:
% php singleton2.php
array(1) {
[0]=>
string(7) "Florida"
}
array(2) {
[0]=>
string(7) "Florida"
[1]=>
string(8) "Kentucky"
}
Wzorce projektowe
317
SPOSB
77.
SPOSB
78.
318
Wzorce projektowe
Kod
Kod zaprezentowany na listingu 7.17 zapiszemy w pliku visitor1.php.
Listing 7.17. Zastosowanie wzorca Wizytator do przegldania rekordw w bazie danych
<?php
class Record
{
public $name;
public $age;
public $salary;
public function Record( $name, $age, $salary )
{
$this->name = $name;
$this->age = $age;
$this->salary = $salary;
}
}
class RecordList
{
private $records = array();
public function RecordList()
{
$this->records []= new Record(
$this->records []= new Record(
$this->records []= new Record(
$this->records []= new Record(
}
Wzorce projektowe
319
SPOSB
78.
SPOSB
78.
Wykorzystanie sposobu
Do uruchomienia skryptu zaprezentowanego powyej wykorzystamy interpreter PHP
dziaajcy w wierszu polecenia:
% php visitor1.php
35000
Modyfikacja sposobu
Wersja zastosowania wzorca Wizytator z funkcjami jest wedug mnie troch niezgrabna. Lepiej byoby zdefiniowa obiekt-wizytator odczytujcy poszczeglne rekordy. Dziki temu dane
o wartoci minimalnej mogyby by zapisane w obiekcie i odczytane w pniejszym czasie.
Na rysunku 7.13 pokazano odmian implementacji wzorca Wizytator, gdzie obiekt typu
RecordVisitor pobiera metoda iterate(), a nie funkcja.
320
Wzorce projektowe
Wzorce projektowe
321
SPOSB
78.
SPOSB
78.
322
Wzorce projektowe