You are on page 1of 45

IDZ DO

PRZYKADOWY ROZDZIA
SPIS TRECI

KATALOG KSIEK
KATALOG ONLINE

100 sposobw na PHP


Autor: Jack Herrington
Tumaczenie: Radosaw Meryk
ISBN: 83-246-0426-X
Tytu oryginau: PHP Hacks
Format: B5, stron: 440

ZAMW DRUKOWANY KATALOG

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

Zbir rozwiza dla twrcw dynamicznych witryn WWW


Korzystanie z danych pochodzcych z innych witryn WWW
Dynamiczne generowanie grafiki i animacji Flash
Obsuga komunikatorw internetowych i protokou IRC
Jzyk PHP zdoby ogromn popularno jako narzdzie do tworzenia dynamicznych
witryn WWW, a grono jego uytkownikw stale si powiksza. Programici i projektanci
doceniaj jego moliwoci, szybko i wygod. Standardowe ju zastosowania jzyka
PHP czenie witryny WWW z baz danych, przechowywanie treci artykuw
w tabelach i obsuga formularzy nie wyczerpuj moliwoci tej platformy
programistycznej. PHP oferuje znacznie wicej pozwala midzy innymi na
dynamiczne generowanie grafiki, korzystanie z usug sieciowych i protokou SOAP
oraz przetwarzanie plikw XML.
Ksika 100 sposobw na PHP to co wicej ni kolejny podrcznik tworzenie
aplikacji WWW. Znajdziesz w niej mniej znane sposoby wykorzystywania PHP przy
budowaniu witryn internetowych. Nauczysz si korzysta z biblioteki PEAR, tworzy
interfejsw uytkownika z wykorzystaniem jzyka DHTML oraz technologii SVG oraz
generowa pliki RTF, CSV i XLS. Dowiesz si, jak stosowa wzorce projektowe
i testowa aplikacje wykorzystujc testy jednostkowe. Poznasz zasady programowania
obiektowego w PHP i tchniesz nowe ycie w dziaajce ju aplikacje dodajc do nich
ciekawe wodotryski, ktrych przykady znajdziesz w tej ksice.
Instalacja PHP oraz biblioteki PEAR
Projektowanie interfejsw uytkownika
czenie PHP z DHTML oraz JavaScript
Generowanie grafiki bitmapowej i wektorowej
Manipulowanie danymi w bazie za pomoc plikw XML
czenie aplikacji WWW z GoogleMaps oraz Wikipedi
Wykorzystywanie wzorcw projektowych
Testowanie aplikacji
Generowanie animacji Flash
Wysyanie SMS-w oraz wiadomoci na serwery IRC
Poznaj nietypowe zastosowania jzyka PHP

O autorach ...................................................................................................................................... 9
Przedmowa ................................................................................................................................... 13
Rozdzia 1. Instalacja i podstawy ................................................................................................ 21
1. Instalacja PHP .................................................................................................................. 21
2. Instalacja moduw PEAR .............................................................................................. 32

Rozdzia 2. Projektowanie aplikacji internetowych ................................................................... 35


3.
4.
5.
6.
7.

Tworzenie interfejsu z wykorzystaniem skrek ..................................................... 35


Tworzenie nawigacji typu breadcrumb ....................................................................... 39
Tworzenie ramek na stronach WWW .......................................................................... 43
Zastosowanie zakadek w interfejsie aplikacji internetowych ................................. 47
Zapewnienie uytkownikom moliwoci formatowania stron
z wykorzystaniem techniki XSL .................................................................................... 50
8. Tworzenie prostych wykresw HTML ........................................................................ 53
9. Prawidowe ustawianie rozmiaru znacznikw graficznych .................................... 55
10. Wysyanie wiadomoci e-mail w formacie HTML .................................................... 57

Rozdzia 3. DHTML ....................................................................................................................... 61


11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.

Umieszczenie na stronie interaktywnego arkusza kalkulacyjnego ......................... 61


Tworzenie wyskakujcych wskazwek ....................................................................... 64
Tworzenie list w trybie przecignij i upu ................................................................ 66
Tworzenie dynamicznych wykresw .......................................................................... 69
Podzia treci na rozwijane sekcje ................................................................................. 74
Tworzenie rozwijanych samoprzylepnych karteczek ........................................... 78
Tworzenie dynamicznych menu nawigacyjnych ....................................................... 80
Dynamiczne ukrywanie kodu JavaScript .................................................................... 83
Tworzenie zegara binarnego za pomoc kodu DHTML .......................................... 85
Uatwienie implementacji Ajax za pomoc moduu JSON ....................................... 88
Utworzenie pokazu slajdw za pomoc kodu DHTML ........................................... 91
Wykorzystanie grafiki wektorowej w PHP ................................................................. 93

Spis treci

23.
24.
25.
26.

Tworzenie narzdzia do wybierania kolorw ............................................................ 96


Tworzenie grafu czy .................................................................................................... 98
Utworzenie interaktywnego kalendarza ................................................................... 101
Tworzenie efektu przewijania map Google .............................................................. 105

Rozdzia 4. Grafika ..................................................................................................................... 113


27.
28.
29.
30.
31.
32.
33.

Tworzenie miniaturek ................................................................................................... 113


Tworzenie atrakcyjnej grafiki za pomoc SVG ......................................................... 115
Uproszczenie obsugi grafiki dziki wykorzystaniu obiektw ............................. 118
Podzia obrazu na kilka mniejszych ........................................................................... 126
Tworzenie wykresw w PHP ...................................................................................... 130
Nakadanie obrazw ..................................................................................................... 132
Dostp do zdj iPhoto z poziomu PHP ................................................................... 136

Rozdzia 5. Bazy danych i XML ................................................................................................. 149


34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.

Projektowanie lepszego schematu SQL ..................................................................... 149


Uniwersalny dostp do bazy danych ......................................................................... 154
Tworzenie dynamicznych obiektw dostpu do bazy danych ................................ 156
Generowanie instrukcji CRUD dla baz danych ........................................................ 160
Zastosowanie wyrae regularnych do atwego czytania dokumentw XML .. 169
Eksportowanie schematu bazy danych w formacie XML ...................................... 172
Prosty mechanizm obsugi zapyta do bazy danych w formacie XML ............... 174
Generowanie kodu SQL ............................................................................................... 175
Generowanie kodu PHP dostpu do bazy danych .................................................. 178
Konwersja CSV na PHP ................................................................................................ 184
Odczyt danych ze stron WWW ................................................................................... 187
Odczytywanie danych z arkuszy Excela wgranych na serwer ................................ 192
adowanie danych z Excela do bazy danych ........................................................... 196
Przeszukiwanie dokumentw programu Microsoft Word .................................... 200
Dynamiczne tworzenie dokumentw RTF ............................................................... 203
Dynamiczne tworzenie arkuszy Excela ..................................................................... 208
Tworzenie kolejki wiadomoci .................................................................................... 213

Rozdzia 6. Projektowanie aplikacji .......................................................................................... 217


51.
52.
53.
54.
55.
56.
57.

Tworzenie modularnych interfejsw ......................................................................... 217


Obsuga tekstu Wiki ...................................................................................................... 221
Przeksztacanie dowolnych obiektw na tablice ...................................................... 224
Tworzenie prawidowego kodu XML ........................................................................ 227
Rozwizanie problemu podwjnego przesyania .................................................... 230
Tworzenie spersonalizowanych raportw ................................................................ 234
Tworzenie systemu logowania .................................................................................... 236

Spis treci

58.
59.
60.
61.
62.
63.
64.
65.
66.

Zabezpieczenia z wykorzystaniem rl ....................................................................... 241


Migracja do hase MD5 ................................................................................................. 248
Zastosowanie moduu mod_rewrite do tworzenia uytecznych adresw URL ........252
Utworzenie mechanizmu przekierowania reklam ................................................... 257
Wykorzystanie przycisku Buy Now serwisu PayPal .............................................. 260
Odczytywanie informacji o lokalizacji uytkownikw aplikacji ................................ 269
Import informacji z plikw vCard .............................................................................. 270
Tworzenie plikw vCard na podstawie danych aplikacji ...................................... 273
Tworzenie koszyka na zakupy .................................................................................... 274

Rozdzia 7. Wzorce projektowe ................................................................................................. 283


67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.

Obserwacja obiektw .................................................................................................... 284


Tworzenie obiektw z wykorzystaniem wzorca Fabryka Abstrakcyjna ............. 287
Elastyczne tworzenie obiektw z wykorzystaniem wzorca Metoda Fabrykujca ....290
Wyodrbnienie kodu konstrukcyjnego za pomoc wzorca Budowniczy ............ 292
Oddzielenie czci co od jak za pomoc wzorca Strategia ............................. 296
czenie dwch moduw z wykorzystaniem wzorca Adapter ........................... 299
Pisanie przenonego kodu z wykorzystaniem wzorca Most ................................. 302
Rozszerzalne przetwarzanie z wykorzystaniem
wzorca acuch odpowiedzialnoci .......................................................................... 305
Podzia rozbudowanych klas na mniejsze
z wykorzystaniem wzorca Kompozyt ....................................................................... 309
Uproszczenie interfejsu API z wykorzystaniem wzorca Fasada ........................... 311
Tworzenie staych obiektw za pomoc wzorca Singleton .................................... 315
Uatwienie wykonywania operacji z danymi
dziki zastosowaniu wzorca Wizytator ..................................................................... 318

Rozdzia 8. Testowanie .............................................................................................................. 323


79.
80.
81.
82.
83.
84.
85.

Testowanie kodu za pomoc testw jednostkowych .............................................. 323


Generowanie wasnych testw jednostkowych ....................................................... 325
Wyszukiwanie niesprawnych czy ........................................................................... 329
Testowanie aplikacji z wykorzystaniem symulowanych uytkownikw ........... 331
Testowanie aplikacji z wykorzystaniem robotw .................................................... 335
Testowanie witryny za pomoc aplikacji typu pajk .......................................... 339
Automatyczne generowanie dokumentacji ............................................................... 343

Rozdzia 9. Alternatywne interfejsy uytkownika .................................................................... 347


86.
87.
88.
89.

Tworzenie map z wykorzystaniem systemu MapServer ........................................ 347


Tworzenie interfejsw GUI z wykorzystaniem biblioteki GTk ............................. 357
Wysyanie nagwkw RSS do komunikatorw za pomoc protokou Jabber ....... 360
Komunikacja z aplikacj internetow za pomoc IRC ............................................ 367

Spis treci

90.
91.
92.
93.
94.

Odczyt rde RSS na konsoli PSP ............................................................................. 369


Wyszukiwanie w Google wedug sw kluczowych ............................................... 372
Utworzenie nowego interfejsu witryny Amazon.com ............................................ 378
Wysyanie wiadomoci SMS za pomoc komunikatorw ..................................... 381
Generowanie animacji Flasha ...................................................................................... 385

Rozdzia 10. Dla zabawy ............................................................................................................ 395


95.
96.
97.
98.
99.
100.

Tworzenie wasnych map Google .............................................................................. 395


Tworzenie dynamicznych list odtwarzania .............................................................. 400
Utworzenie centrum wymiany plikw multimedialnych ...................................... 403
Sprawdzanie statusu gry sieciowej za pomoc skryptu PHP ................................ 408
Wikipedia na konsoli PSP ............................................................................................ 410
Gdzie jest lepsza pogoda? ............................................................................................ 417

Skorowidz ................................................................................................................................... 421

Spis treci

Obserwacja obiektw

ROZDZIA SIDMY

Sposoby 67. 78.


W 1994 roku Erich Gamma, Richard Helm, Ralph Johnson i John Vlissides opublikowali
ksik pt. Design Patterns1 (Addison-Wesley). Z powodu doskonaej treci publikacja
bardzo szybko zostaa zaliczona do klasyki literatury informatycznej. Jednym z jej osigni
byo wypromowanie nowego metajzyka w dziedzinie inynierii i architektury systemw.
Gwna idea ksiki opracowanie zbioru wzorcw struktury obiektw zostaa zapoyczona z budownictwa i przystosowana do programowania.
Zbir 40 wzorcw projektowych zaprezentowanych w ksice Design Patterns to efekt
wielu lat dowiadcze. Kady z nich opisano w formie neutralnej pod wzgldem jzyka.
Wikszo mona zastosowa w kadym rodowisku projektowym.
Niektre wzorce projektowe, na przykad Iterator, opracowano specjalnie po to,
by uzupeni braki jzykw programowania (w przypadku wzorca Iterator
chodzi o jzyk C++). W PHP jest wbudowana implementacja tego wzorca
konstrukcja foreach.

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.

Polskie wydanie Wzorce projektowe Wydawnictwa Naukowo-Techniczne 2005 przyp. tum.

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

Tworzenie obiektw z wykorzystaniem wzorca Fabryka Abstrakcyjna

Zobacz te
Przeksztacanie dowolnych obiektw na tablice [Sposb 53.].
Tworzenie kolejki wiadomoci [Sposb 50.].
SPOSB

Tworzenie obiektw z wykorzystaniem wzorca

68. Fabryka Abstrakcyjna

Wykorzystanie wzorca projektowego Fabryka Abstrakcyjna do ledzenia typu tworzonych obiektw.

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.

Rysunek 7.2. Klasy Record i RecordFactory


Obiekty-fabryki czsto tworz wicej ni jeden typ obiektw. Dla uproszczenia
przykadu ograniczyem obiekt-fabryk do tworzenia tylko jednego typu obiektw.

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.

Tworzenie obiektw z wykorzystaniem wzorca Fabryka Abstrakcyjna

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

Tworzenie obiektw z wykorzystaniem wzorca Fabryka Abstrakcyjna


class RecordFactory
{
public static function createRecord( $id, $first, $last,
$addr1, $addr2, $city, $state, $postal, $country )
{
if ( strlen( $country ) > 0 && $country != "USA" )
return new ForeignRecord( $id, $first, $last,
$addr1, $addr2, $city, $state, $postal, $country );
else
return new USRecord( $id, $first, $last,
$addr1, $addr2, $city, $state, $postal );
}
}
function readRecords()
{
$records = array();
$records []= RecordFactory::createRecord(
1, "Jack", "Herrington", "4250 San Jaquin Dr.", "",
"Los Angeles", "CA", "90210", ""
);
$records []= RecordFactory::createRecord(
1, "Maria", "Kowalska", "Pstrowskiego 4", "",
"Malbork", "pomorskie", "82-200", "Polska"
);
return $records;
}
$records = readRecords();
foreach( $records as $r )
{
$class = new ReflectionClass( $r );
print $class->getName()." - ".$r->id." - ".$r->first." - ".$r->last."\n";
}
?>

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.

Elastyczne tworzenie obiektw z wykorzystaniem wzorca Metoda Fabrykujca

Tworzenie obiektu bazy danych


Obiekt-fabryka tworzy wszystkie typy obiektowe powizane z poszczeglnymi
tabelami w bazie danych.
Tworzenie przenonych obiektw
Obiekt-fabryka tworzy rne obiekty w zalenoci od typu systemu operacyjnego,
w ktrym dziaa kod, bd od typw baz danych, z ktrymi aplikacja si czy.
Tworzenie wedug standardu
Aplikacja obsuguje rnorodne standardy formatw plikw i wykorzystuje
obiekt-fabryk do tworzenia obiektw odpowiednich dla poszczeglnych typw
plikw. Obiekty czytajce pliki mog si zarejestrowa w obiekcie-fabryce w celu
dodania obsugi plikw bez koniecznoci modyfikacji klientw.
Wykorzystywanie wzorcw projektowych przez pewien czas pozwala programicie na
uzyskanie wyczucia co do tego, kiedy warto zastosowa okrelony wzorzec. Wzorzec
Fabryka Abstrakcyjna stosuje si w przypadku tworzenia duej liczby obiektw rnych
typw. Jak mona si przekona, zmiany typw tworzonych obiektw lub sposobu ich
tworzenia czsto powoduj konieczno wielu modyfikacji w kodzie. W przypadku zastosowania klasy-fabryki zmian trzeba wprowadzi tylko w jednym miejscu.

Zobacz te
Elastyczne tworzenie obiektw z wykorzystaniem wzorca Metoda Fabrykujca
[Sposb 69.].
SPOSB

Elastyczne tworzenie obiektw

69. z wykorzystaniem wzorca Metoda Fabrykujca


Wykorzystanie wzorca Metoda Fabrykujca podczas tworzenia obiektw w celu umoliwienia klasom
pochodnym modyfikacji typw tworzonych obiektw.

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

Elastyczne tworzenie obiektw z wykorzystaniem wzorca Metoda Fabrykujca

Rysunek 7.3. Zwizki pomidzy klasami RecordReader i Record


Listing 7.3. Przykad metod fabrycznych klasy
<?php
class Record
{
public $id = null;
public $first = null;
public $last = null;
public function Record( $id, $first, $last )
{
$this->id = $id;
$this->first = $first;
$this->last = $last;
}
}
class RecordReader
{
function readRecords()
{
$records = array();
$records []= $this->newRecord( 1, "Jack", "Herrington" );
$records []= $this->newRecord( 2, "Lori", "Herrington" );
$records []= $this->newRecord( 3, "Megan", "Herrington" );
return $records;
}
protected function newRecord( $id, $first, $last )
{
return new Record( $id, $first, $last );
}
}
$rr = new RecordReader();
$records = $rr->readRecords();
foreach( $records as $r )
{
print $r->id." - ".$r->first." - ".$r->last."\n";
}
?>

Wzorce projektowe

291

SPOSB

69.

SPOSB

70.

Wyodrbnienie kodu konstrukcyjnego za pomoc wzorca Budowniczy

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

Po utworzeniu egzemplarza obiektu RecordReader nastpuje wywoanie jego metody


readRecords(), ktra z kolei wywouje metod newRecord w celu utworzenia wszystkich obiektw Record. Utworzone obiekty s nastpnie wywietlane na konsoli za pomoc ptli foreach.
W najbardziej widoczny sposb wzorzec Metoda Fabrykujca zastosowano w interfejsie
API XML DOM organizacji W3C instalowanym w ramach bazowej instalacji PHP 5.
Obiekt DOMDocument, ktry spenia rol korzenia kadego drzewa DOM, zawiera zbir
metod-fabryk: createElement(), createAttrribute(), createTextNode() itd.
Implementacje pochodne od obiektu DOMDocument mog przesania te metody w celu
zmiany obiektw tworzonych podczas adowania drzew XML z dysku, zmiennych tekstowych lub tworzonych w locie.
Podobnie jak w przypadku wzorca Fabryka Abstrakcyjna najwaniejsz przesank do
wykorzystania wzorca Metoda Fabrykujca jest sytuacja, gdy piszemy duo kodu tworzcego obiekty. Dziki zastosowaniu wzorca Fabryka Abstrakcyjna bd Method Factory zyskujemy pewno, e jeli zmieni si typ obiektw, ktry chcemy tworzy, lub sposb ich
tworzenia, zmiany w kodzie bd minimalne.

Zobacz te
Tworzenie obiektw z wykorzystaniem wzorca Fabryka Abstrakcyjna [Sposb 68.].
SPOSB

Wyodrbnienie kodu konstrukcyjnego

70. za pomoc wzorca Budowniczy

Wykorzystanie wzorca Budowniczy do wyodrbnienia kodu, ktry wykonuje rutynowe operacje


konstrukcyjne, takie jak tworzenie dokumentw HTML lub tekstu wiadomoci e-mail.

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

Wyodrbnienie kodu konstrukcyjnego za pomoc wzorca Budowniczy

Przykadem kodu konstrukcyjnego moe by kod odczytujcy dokument XML z dysku


i tworzcy jego reprezentacj w pamici. Innym moe by modu tworzcy wiadomoci
e-mail przypominajce klientom o tym, e upyn termin patnoci.
W niniejszym podrozdziale poka przykad tworzenia wiadomoci o spnionych patnociach. Zrobi to jednak sposobem: wykorzystam wzorzec Budowniczy, dziki czemu
kod tworzcy wiadomo w formacie HTML bdzie mona wykorzysta do tworzenia
wiadomoci w formacie XHTML lub tekstowym.
W kodzie, ktry pisze wiadomo o spnionej patnoci, zamierzam wykorzysta
obiekt-konstruktora zamiast bezporedniego tworzenia cigu znakw. Obiekt ten bdzie
zawiera szereg metod, tak jak pokazano na rysunku 7.4. Kod tworzcy wiadomo jest
umieszczony pomidzy wywoaniami metod startBody() oraz endBody(). Metoda
addText() dodaje tekst wiadomoci, natomiast addBreak() znak zakoczenia wiersza.

Rysunek 7.4. Hierarchia obiektw tworzcych wiadomoci

Klasa abstrakcyjna OutputBuilder ma kilka zmaterializowanych egzemplarzy. Jednym


z nich jest HTMLBuilder tworzcy kod HTML. Klas jej pochodn jest XHTMLBuilder
klasa modyfikujca dziaanie klasy nadrzdnej w sposb wystarczajcy do utworzenia wyniku zgodnego z XHTML-em. Ostatni klas jest TextBuilder, ktra tworzy reprezentacj wiadomoci w formacie zwykego tekstu.

Wzorce projektowe

293

SPOSB

70.

SPOSB

70.

Wyodrbnienie kodu konstrukcyjnego za pomoc wzorca Budowniczy

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

Wyodrbnienie kodu konstrukcyjnego za pomoc wzorca Budowniczy


print "HTML:\n\n";
$html = new HTMLBuilder();
buildDocument( $html );
echo( $html->getOutput() );
print "\nXHTML:\n\n";
$xhtml = new XHTMLBuilder();
buildDocument( $xhtml );
echo( $xhtml->getOutput() );
print "\nTekst:\n\n";
$text = new TextBuilder();
buildDocument( $text );
echo( $text->getOutput() );
?>

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.

Wywietli si wynik dziaania trzech obiektw konstrukcyjnych. Pierwszy to wersja


wiadomoci w formacie HTML z prawidowymi znacznikami HTML oraz znacznikiem
<br>. Kod konstrukcyjny dla XHTML-a nieco zmodyfikowa wiadomo przeksztaci znacznik <br> na <br />. Wersja tekstowa to po prostu zwyky tekst. Znak koca
wiersza zastpiono znakiem powrotu karetki.
Na pocztku kodu znajduje si definicja klasy abstrakcyjnej OutputBuilder, za ktr
wystpuj poszczeglne egzemplarze klas dla rnych formatw wyniku. Obiekt konstruktora wykorzystano w funkcji buildDocument(), ktra tworzy wiadomo. Kod na
kocu skryptu to testy funkcji buildDocument() dla kadego z typw obiektw konstrukcyjnych.

Wzorce projektowe

295

SPOSB

70.

SPOSB

71.

Oddzielenie czci co od jak za pomoc wzorca Strategia

Wzorzec Budowniczy w aplikacji internetowej w PHP mona wykorzysta w kilku miejscach:


Odczyt plikw
W operacjach przetwarzania plikw mona wykorzysta wzorzec Budowniczy
do oddzielenia operacji analizy treci pliku od tworzenia struktur danych w pamici
z danymi z pliku.
Zapis plikw
Zgodnie z tym, co pokazaem w tym podrozdziale, wzorzec Budowniczy mona
wykorzysta do tworzenia wielu formatw wynikowych za pomoc jednego
systemu tworzenia dokumentw.
Generowanie kodu
Wzorzec Budowniczy mona zastosowa do generowania kodu w wielu jzykach
za pomoc jednego systemu generujcego.
W rodowisku .NET wykorzystuje si wzorzec Budowniczy do tworzenia kodu HTML
strony wynikowej, tak aby za pomoc tej samej konstrukcji sterujcej generowa rne
odmiany kodu HTML w zalenoci od typu przegldarki dajcej strony.
SPOSB

Oddzielenie czci co od jak

71. za pomoc wzorca Strategia

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

Oddzielenie czci co od jak za pomoc wzorca Strategia

Rysunek 7.5. Relacje pomidzy obiektami CarChooser, CarWeighter i Car


Listing 7.5. Zastosowanie wzorca Strategia
<?php
class Car
{
public $name;
public $speed;
public $looks;
public $mileage;
public function Car( $name, $speed, $looks, $mileage )
{
$this->name = $name;
$this->speed = $speed;
$this->looks = $looks;
$this->mileage = $mileage;
}
}
class CarWeighter
{
private function diff( $a, $b )
{
return abs( $a - $b );
}
public function weight( $a, $b )
{
$d = 0;
$d += $this->diff( $a->speed, $b->speed );
$d += $this->diff( $a->looks, $b->looks );
$d += $this->diff( $a->mileage, $b->mileage );
return ( 0 - $d );
}
}
class CarChooser
{
private $ideal;
private $alg;
function CarChooser( $ideal, $alg )
{

Wzorce projektowe

297

SPOSB

71.

SPOSB

71.

Oddzielenie czci co od jak za pomoc wzorca Strategia


$this->ideal = $ideal;
$this->alg = $alg;
}
public function choose( $carlist )
{
$minrank = null;
$found = null;
$alg = $this->alg;
foreach( $carlist as $car )
{
$rank = $alg->weight( $this->ideal, $car );
if ( !isset( $minrank ) ) $minrank = $rank;
if ( $rank >= $minrank )
{
$minrank = $rank;
$found = $car;
}
}
return $found;
}
}
function pickCar( $car )
{
$carlist = array();
$carlist []= new Car( "rakieta", 90, 30, 10 );
$carlist []= new Car( "rodzinny", 45, 30, 55 );
$carlist []= new Car( "adny", 40, 90, 10 );
$carlist []= new Car( "ekonomiczny", 40, 40, 90 );
$cw = new CarWeighter();
$cc = new CarChooser( $car, $cw );
$found = $cc->choose( $carlist );
echo( $found->name."\n" );
}
pickCar( new Car( "idealny", 80, 40, 10 ) );
pickCar( new Car( "idealny", 40, 90, 10 ) );
?>

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

czenie dwch moduw z wykorzystaniem wzorca Adapter

Wykorzystanie sposobu
Do uruchomienia kodu wykorzystamy interpreter PHP dziaajcy w wierszu polecenia:
% php strategy.php
rakieta
adny

Z uzyskanego wyniku wida, e samochd, jaki aplikacja poleca mi w przypadku, gdy


chodzi mi o szybko, nazywa si rakieta (doskonae okrelenie). W przypadku, gdy interesuje mnie co bardziej seksownego, aplikacja proponuje samochd adny wietnie!
Kod, ktry wyciga wniosek dotyczcy tego, czy samochd spenia kryteria, jest cakowicie oddzielony od kodu, ktry przeszukuje list samochodw i wybiera z niej jeden
pojazd. Algorytm porwnujcy samochd z wprowadzonymi kryteriami mona zmodyfikowa niezalenie od kodu wybierajcego samochd z posortowanej listy. Na przykad
w algorytmie porwnujcym samochody mona uwzgldni marki, ktrymi ostatnio
interesowalimy si, lub te, ktrych w ostatnim czasie bylimy posiadaczami. Mona rwnie zmodyfikowa kod wybierajcy samochody tak, by proponowa trzy z pocztku listy.
W ten sposb uytkownik miaby dodatkowe moliwoci wyboru.
SPOSB

czenie dwch moduw

72. z wykorzystaniem wzorca Adapter


Wykorzystanie klasy-adaptera do przenoszenia danych pomidzy dwoma moduami w sytuacji,
gdy nie chcemy modyfikowa interfejsu API adnego z moduw.

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.

czenie dwch moduw z wykorzystaniem wzorca Adapter

Rysunek 7.6. Adapter umieszczony pomidzy kodem tworzcym wykresy a danymi

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

czenie dwch moduw z wykorzystaniem wzorca Adapter


for( $r = 0; $r < $this->data->getCount(); $r++ )
{
$n = $this->data->getName( $r );
$v = $this->data->getValue( $r );
$s = ( $v - $this->dmin ) * $ratio;
echo( sprintf( "%10s : ", $n ) );
for( $st = 0; $st < $s; $st++ ) { echo("*"); }
echo( "\n" );
}
}
}
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(
$this->records []= new Record(
}

"Janusz", 23, 26000 );


"Beata", 24, 29000 );
"Stefania", 28, 42000 );
"Jerzy", 28, 120000 );
"Grzegorz", 43, 204000 );

public function getRecords()


{
return $this->records;
}
}
class RecordGraphAdapter extends TextGraphDataSource
{
private $records;
public function RecordGraphAdapter( $rl )
{
$this->records = $rl->getRecords();
}
public function getCount( )
{
return count( $this->records );
}
public function getName( $row )
{
return $this->records[ $row ]->name;
}
public function getValue( $row )
{
return $this->records[ $row ]->salary;
}
}

Wzorce projektowe

301

SPOSB

72.

SPOSB

73.

Pisanie przenonego kodu z wykorzystaniem wzorca Most


$rl = new RecordList();
$ga = new RecordGraphAdapter( $rl );
$tg = new TextGraph( $ga );
$tg->render();
?>

Pocztek skryptu to kod odpowiedzialny za tworzenie wykresu. Zdefiniowano w nim


klas abstrakcyjn TextGraphDataSource oraz klas TextGraph wykorzystujc klas
TextGraphDataSource jako format danych. W rodkowej czci skryptu zdefiniowano klasy Record i RecordList (zawierajce dane do utworzenia wykresu). W trzeciej
czci zdefiniowano klas RecordGraphAdapter, ktra przystosowuje klas RecordList do wykorzystania jako rdo danych wykresu.
Kod testowy na pocztku skryptu najpierw tworzy obiekt RecordList, a nastpnie
obiekt-adapter oraz obiekt TextGraph, ktry odwouje si do adaptera. Wykres tworzy
si poprzez odczyt danych z adaptera.

Wykorzystanie sposobu
Do uruchomienia kodu wykorzystamy interpreter PHP dziaajcy w wierszu polecenia:
% php adapter.php
Janusz :
Beata : *
Stefania : ****
Jerzy : **********************
Grzegorz : ****************************************

Najmniej zarabia Janusz, a najwicej Grzegorz. Na wykresie zastosowano automatyczne


skalowanie, dlatego obok Janusza nie ma gwiazdek (minimum), natomiast obok Grzegorza
wywietla si 40 gwiazdek (maksimum). wietnie ci idzie, Grzegorz! Waniejsze w tym
kodzie jest jednak to, e konwersja danych przebiega bez problemu, bez koniecznoci
zagbiania si w szczegy implementacji klasy Record.
Wzorzec projektowy Adapter warto stosowa zawsze wtedy, gdy wystpuj dwa interfejsy API, ktre musz ze sob wsppracowa, a modyfikacja adnego z tych interfejsw nie wchodzi w rachub.
SPOSB

73.

Pisanie przenonego kodu z wykorzystaniem wzorca Most


Wykorzystanie wzorca Most w celu ukrycia szczegw implementacji obiektw lub modyfikacji
implementacji na podstawie rodowiska.

W jednej z firm, w ktrej pracowaem, tworzylimy du aplikacj w C++, ktra dziaaa na


wielu platformach. Podczas prac nad ni wielokrotnie wykorzystalimy wzorzec Most.
Jego podstawow cech jest moliwo ukrycia czci implementacji klasy w innej klasie
po to, by nie dopuci do ogldania implementacji przez innych programistw lub dlatego, e cz implementacji zaley od platformy.

302

Wzorce projektowe

Pisanie przenonego kodu z wykorzystaniem wzorca Most

W przykadzie zaprezentowanym w niniejszym podrozdziale, w celu pokazania zalet


wzorca Most, wykorzystamy przypadek, w ktrym cz implementacji zaley od platformy. Na rysunku 7.7 pokazano zwizki pomidzy klasami TableCreator i TableCreatorImp. Rola pierwszej z nich polega na tworzeniu tabel w docelowej bazie danych. Klas implementacyjn TableCreatorImp zdefiniowano w innym pliku,
ktry jest wczany z katalogu specyficznego dla okrelonego typu bazy danych.

Rysunek 7.7. Klasa TableCreator i jej klasa implementacyjna

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" );
?>

Kod pokazany na listingu 7.8 zapiszemy w pliku mysql/sql.php.


Listing 7.8. Przykadowa klasa implementacyjna dla bazy danych MySQL
<?php
class TableCreatorImp
{
static public function createTable( $name )
{
echo( "Wersja klasy createTable dla bazy MySQL tworzca tabel $name\n" );
}
}
?>

Wzorce projektowe

303

SPOSB

73.

SPOSB

73.

Pisanie przenonego kodu z wykorzystaniem wzorca Most

Kod pokazany na listingu 7.9 zapiszemy w pliku oracle/sql.php.


Listing 7.9. Przykadowa klasa implementacyjna dla bazy danych Oracle
<?php
class TableCreatorImp
{
static public function createTable( $name )
{
echo( " Wersja klasy createTable dla bazy Oracle tworzca tabel $name\n"
);
}
}
?>

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

A oto wersja dla bazy danych Oracle:


%php -d include_path = '.:/usr/local/php5/lib/php:oracle' bridge.php
Wersja klasy createTable dla bazy Oracle 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

Rozszerzalne przetwarzanie z wykorzystaniem wzorca acuch odpowiedzialnoci


SPOSB

Rozszerzalne przetwarzanie z wykorzystaniem wzorca

74. acuch odpowiedzialnoci

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.

Rozszerzalne przetwarzanie z wykorzystaniem wzorca acuch odpowiedzialnoci

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

Rozszerzalne przetwarzanie z wykorzystaniem wzorca acuch odpowiedzialnoci


{
$this->base = $base;
$this->imgurl = $imgurl;
}
public function getRealURL( $url )
{
if ( preg_match( "|^".$this->base."(.*?)$|", $url, $matches ) )
{
return $this->imgurl.$matches[1];
}
return null;
}
}
class DocumentURLHandler extends URLHandler
{
private $base;
private $story_url;
public function DocumentURLHandler( $base, $story_url )
{
$this->base = $base;
$this->story_url = $story_url;
}
public function getRealURL( $url )
{
if ( preg_match( "|^".$this->base."(.*?)/(.*?)/(.*?)$|", $url, $matches )
)
{
return $this->story_url.$matches[1].$matches[2].$matches[3];
}
return null;
}
}

$ih = new ImageURLHandler( "http://mysite.com/images/",


"http://mysite.com/image.php?img=" );
URLMapper::instance()->addHandler( $ih );
$ih = new DocumentURLHandler( "http://mysite.com/story/",
"http://mysite.com/story.php?id=" );
URLMapper::instance()->addHandler( $ih );
$testurls
$testurls
$testurls
$testurls
$testurls
$testurls

= 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";

foreach( $testurls as $in )


{
$out = URLMapper::instance()->mapURL( $in );
print "$in\n --> $out\n\n";
}
?>

Wzorce projektowe

307

SPOSB

74.

SPOSB

74.

Rozszerzalne przetwarzanie z wykorzystaniem wzorca acuch odpowiedzialnoci

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

Podzia rozbudowanych klas na mniejsze z wykorzystaniem wzorca Kompozyt


SPOSB

Podzia rozbudowanych klas na mniejsze

75. z wykorzystaniem wzorca Kompozyt

Wykorzystanie wzorca Kompozyt w celu podzielenia rozbudowanych klas na mniejsze.

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.

Rysunek 7.9. Zoona klasa Customer wraz z jej klasami potomnymi

Wzorce projektowe

309

SPOSB

75.

SPOSB

75.

Podzia rozbudowanych klas na mniejsze z wykorzystaniem wzorca Kompozyt

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

Uproszczenie interfejsu API z wykorzystaniem wzorca Fasada


$cust = new Customer();
$cust->Load( 1 );
print( $cust );
print( "\n" );
?>

Wykorzystanie sposobu
Powyszy skrypt uruchomimy, wykorzystujc interpreter PHP dziaajcy w wierszu
polecenia:
%php composite.php
George Bush

Skrypt tworzy nowego klienta i aduje rekord numer 1. W przykadzie zakodowaem


na sztywno dane George W. Busha. Nastpnie skrypt wywietla informacje zapisane
w obiekcie Customer.
Nie ma w tym nic wielkiego. Idea przykadu jest rwnie skuteczna jak prosta. Nie naley
uywa megaklas zawierajcych po 100 pl. O wiele lepiej jest posugiwa si niewielkimi pogrupowanymi klasami, takimi jak CustomerName i CustomerAddress, ktre
mona wkomponowa w wiksze struktury w tym przypadku klas Customer. Co
wicej, klas CustomerAddress mona wykorzysta w innych klasach, gdzie istnieje
potrzeba wykorzystania adresw pocztowych.
Wzorzec Kompozyt warto zastosowa w przypadku, gdy dane obiektu s rozproszone
w wielu tabelach bazy danych. Kadej z tabel powinna odpowiada wasna klasa lub
inna struktura danych.
Wzorzec Kompozyt uatwia optymalizacj odczytu informacji z bazy danych. Poniewa
zaadowanie kadego z obiektw podrzdnych, takich jak adres, wymaga osobnego zapytania, dane mona odczytywa w niewielkich porcjach. Inaczej mwic, mona opni adowanie okrelonego podobiektu do czasu, kiedy zapisane w nim dane bd potrzebne. Dziki temu kod nie musi pobiera setek pl z wielu tabel w przypadku, kiedy
potrzebne jest jedynie imi i nazwisko klienta.
SPOSB

Uproszczenie interfejsu API

76. z wykorzystaniem wzorca Fasada


Wykorzystanie wzorca Fasada w celu uproszczenia interfejsu API prezentowanego innym programistom.

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.

Uproszczenie interfejsu API z wykorzystaniem wzorca Fasada

Rysunek 7.10. Interfejs API systemu rejestrowania z prostym przykadem wzorca Fasada

Pokazany interfejs API umoliwia rejestrowanie zdarze w formacie XML, tekstowym


lub obu. Jako programista jestem pod wraeniem umiejtnoci autora. Wydaje si, e s
dostpne metody dla wszystkich informacji: rozpoczcia komunikatu, wprowadzenia
tekstu, porzdkowania, a nawet obsugi formatu XML i tekstowego.
Naprawd jednak interesuje mnie, jakich metod mam uywa i kiedy. Wanie do tego
suy wzorzec Fasada jego zastosowanie daje pewno prawidowego wykorzystywania
interfejsu API. Wzorzec Fasada zastosowany w tym przykadzie to lista trzech funkcji wywietlanych w ramce, przez ktr przechodzi linia po lewej stronie rysunku. Ta linia to
rodzaj teoretycznej bariery, na ktrej wywietla si informacja: jestem odpowiedzialny
za operacje zdefiniowane po prawej stronie; wywouj moje metody, a ja zajm si reszt.
Zastosowanie wzorca Fasada nie tylko upraszcza interfejsy API, ale take ukrywa szczegy implementacji przed klientami. Implementacja moe si zmieni, a klient nawet tego
nie zauway. Jest to rwnie wane jak uproszczenie interfejsw. Naley pamita, e
lune wizanie obiektw oznacza stabilne i niezawodne systemy.

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

Uproszczenie interfejsu API z wykorzystaniem wzorca Fasada

Kod pokazany na listingu 7.13 zapiszemy w pliku log.php.


Listing 7.13. Zastosowanie wzorca Fasada
<?php
require( "log_impl.php" );
function log_start( $fileName )
{
Log::instance()->start( $fileName );
}
function log_message( $message )
{
Log::instance()->add( $message );
}
function log_end()
{
Log::instance()->end();
}
?>

Kod pokazany na listingu 7.14 zapiszemy w pliku log_impl.php.


Listing 7.14. Implementacja wzorca Fasada
<?php
class XMLLog
{
private $fileName;
private $doc;
private $log;
public function XMLLog( $fileName )
{
$this->fileName = $fileName;
$this->doc = new DOMDocument();
$this->doc->formatOutput = true;
$this->log = $this->doc->createElement( "log" );
$this->doc->appendChild( $this->log );
}
public function add( $message )
{
$mess_obj = $this->doc->createElement( "message" );
$text = $this->doc->createTextNode( $message );
$mess_obj->appendChild( $text );
$this->log->appendChild( $mess_obj );
}
public function close()
{
$this->doc->save( $this->fileName );
}
}
class TextLog
{
private $fh;

Wzorce projektowe

313

SPOSB

76.

SPOSB

76.

Uproszczenie interfejsu API z wykorzystaniem wzorca Fasada


public function TextLog( $fileName )
{
$this->fh = fopen( $fileName, "w" );
}
public function add( $message )
{
fprintf( $this->fh, $message."\n" );
}
public function close()
{
fclose( $this->fh );
}
}
class Log
{
private $xmlLog = null;
private $textLog = null;
public function Log()
{
}
public function start( $fileName )
{
$this->xmlLog = new XMLLog( $fileName.".xml" );
$this->textLog = new TextLog( $fileName.".txt" );
}
public function add( $message )
{
$this->xmlLog->add( $message );
$this->textLog->add( $message );
}
public function end()
{
$this->xmlLog->close();
$this->textLog->close();
}
public static function instance()
{
static $inst = null;
if ( !isset( $inst ) ) $inst = new Log();
return $inst;
}
}
?>

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

Tworzenie staych obiektw za pomoc wzorca Singleton


Zamknicie aplikacji
% cat mylog.xml
<?xml version="1.0"?>
<log>
<message>Otwarcie aplikacji</message>
<message>Zarejestrowanie komunikatu</message>
<message>Zamkni&#x119;cie aplikacji</message>
</log>

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.

Tworzenie staych obiektw za pomoc wzorca Singleton


Wykorzystanie wzorca Singleton do utworzenia obiektw, ktre wystpuj w systemie jako pojedyncze
egzemplarze.

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?).

Rysunek 7.11. Obiekt Singleton dostpu do bazy danych

Wzorce projektowe

315

SPOSB

77.

SPOSB

77.

Tworzenie staych obiektw za pomoc wzorca Singleton

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

Wykonanie przykadu dowodzi tego, e wiele wywoa statycznej metody get_handle()


za kadym razem zwraca ten sam obiekt, a tym samym zapewnia skorzystanie z tego
samego uchwytu do bazy danych.

316

Wzorce projektowe

Tworzenie staych obiektw za pomoc wzorca Singleton

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.

Uatwienie wykonywania operacji z danymi dziki zastosowaniu wzorca Wizytator

Z pierwszego zrzutu wida, e na licie znajduje si pierwszy stan Floryda. Drugi


zrzut dowodzi, e do listy wspdzielonego obiektu dodano drugi stan Kentucky.
Jeli mam by szczery, nie polecam zbyt czstego wykorzystywania wzorca Singleton.
Wedug mnie jest on wykorzystywany zbyt czsto. Niejednokrotnie miaem do czynienia
z kodem, gdzie stosowano pewne niezgrabne obejcia w celu wykorzystania obiektw
Singleton. Bardzo czsto oznaczao to niepoprawne korzystanie z wzorca Singleton. Jeli
zastosowanie wzorca Singleton wymaga zbyt wielu operacji, moe to oznacza, e wzorzec ten nie zosta zastosowany we waciwym miejscu.
SPOSB

Uatwienie wykonywania operacji

78. z danymi dziki zastosowaniu wzorca Wizytator


Wykorzystanie wzorca Wizytator do oddzielenia przegldania danych od ich przetwarzania.

Na pocztku mojej kariery programistycznej tworzyem wiele programw wykonujcych


obliczenia naukowe, w ktrych wykorzystywano systemy zbierania danych. Byy to
systemy rejestrujce prbki danych w odstpach co 3 mikrosekundy inaczej mwic,
333 333 prbek na sekund. Taka czstotliwo pobierania informacji oznaczaa 38 megabajtw danych na minut! W przypadku dugo trwajcych sesji rozmiar pliku z danymi z atwoci przekracza kilka gigabajtw. Nie trzeba dodawa, e rejestrowanie takich iloci informacji i zapisywanie ich na dysku bez zastosowania specjalnych chwytw
sprawiao kopoty.
Osobnym problemem byo analizowanie tych danych. W jaki sposb analizowa plik
o rozmiarze kilku gigabajtw, jeli komputer, ktrego uywamy do tego celu, ma zaledwie
128 MB pamici? Wiadomo, e trzeba podzieli dane. Oznacza to odczytywanie pliku
sekcja po sekcji, wyrzucanie niepotrzebnych danych z pamici na dysk i wczytywanie
tych, ktre s potrzebne, z dysku do pamici.
Algorytmy stosowane we wspomnianych programach naukowych byy i tak dostatecznie rozbudowane, nawet bez obsugi wymiany danych z dyskiem, a co dopiero z ni.
Aby elegancko rozwiza nasze problemy, zastosowalimy wzorzec Wizytator (ang. Visitor).
Jeden obiekt by odpowiedzialny za wymian danych pomidzy pamici a dyskiem,
a drugi za przetwarzanie ich w pamici.
Na rysunku 7.12 pokazano obiekt RecordList zawierajcy list obiektw Record. Jest
w nim metoda iterate(), ktra pobiera argument w postaci nazwy innej funkcji i wywouje j dla kadego rekordu.
Dziki takiemu podejciu funkcja przetwarzania danych przekazywana do metody iterate() nie musi zna sposobu zarzdzania rekordami w pamici. Jedyne dziaania,
jakie musi wykonywa, to obsugiwa przekazane do niej dane.

318

Wzorce projektowe

Uatwienie wykonywania operacji z danymi dziki zastosowaniu wzorca Wizytator

Rysunek 7.12. Obiekt RecordList z metod iterate

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(
}

"Leszek", 22, 35000 );


"Henryk", 25, 37000 );
"Maria", 42, 65000 );
"Stefania", 45, 80000 );

public function iterate( $func )


{
foreach( $this->records as $r )
{
call_user_func( $func, $r );
}
}
}
$min = 100000;

Wzorce projektowe

319

SPOSB

78.

SPOSB

78.

Uatwienie wykonywania operacji z danymi dziki zastosowaniu wzorca Wizytator


function find_min_salary( $rec )
{
global $min;
if( $rec->salary < $min ) { $min = $rec->salary; }
}
$rl = new RecordList();
$rl->iterate( "find_min_salary", $min );
echo( $min."\n" );
?>

Wykorzystanie sposobu
Do uruchomienia skryptu zaprezentowanego powyej wykorzystamy interpreter PHP
dziaajcy w wierszu polecenia:
% php visitor1.php
35000

Zaprezentowany algorytm wybiera rekord osoby o najniszej pensji spord wszystkich


przetwarzanych rekordw. Kod skryptu jest stosunkowo prosty. Klasa Record zawiera
dane poszczeglnych rekordw. Klasa RecordList aduje si z pewnymi przykadowymi danymi (w praktycznym zastosowaniu dane te mona by odczyta z bazy danych
lub pliku). Metoda iterate() w ptli foreach() przetwarza list rekordw. Metoda
call_user_func() wywouje przekazan do niej funkcj przetwarzajc dane dla
kadego rekordu. W tym przykadzie jest to funkcja find_min_salary(), ktra przeglda poszczeglne rekordy w celu znalezienia najniszej wartoci pensji.

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.

Rysunek 7.13. Implementacja wzorca Wizytator, w ktrej wizytator jest obiektem

320

Wzorce projektowe

Uatwienie wykonywania operacji z danymi dziki zastosowaniu wzorca Wizytator

Zaktualizowany kod zaprezentowano na listingu 7.18.


Listing 7.18. Zaktualizowana wersja implementacji wzorca Wizytator
<?php
class Record
{
public $name;
public $age;
public $salary;
public function Record( $name, $age, $salary )
{
$this->name = $name;
$this->age = $age;
$this->salary = $salary;
}
}
abstract class RecordVisitor
{
abstract function visitRecord( $rec );
}
class RecordList
{
private $records = array();
public function RecordList()
{
$this->records []= new Record(
$this->records []= new Record(
$this->records []= new Record(
$this->records []= new Record(
}

"Leszek", 22, 35000 );


"Henryk", 25, 37000 );
"Maria", 42, 65000 );
"Stefania", 45, 80000 );

public function iterate( $vis )


{
foreach( $this->records as $r )
{
$vis->visitRecord( $r );
}
}
}
class MinSalaryFinder extends RecordVisitor
{
public $min = 1000000;
public function visitRecord( $rec )
{
if( $rec->salary < $this->min ) { $this->min = $rec->salary; }
}
}
$rl = new RecordList();
$msl = new MinSalaryFinder();
$rl->iterate( $msl );
echo( $msl->min."\n" );
?>

Wzorce projektowe

321

SPOSB

78.

SPOSB

78.

Uatwienie wykonywania operacji z danymi dziki zastosowaniu wzorca Wizytator

W tej wersji dodaem klas abstrakcyjn RecordVisitor i zaimplementowaem j za


pomoc klasy MinSalaryFinder, ktra zapisuje minimaln warto pensji. Kod testowy tworzy obiekt RecordList, nastpnie obiekt MinSalaryFinder i przetwarza dane
z listy za pomoc metody iterate(). Na koniec wywietla znalezion warto minimaln.
Na zakoczenie warto wycign kilka wnioskw dotyczcych zaprezentowanego sposobu. Po pierwsze, jzyk PHP nie najlepiej nadaje si do dynamicznego wywoywania
funkcji. Specyfikowanie funkcji za pomoc nazwy jest niezrczne i stwarza duo okazji
do popenienia bdw. W jzykach Python, Perl, Ruby, Java i C# (a take wikszoci innych jzykw) s moliwoci przypisywania wskanika funkcji do zmiennej. W takim
przypadku mona za porednictwem wskanika na funkcj wywoa metod. Lubi PHP
tak jak wielu innych programistw, ale uwaam, e ten problem naleaoby rozwiza
w kolejnej wersji jzyka.

322

Wzorce projektowe

You might also like