Zwiększ elastyczność swojego kodu dzięki wzorcom projektowym!
* Jak rozpocząć przygodę z językiem Ruby?
* Jak wykorzystać drzemiące w nim możliwości?
* Jak zwiększyć elastyczność tworzonego kodu za pomocą wzorców projektowych?
Stworzony w 1995 roku przez Yukihiro Matsumoto język Ruby dzięki swym unikalnym możliwościom zdobywa serca programistów na całym świecie. Cechy, które podbijają to nieufne środowisko, to między innymi prosta składnia z wbudowanymi w nią wyrażeniami regularnymi, automatyczne oczyszczanie pamięci i wiele, wiele innych. Ogromna i chętna do pomocy społeczność czyni to rozwiązanie jeszcze bardziej atrakcyjnym. Ruby pozwala na korzystanie ze wzorców projektowych - zbioru zasad i reguł prowadzących do celu w najlepszy, najszybszy i najbardziej elastyczny sposób.
Wzorce projektowe kojarzą się głównie z językami Java oraz C i C++.
Książka „Ruby. Wzorce projektowe” pokazuje, że można ich z powodzeniem używać również w języku Ruby. Dowiesz się z niej, w jaki sposób wykorzystać znane wzorce, takie jak Observer, Singleton czy też Proxy. Autor przedstawi Ci również nowe wzorce, które ze względu na cechy języka Ruby mogą zostać w nim zastosowane. Jednak zanim przejdziesz do ich omawiania, Russ poprowadzi Cię przez podstawy programowania w tym języku. Nauczysz się używać między innymi pętli, instrukcji warunkowych, wyrażeń regularnych. Niewątpliwie Twoją ciekawość wzbudzi tak zwany „duck typing”, który oczywiście także został dokładnie tu omówiony. Russ Olsen dzięki swojemu wieloletniemu doświadczeniu każdy wzorzec ilustruje przykładem z życia wziętym. Ułatwi Ci to przyswojenie i zastosowanie we własnych projektach przedstawionych tu wzorców.
* Podstawy programowania w języku Ruby
* Zastosowanie wzorców - takich jak Observer, Composite, Iterator, Command i wiele innych
* Wykorzystanie technik metaprogramowania do tworzenia obiektów niestandardowych
* Wykorzystanie wyrażeń regularnych
* Użycie języków dziedzinowych
* Sposób instalacji języka Ruby
Korzystaj z doświadczenia najlepszych programistów - używaj wzorców projektowych w języku Ruby!
1. Ruby. Wzorce projektowe
Autor: Russ Olsen
T³umaczenie: Miko³aj Szczepaniak
ISBN: 978-83-246-1688-6
Tytu³ orygina³u: Design Patterns in Ruby
(Addison-Wesley Professional Ruby Series)
Format: 172x245, stron: 370
Zwiêksz elastycznoœæ swojego kodu dziêki wzorcom projektowym!
• Jak rozpocz¹æ przygodê z jêzykiem Ruby?
• Jak wykorzystaæ drzemi¹ce w nim mo¿liwoœci?
• Jak zwiêkszyæ elastycznoœæ tworzonego kodu za pomoc¹ wzorców projektowych?
Stworzony w 1995 roku przez Yukihiro Matsumoto jêzyk Ruby dziêki swym unikalnym
mo¿liwoœciom zdobywa serca programistów na ca³ym œwiecie. Cechy, które podbijaj¹
to nieufne œrodowisko, to miêdzy innymi prosta sk³adnia z wbudowanymi w ni¹
wyra¿eniami regularnymi, automatyczne oczyszczanie pamiêci i wiele, wiele innych.
Ogromna i chêtna do pomocy spo³ecznoœæ czyni to rozwi¹zanie jeszcze bardziej
atrakcyjnym. Ruby pozwala na korzystanie ze wzorców projektowych – zbioru zasad
i regu³ prowadz¹cych do celu w najlepszy, najszybszy i najbardziej elastyczny sposób.
Wzorce projektowe kojarz¹ siê g³ównie z jêzykami Java oraz C i C++. Ksi¹¿ka „Ruby.
Wzorce projektowe” pokazuje, ¿e mo¿na ich z powodzeniem u¿ywaæ równie¿ w jêzyku
Ruby. Dowiesz siê z niej, w jaki sposób wykorzystaæ znane wzorce, takie jak Observer,
Singleton czy te¿ Proxy. Autor przedstawi Ci równie¿ nowe wzorce, które ze wzglêdu
na cechy jêzyka Ruby mog¹ zostaæ w nim zastosowane. Jednak zanim przejdziesz
do ich omawiania, Russ poprowadzi Ciê przez podstawy programowania w tym jêzyku.
Nauczysz siê u¿ywaæ miêdzy innymi pêtli, instrukcji warunkowych, wyra¿eñ regularnych.
Niew¹tpliwie Twoj¹ ciekawoœæ wzbudzi tak zwany „duck typing”, który oczywiœcie tak¿e
zosta³ dok³adnie tu omówiony. Russ Olsen dziêki swojemu wieloletniemu doœwiadczeniu
ka¿dy wzorzec ilustruje przyk³adem z ¿ycia wziêtym. U³atwi Ci to przyswojenie
i zastosowanie we w³asnych projektach przedstawionych tu wzorców.
• Podstawy programowania w jêzyku Ruby
• Zastosowanie wzorców – takich jak Observer, Composite, Iterator, Command
i wiele innych
Wydawnictwo Helion • Wykorzystanie technik metaprogramowania do tworzenia obiektów
ul. Koœciuszki 1c niestandardowych
44-100 Gliwice • Wykorzystanie wyra¿eñ regularnych
tel. 032 230 98 63 • U¿ycie jêzyków dziedzinowych
e-mail: helion@helion.pl • Sposób instalacji jêzyka Ruby
Korzystaj z doœwiadczenia najlepszych programistów – u¿ywaj
wzorców projektowych w jêzyku Ruby!
2. S PIS TRE¥CI
Sïowo wstÚpne .................................................................................................. 15
Przedmowa ........................................................................................................ 19
PodziÚkowania .................................................................................................. 25
O autorze ............................................................................................................ 27
CZ}¥m I WZORCE PROJEKTOWE I RUBY .................................................29
Rozdziaï 1. Budowa lepszych programów z wykorzystaniem
wzorców projektowych ................................................................................... 31
Banda Czworga ................................................................................................. 32
Wzorce dla wzorców ........................................................................................ 33
Oddzielaj elementy zmienne od elementów staïych ...................... 33
Programuj pod kÈtem interfejsu, nie implementacji ....................... 34
Stosuj kompozycjÚ zamiast dziedziczenia ........................................ 36
Deleguj, deleguj i jeszcze raz deleguj ................................................ 40
Czy dane rozwiÈzanie rzeczywi cie jest niezbÚdne ........................ 41
Czterna cie z dwudziestu trzech .................................................................... 43
Wzorce projektowe w jÚzyku Ruby? ............................................................. 45
Rozdziaï 2. Pierwsze kroki w jÚzyku Ruby ...................................................................... 47
Interaktywny jÚzyk Ruby ................................................................................ 48
Przywitaj siÚ ze wiatem .................................................................................. 48
Zmienne .............................................................................................................. 51
Typy Fixnum i Bignum .................................................................................... 52
Liczby zmiennoprzecinkowe .......................................................................... 53
W jÚzyku Ruby nie ma typów prostych ........................................................ 54
Kiedy brakuje obiektów… ............................................................................... 55
Prawda, faïsz i nil .............................................................................................. 55
Decyzje, decyzje ................................................................................................ 57
PÚtle ..................................................................................................................... 58
3. 8 RUBY. WZORCE PROJEKTOWE
WiÚcej o ïañcuchach .......................................................................................... 60
Symbole ............................................................................................................... 63
Tablice ................................................................................................................. 63
Tablice mieszajÈce ............................................................................................. 65
Wyra enia regularne ........................................................................................ 65
Nasza pierwsza klasa ........................................................................................ 66
Operacje na zmiennych egzemplarzy ........................................................... 68
Obiekt pyta: Kim jestem? ................................................................................. 70
Dziedziczenie, podklasy i nadklasy ............................................................... 71
Opcje argumentów ........................................................................................... 72
Moduïy ................................................................................................................ 73
WyjÈtki ................................................................................................................ 76
WÈtki ................................................................................................................... 77
ZarzÈdzanie odrÚbnymi plikami z kodem ródïowym .............................. 78
Podsumowanie .................................................................................................. 79
CZ}¥m II WZORCE PROJEKTOWE W J}ZYKU RUBY ..................................81
Rozdziaï 3. Urozmaicanie algorytmów
za pomocÈ wzorca projektowego Template Method ................................. 83
Jak stawiÊ czoïo typowym problemom .......................................................... 84
Izolowanie elementów zachowujÈcych dotychczasowÈ formÚ ................ 85
Odkrywanie wzorca projektowego Template Method .............................. 88
Metody zaczepienia .......................................................................................... 89
Gdzie siÚ wïa ciwie podziaïy wszystkie te deklaracje? ............................... 92
Typy, bezpieczeñstwo i elastyczno Ê ............................................................. 93
Testy jednostkowe nie majÈ charakteru opcjonalnego ............................... 95
U ywanie i nadu ywanie wzorca projektowego Template Method ....... 97
Szablony w praktycznych zastosowaniach ................................................... 98
Podsumowanie .................................................................................................. 99
Rozdziaï 4. ZastÚpowanie algorytmu strategiÈ .............................................................. 101
Deleguj, deleguj i jeszcze raz deleguj .......................................................... 102
Wspóïdzielenie danych przez kontekst i strategiÚ .................................... 104
Jeszcze raz o kaczym typowaniu .................................................................. 106
Obiekty Proc i bloki kodu .............................................................................. 107
Krótka analiza kilku prostych strategii ........................................................ 111
U ywanie i nadu ywanie wzorca projektowego Strategy ....................... 112
Wzorzec Strategy w praktycznych zastosowaniach .................................. 113
Podsumowanie ................................................................................................ 114
Rozdziaï 5. Jak byÊ na bie Èco dziÚki wzorcowi Observer ......................................... 117
Trzymamy rÚkÚ na pulsie .............................................................................. 117
Jak skuteczniej trzymaÊ rÚkÚ na pulsie? ...................................................... 119
WyodrÚbnianie mechanizmu umo liwiajÈcego obserwacjÚ .................... 122
Stosowanie bloków kodu w roli obserwatorów ......................................... 125
Odmiany wzorca projektowego Observer .............................................. 126
4. SPIS TRE¥CI 9
U ywanie i nadu ywanie wzorca projektowego Observer ..................... 127
Wzorzec Observer w praktycznych zastosowaniach ................................ 129
Podsumowanie ................................................................................................ 130
Rozdziaï 6. Budowa wiÚkszej caïo ci z czÚ ci za pomocÈ wzorca Composite ......... 133
Caïo Ê i czÚ ci ................................................................................................... 134
Tworzenie kompozytów ................................................................................ 136
Doskonalenie implementacji wzorca Composite
z wykorzystaniem operatorów .................................................................. 140
A mo e tablica w roli kompozytu? ............................................................... 141
Kïopotliwe ró nice .......................................................................................... 141
Wska niki w obie strony ................................................................................ 142
U ywanie i nadu ywanie wzorca projektowego Composite .................. 143
Kompozyty w praktycznych zastosowaniach ............................................ 145
Podsumowanie ................................................................................................ 147
Rozdziaï 7. Przeszukiwanie kolekcji z wykorzystaniem wzorca Iterator ................ 149
Iteratory zewnÚtrzne ...................................................................................... 149
Iteratory wewnÚtrzne ..................................................................................... 152
Iteratory wewnÚtrzne kontra iteratory wewnÚtrzne ................................ 153
Niezrównany moduï Enumerable ................................................................ 154
U ywanie i nadu ywanie wzorca projektowego Iterator ........................ 156
Iteratory w praktycznych zastosowaniach ................................................. 158
Podsumowanie ................................................................................................ 161
Rozdziaï 8. Doprowadzanie spraw do koñca za pomocÈ wzorca Command ........... 163
Eksplozja podklas ............................................................................................ 164
Prostsze rozwiÈzanie ...................................................................................... 165
Stosowanie bloków kodu w roli poleceñ ..................................................... 166
Rejestrowanie poleceñ .................................................................................... 167
Wycofywanie operacji za pomocÈ wzorca Command .............................. 170
Kolejkowanie poleceñ .................................................................................... 173
U ywanie i nadu ywanie wzorca projektowego Command .................. 174
Wzorzec projektowy Command w praktycznych zastosowaniach ........ 175
Migracje w ramach interfejsu ActiveRecord ................................... 175
Madeleine ............................................................................................. 176
Podsumowanie ................................................................................................ 179
Rozdziaï 9. Wypeïnianie luk z wykorzystaniem wzorca Adapter ............................. 181
Adaptery programowe ................................................................................... 182
Minimalne niedociÈgniÚcia ............................................................................ 184
Czy alternatywÈ mo e byÊ adaptowanie istniejÈcych klas? ..................... 186
Modyfikowanie pojedynczych obiektów .................................................... 187
AdaptowaÊ czy modyfikowaÊ? ..................................................................... 188
U ywanie i nadu ywanie wzorca projektowego Adapter ....................... 190
Adaptery w praktycznych zastosowaniach ................................................ 190
Podsumowanie ................................................................................................ 191
5. 10 RUBY. WZORCE PROJEKTOWE
Rozdziaï 10. Tworzenie zewnÚtrznego reprezentanta naszego obiektu
z wykorzystaniem wzorca Proxy ................................................................. 193
RozwiÈzaniem sÈ po rednicy ........................................................................ 194
Po rednik ochrony .......................................................................................... 196
Po rednicy zdalni ............................................................................................ 197
Po rednicy wirtualni jako rodek rozleniwiajÈcy ...................................... 198
Eliminacja najbardziej uciÈ liwych
elementów implementacji po redników .................................................. 200
Metody i przekazywanie komunikatów ......................................... 201
Metoda method_missing ................................................................... 202
Wysyïanie komunikatów ................................................................... 203
Bezbolesne implementowanie po redników ................................. 203
U ywanie i nadu ywanie po redników ...................................................... 206
Wzorzec Proxy w praktycznych zastosowaniach ...................................... 207
Podsumowanie ................................................................................................ 208
Rozdziaï 11. Doskonalenie obiektów za pomocÈ wzorca Decorator ........................... 211
Dekoratory: lekarstwo na brzydki kod ........................................................ 212
Dekoracja formalna ......................................................................................... 217
Jak upro ciÊ model delegacji zadañ ............................................................. 218
Dynamiczna alternatywa dla wzorca projektowego Decorator .............. 219
Opakowywanie metod ....................................................................... 219
Dekorowanie za pomocÈ moduïów ................................................. 220
U ywanie i nadu ywanie wzorca projektowego Decorator .................... 221
Dekoratory w praktycznych zastosowaniach ............................................. 222
Podsumowanie ................................................................................................ 223
Rozdziaï 12. Jak zyskaÊ pewno Ê, e to ten jedyny,
z wykorzystaniem wzorca Singleton .......................................................... 225
Jeden obiekt, dostÚp globalny ....................................................................... 225
Zmienne i metody klasowe ........................................................................... 226
Zmienne klasowe ................................................................................ 226
Metody klasowe .................................................................................. 227
Pierwsza próba opracowania singletonu w Ruby ..................................... 228
ZarzÈdzanie jedynym obiektem ....................................................... 229
Upewnianie siÚ, e istnieje tylko jeden ........................................... 230
Moduï Singleton .............................................................................................. 231
Singletony leniwe i chciwe ............................................................................ 232
Konstrukcje alternatywne wzglÚdem klasycznego singletonu ............... 232
Zmienne globalne jako singletony ................................................... 232
Klasy jako singletony .......................................................................... 233
Moduïy jako singletony ..................................................................... 235
6. SPIS TRE¥CI 11
Pasy bezpieczeñstwa kontra kaftan bezpieczeñstwa ................................ 236
U ywanie i nadu ywanie wzorca projektowego Singleton .................... 237
Czy singletony nie sÈ przypadkiem
zwykïymi zmiennymi globalnymi? ............................................... 237
Wïa ciwie iloma singletonami dysponujemy? ............................... 238
Singletony i niezbÚdna wiedza ......................................................... 238
Eliminowanie utrudnieñ zwiÈzanych z testowaniem .................. 240
Singletony w praktycznych zastosowaniach .............................................. 241
Podsumowanie ................................................................................................ 242
Rozdziaï 13. Wybór wïa ciwej klasy za pomocÈ wzorca Factory ................................. 243
Inny rodzaj kaczego typowania .................................................................... 244
Powrót wzorca projektowego Template Method ...................................... 246
Sparametryzowane metody wytwórcze ...................................................... 248
Klasy to po prostu obiekty ............................................................................. 251
Zïe wie ci: nasz program podbiï wiat ........................................................ 252
Grupowe tworzenie obiektów ...................................................................... 253
Klasy to po prostu obiekty (raz jeszcze) ...................................................... 255
Korzystanie z nazw ......................................................................................... 257
U ywanie i nadu ywanie wzorców fabryk ................................................ 258
Wzorce fabryk w praktycznych zastosowaniach ....................................... 258
Podsumowanie ................................................................................................ 260
Rozdziaï 14. Uproszczone konstruowanie obiektów
z wykorzystaniem wzorca Builder .............................................................. 263
Budowa komputerów ..................................................................................... 264
Klasy budowniczych polimorficznych ........................................................ 267
Klasy budowniczych mogÈ te zapewniÊ
bezpieczeñstwo tworzenia obiektów ........................................................ 270
Klasy budowniczych wielokrotnego u ytku .............................................. 270
Lepsza implementacja budowniczych
z wykorzystaniem magicznych metod ..................................................... 271
U ywanie i nadu ywanie wzorca projektowego Builder ........................ 272
Klasy budowniczych w praktycznych zastosowaniach ............................ 273
Podsumowanie ................................................................................................ 274
Rozdziaï 15. ’Èczenie systemu z wykorzystaniem interpretera ................................... 275
JÚzyk dostosowany do realizowanego zadania .......................................... 276
Konstruowanie interpretera .......................................................................... 277
Interpreter odnajdywania plików ................................................................ 279
Odnajdywanie wszystkich plików ................................................... 279
Wyszukiwanie plików wedïug nazw .............................................. 280
Wielkie pliki i pliki zapisywalne ....................................................... 281
Bardziej zïo one operacje wyszukiwania z uwzglÚdnieniem
logicznej negacji, koniunkcji i alternatywy ................................. 282
7. 12 RUBY. WZORCE PROJEKTOWE
Tworzenie drzewa AST .................................................................................. 284
Prosty analizator skïadniowy ............................................................ 284
Interpreter bez analizatora skïadniowego? ..................................... 286
Analiza skïadniowa danych jÚzyka XML czy YAML? .................. 287
Generowanie zïo onych analizatorów skïadniowych
za pomocÈ narzÚdzia Racc .............................................................. 288
A mo e wykorzystaÊ analizator samego jÚzyka Ruby? ................ 288
U ywanie i nadu ywanie wzorca projektowego Interpreter .................. 289
Interpretery w praktycznych zastosowaniach ........................................... 290
Podsumowanie ................................................................................................ 291
CZ}¥m III WZORCE DLA J}ZYKA RUBY ...................................................293
Rozdziaï 16. Otwieranie systemów za pomocÈ jÚzyków dziedzinowych (DSL) ...... 295
JÚzyki dziedzinowe ......................................................................................... 296
JÚzyk DSL kopii zapasowej ............................................................................ 297
Czy to plik z danymi? Nie, to program! ...................................................... 297
Budowa jÚzyka PackRat ................................................................................. 299
’Èczenie opracowanych dotychczas elementów w jednÈ caïo Ê ............ 300
Krytyczne spojrzenie na jÚzyk PackRat ....................................................... 301
Doskonalenie jÚzyka PackRat ....................................................................... 302
U ywanie i nadu ywanie wewnÚtrznych jÚzyków DSL ......................... 305
WewnÚtrzne jÚzyki DSL w praktycznych zastosowaniach ..................... 305
Podsumowanie ................................................................................................ 307
Rozdziaï 17. Tworzenie niestandardowych obiektów
technikÈ metaprogramowania ..................................................................... 309
Obiekty szyte na miarÚ metoda po metodzie ............................................. 310
Obiekty niestandardowe tworzone moduï po module ............................ 312
Wyczarowywanie zupeïnie nowych metod ............................................... 313
Rzut okiem na wewnÚtrznÈ strukturÚ obiektu ........................................... 317
U ywanie i nadu ywanie metaprogramowania ........................................ 318
Metaprogramowanie w praktycznych zastosowaniach ........................... 319
Podsumowanie ................................................................................................ 322
Rozdziaï 18. Konwencja ponad konfiguracjÈ ................................................................... 323
Interfejs u ytkownika dobry dla... programistów ..................................... 325
Przewidywanie przyszïych potrzeb ................................................. 326
OszczÚd my u ytkownikom powtarzania swojej woli ................ 326
UdostÚpnianie szablonów ................................................................. 327
Bramka wiadomo ci ........................................................................................ 327
Wybór adaptera ............................................................................................... 329
’adowanie klas ................................................................................................ 331
Dodanie prostych zabezpieczeñ ................................................................... 333
Uïatwianie u ytkownikowi wej cia w wiat
naszego oprogramowania ........................................................................... 335
8. SPIS TRE¥CI 13
Podsumowanie przykïadu bramki wiadomo ci ........................................ 336
U ywanie i nadu ywanie wzorca projektowego
Convention Over Configuration ............................................................... 337
Wzorzec Convention Over Configuration
w praktycznych zastosowaniach ............................................................... 338
Podsumowanie ................................................................................................ 339
Rozdziaï 19. Konkluzja ......................................................................................................... 341
DODATKI ................................................................................345
Dodatek A Instalacja jÚzyka Ruby ................................................................................... 347
Instalacja Ruby w systemie Microsoft Windows ........................................ 347
Instalacja Ruby w systemie Linux i pozostaïych systemach
wywodzÈcych siÚ z systemu UNIX ........................................................... 348
System Mac OS X ............................................................................................ 348
Dodatek B PogïÚbiona analiza ......................................................................................... 349
Wzorce projektowe ......................................................................................... 349
Ruby .................................................................................................................. 350
Wyra enia regularne ...................................................................................... 352
Blogi i witryny internetowe ........................................................................... 352
Skorowidz ........................................................................................................ 353
9. R OZDZIA’ 4.
ZastÚpowanie
algorytmu strategiÈ
W poprzednim rozdziale szukali my odpowiedzi na pytanie: Jak zró nicowaÊ wybranÈ
czÚ Ê algorytmu? Jak sprawiÊ, by trzeci krok procesu piÚciokrokowego raz podejmowaï
jedne dziaïania, a innym razem realizowaï nieco inne zadania? W rozdziale 3. przyjÚli-
my rozwiÈzanie polegajÈce na stosowaniu wzorca projektowego Template Method,
czyli na tworzeniu klasy bazowej z metodÈ szablonowÈ odpowiedzialnÈ za ogólne
przetwarzanie podklas charakterystycznych dla mechanizmów szczegóïowych. Gdy-
by my raz chcieli realizowaÊ jeden wariant, by innym razem stosowaÊ mechanizm
alternatywny, powinni my opracowaÊ dwie podklasy, po jednej dla obu scenariuszy.
Wzorzec projektowy Template Method ma, niestety, pewne wady, z których naj-
powa niejszÈ jest konieczno Ê korzystania z relacji dziedziczenia (wïa nie wokóï niej
opracowano ten wzorzec). Jak wiemy z rozdziaïu 1., stosowanie cisïej relacji dzie-
dziczenia niesie ze sobÈ wiele negatywnych konsekwencji. Niezale nie od wysiïku,
jaki wïo ymy w rozwa ne projektowanie naszego kodu, podklasy bÚdÈ ci le zwiÈ-
zane ze swojÈ nadklasÈ, co w przypadku tej relacji jest zupeïnie naturalne. Techniki
opracowane na podstawie relacji dziedziczenia, w tym wzorzec Template Method,
ograniczajÈ wiÚc elastyczno Ê naszego kodu. Po wybraniu okre lonej wersji algo-
rytmu (w naszym przypadku takim krokiem byïo utworzenie obiektu klasy HTML-
Report), zmiana tego trybu na inny mo e siÚ okazaÊ niezwykle trudna. Gdyby my
zastosowali wzorzec projektowy Template Method i zdecydowali siÚ na zmianÚ
formatu raportu, musieliby my utworzyÊ nowy obiekt raportu, np. obiekt klasy
PlainTextReport, tylko po to, by u yÊ nowego formatu. Mamy inne wyj cie?
10. 102 CzÚ Ê II • WZORCE PROJEKTOWE W J}ZYKU RUBY
DELEGUJ, DELEGUJ I JESZCZE RAZ DELEGUJ
Alternatywnym rozwiÈzaniem jest uwzglÚdnienie rady sformuïowanej przez BandÚ
Czworga i przypomnianÈ w rozdziale 1. tej ksiÈ ki: wybierajmy technikÚ delegowania
zadañ. Jaki bÚdzie efekt rezygnacji z ka dorazowego tworzenia podklas na rzecz
wyodrÚbnienia kïopotliwego, zmienianego kodu do wyspecjalizowanej klasy? BÚdziemy
wówczas mogli opracowaÊ caïÈ rodzinÚ klas, po jednej dla ka dej odmiany imple-
mentowanego mechanizmu. Poni ej przedstawiono przykïad kodu odpowiedzialnego
za formatowanie raportów w jÚzyku HTML przeniesionego na poziom odrÚbnej klasy:
class Formatter
def output_report( title, text )
raise 'Wywoïano metodÚ abstrakcyjnÈ'
end
end
class HTMLFormatter < Formatter
def output_report( title, text )
puts('<html>')
puts(' <head>')
puts(" <title>#{title}</title>")
puts(' </head>')
puts(' <body>')
text.each do |line|
puts(" <p>#{line}</p>" )
end
puts(' </body>')
puts('</html>')
end
end
KlasÚ odpowiedzialnÈ za formatowanie raportu w wersji tekstowej mo na by zaim-
plementowaÊ w nastÚpujÈcy sposób:
class PlainTextFormatter < Formatter
def output_report(title, text)
puts("***** #{title} *****")
text.each do |line|
puts(line)
end
end
end
Udaïo nam siÚ przenie Ê szczegóïowy kod zwiÈzany z formatowaniem danych wyni-
kowych poza klasÚ Report, zatem jej definicja bÚdzie teraz nieporównanie prostsza:
class Report
attr_reader :title, :text
attr_accessor :formatter
11. Rozdziaï 4. • ZAST}POWANIE ALGORYTMU STRATEGIk 103
def initialize(formatter)
@title = 'Raport miesiÚczny'
@text = [ 'Wszystko idzie', 'naprawdÚ dobrze.' ]
@formatter = formatter
end
def output_report
@formatter.output_report( @title, @text )
end
end
Okazuje siÚ, e wprowadzona zmiana tylko nieznacznie komplikuje proces korzysta-
nia z klasy Report w nowym ksztaïcie. Musimy teraz przekazywaÊ na wej ciu jej kon-
struktora wïa ciwy obiekt formatujÈcy:
report = Report.new(HTMLFormatter.new)
report.output_report
Banda Czworga okre liïa technikÚ „wyodrÚbniania algorytmu do osobnego obiektu”
mianem wzorca projektowego Strategy (patrz rysunek 4.1). Wzorzec Strategy opra-
cowano w my l zaïo enia, zgodnie z którym warto definiowaÊ rodzinÚ obiektów, tzw.
strategii, realizujÈcych to samo zadanie w ró ny sposób (w naszym przypadku tym
zadaniem jest formatowanie raportu). Wszystkie obiekty strategii nie tylko realizujÈ to
samo zadanie, ale te obsïugujÈ identyczny interfejs. W prezentowanym przykïadzie
ten wspólny interfejs ogranicza siÚ do metody output_report. Skoro wszystkie obiekty
strategii z zewnÈtrz wyglÈdajÈ tak samo, u ytkownik strategii (nazywany przez BandÚ
Czworga klasÈ kontekstu — ang. context class) mo e je traktowaÊ jak wymienne
elementy. Oznacza to, e wybór strategii o tyle nie ma wielkiego znaczenia, e wszyst-
kie te obiekty wyglÈdajÈ podobnie i realizujÈ tÚ samÈ funkcjÚ.
RYSUNEK 4.1.
Wzorzec projektowy
Strategy
Z drugiej strony wybór strategii ma znaczenie ze wzglÚdu na ró nice w sposobie reali-
zacji interesujÈcych nas zadañ. W naszym przykïadzie jedna ze strategii formato-
wania generuje raport w jÚzyku HTML, druga generuje raport w formie zwykïego
tekstu. Gdyby my zastosowali wzorzec Strategy w systemie obliczajÈcym wysoko Ê
podatku dochodowego, mogliby my u yÊ jednej strategii do wyznaczania podatku
pïaconego przez etatowego pracownika i innej do obliczania podatku uiszczanego
przez pracownika zatrudnionego na podstawie umowy o dzieïo.
12. 104 CzÚ Ê II • WZORCE PROJEKTOWE W J}ZYKU RUBY
Wzorzec projektowy Strategy ma wiele praktycznych zalet. Przykïad raportów po-
kazuje, e stosujÈc ten wzorzec, mo emy zapewniÊ skuteczniejszÈ izolacjÚ naszego
kodu dziÚki wyodrÚbnieniu zbioru strategii poza klasÚ gïównÈ. Wzorzec Strategy
zwalnia klasÚ Report z jakiejkolwiek odpowiedzialno ci czy choÊby konieczno ci
skïadowania informacji o formacie generowanych raportów.
Co wiÚcej, poniewa wzorzec Strategy opiera siÚ na relacji kompozycji i technice de-
legacji (zamiast na dziedziczeniu), zmiana strategii w czasie wykonywania programu
nie stanowi adnego problemu. Wystarczy wymieniÊ obiekt strategii:
report = Report.new(HTMLFormatter.new)
report.output_report
report.formatter = PlainTextFormatter.new
report.output_report
Wzorzec projektowy Strategy ma jednÈ cechÚ wspólnÈ ze wzorcem Template Method:
oba wzorce umo liwiajÈ nam koncentrowanie decyzji o wyborze sposobu realizacji
zlecanych zadañ w zaledwie jednym lub dwóch miejscach. W przypadku wzorca Tem-
plate Method wïa ciwÈ odmianÚ algorytmu wybieramy, wskazujÈc konkretnÈ podklasÚ;
we wzorcu Strategy taki wybór sprowadza siÚ do wskazania klasy strategii w czasie
wykonywania programu.
WSPÓ’DZIELENIE DANYCH
PRZEZ KONTEKST I STRATEGI}
JednÈ z najwiÚkszych zalet wzorca projektowego Strategy jest izolowanie kodu kon-
tekstu i strategii w odrÚbnych klasach, co skutecznie dzieli dane pomiÚdzy dwa nie-
zale ne byty. Problem w tym, e musimy znale Ê jaki sposób pokonania muru dzielÈ-
cego oba byty, aby przekazywaÊ informacje, którymi dysponuje kontekst i które sÈ
niezbÚdne do prawidïowego funkcjonowania strategii. Ogólnie mówiÈc, mamy do
wyboru dwa rozwiÈzania.
Po pierwsze, mo emy konsekwentnie stosowaÊ dotychczasowe rozwiÈzanie polegajÈce
na przekazywaniu wszystkich danych niezbÚdnych do pracy obiektów strategii w for-
mie argumentów (przekazywanych przez obiekt kontekstu podczas wywoïywania me-
tod obiektów strategii). Warto pamiÚtaÊ, e w naszym przykïadzie systemu raportujÈ-
cego obiekt raportu byï przekazywany do obiektów formatujÈcych za po rednictwem
argumentów wywoïañ metody output_report. Przekazywanie kompletnych danych
ma tÚ zaletÚ, e pozwala skutecznie izolowaÊ kontekst od obiektów strategii. Strategie
majÈ wspólny interfejs; kontekst korzysta tylko z tego interfejsu. Wady opisanej meto-
dy sÈ widoczne w sytuacji, gdy musimy przekazywaÊ mnóstwo zïo onych danych,
co do których nie mamy adnych gwarancji, e rzeczywi cie zostanÈ wykorzystane.
13. Rozdziaï 4. • ZAST}POWANIE ALGORYTMU STRATEGIk 105
Po drugie, mo emy przekazywaÊ dane z kontekstu do strategii w formie referencji do
samego obiektu kontekstu. Obiekt strategii mo e wówczas uzyskiwaÊ niezbÚdne dane
za po rednictwem metod obiektu kontekstu. W naszym przykïadzie systemu rapor-
tujÈcego odpowiedni model mógïby mieÊ nastÚpujÈcÈ postaÊ:
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(formatter)
@title = 'Raport miesiÚczny'
@text = [ 'Wszystko idzie', 'naprawdÚ dobrze.' ]
@formatter = formatter
end
def output_report
@formatter.output_report(self)
end
end
Obiekt klasy Report przekazuje strategii formatowania referencjÚ do samego siebie;
klasa formatujÈca mo e nastÚpnie uzyskiwaÊ niezbÚdne dane za po rednictwem no-
wych metod title i text. Poni ej przedstawiono przebudowanÈ klasÚ HTMLFormatter
(przystosowanÈ do korzystania z referencji do obiektu klasy Report):
class Formatter
def output_report(context)
raise 'Wywoïano metodÚ abstrakcyjnÈ'
end
end
class HTMLFormatter < Formatter
def output_report(context)
puts('<html>')
puts(' <head>')
puts(" <title>#{context.title}</title>")
puts(' </head>')
puts(' <body>')
context.text.each do |line|
puts(" <p>#{line}</p>")
end
puts(' </body>')
puts('</html>')
end
end
Chocia opisana technika przekazywania kontekstu do strategii skutecznie uprasz-
cza przepïyw danych, warto mieÊ na uwadze to, e stosujÈc wskazane rozwiÈzanie,
zacie niamy zwiÈzki ïÈczÈce kontekst i strategiÚ. W ten sposób istotnie zwiÚkszamy
ryzyko wzajemnego uzale nienia klasy kontekstu i klas strategii.
14. 106 CzÚ Ê II • WZORCE PROJEKTOWE W J}ZYKU RUBY
JESZCZE RAZ O KACZYM TYPOWANIU
PrzykïadowÈ aplikacjÚ generujÈcÈ raporty, którÈ posïugiwali my siÚ w tym rozdziale,
zbudowano zgodnie z zaleceniami Bandy Czworga odno nie do wzorca projektowego
Strategy. Nasza rodzina strategii formatowania skïada siÚ z „abstrakcyjnej” klasy ba-
zowej Formatter oraz dwóch podklas: HTMLFormatter i PlainTextFormatter. Pre-
zentowany model nie najlepiej pasuje do jÚzyka Ruby, poniewa klasa Formatter
w praktyce nie realizuje adnych dziaïañ — istnieje tylko po to, by definiowaÊ wspólny
interfejs wszystkich klas formatujÈcych. Wspomniany problem w aden sposób nie
wpïywa na formalnÈ poprawno Ê naszego kodu — tak napisany program po prostu
dziaïa. Z drugiej strony takie rozwiÈzanie jest sprzeczne z przyjÚtÈ w jÚzyku Ruby
filozofiÈ tzw. kaczego typowania. Skoro klasy HTMLFormatter i PlainTextFormatter
implementujÈ wspólny interfejs przez zgodne definiowanie metody output_report,
wprowadzanie sztucznego tworu (wïa nie w formie klasy pozbawionej dziaïañ) po-
twierdzajÈcego ten fakt nie ma wiÚkszego sensu.
Mo emy wyeliminowaÊ z naszego projektu klasÚ bazowÈ Formatter za pomocÈ
zaledwie kilku uderzeñ w klawisz Delete. Ostatecznie nasz kod bÚdzie miaï nastÚ-
pujÈcÈ postaÊ:
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(formatter)
@title = 'Raport miesiÚczny'
@text = [ 'Wszystko idzie', 'naprawdÚ dobrze.' ]
@formatter = formatter
end
def output_report()
@formatter.output_report(self)
end
end
class HTMLFormatter
def output_report(context)
puts('<html>')
puts(' <head>')
# Wy wietla pozostaáe dane tego obiektu…
puts(" <title>#{context.title}</title>")
puts(' </head>')
puts(' <body>')
context.text.each do |line|
puts(" <p>#{line}</p>")
end
puts(' </body>')
puts('</html>')
end
end
15. Rozdziaï 4. • ZAST}POWANIE ALGORYTMU STRATEGIk 107
class PlainTextFormatter
def output_report(context)
puts("***** #{context.title} *****")
context.text.each do |line|
puts(line)
end
end
end
Je li porównamy ten kod z poprzedniÈ wersjÈ, przekonamy siÚ, e rezygnacja z klasy
bazowej Formatter nie wymagaïa zbyt wielu dodatkowych zmian. DziÚki dynamicznej
kontroli typów nadal mo emy generowaÊ prawidïowe raporty. Chocia obie wersje
dziaïajÈ zgodnie z zaïo eniami, do wiata Ruby du o lepiej pasuje model pozbawiony
klasy bazowej Formatter.
OBIEKTY PROC I BLOKI KODU
Okazuje siÚ, e eliminacja klasy bazowej nie jest jedynym sposobem dostosowania
wzorca projektowego Strategy do charakteru jÚzyka programowania Ruby. Zanim
jednak zrobimy kolejny krok, musimy wyja niÊ jeden z najciekawszych aspektów jÚ-
zyka Ruby: bloki kodu i obiekty Proc.
Jako u ytkownicy obiektowych jÚzyków programowania spÚdzamy mnóstwo czasu
na my leniu o obiektach i sposobach ich skutecznego ïÈczenia. Warto jednak zwróciÊ
uwagÚ na pewnÈ asymetriÚ wystÚpujÈcÈ w typowym postrzeganiu obiektów. Z reguïy
nie mamy problemu z wyodrÚbnianiem danych poza obiekt — mo emy na przykïad
uzyskaÊ warto Ê zmiennej @text obiektu raportu i przekazywaÊ jÈ dalej niezale nie
od pozostaïych skïadowych tego obiektu. Z drugiej strony zazwyczaj przyjmujemy,
e nasz kod jest ci le i nierozerwalnie zwiÈzany z poszczególnymi obiektami. Takie
przekonanie oczywi cie nie znajduje potwierdzenia w praktyce. Co w takim razie
powinni my zrobiÊ, aby wyodrÚbniÊ fragmenty kodu poza nasz obiekt i przekazywaÊ
je dalej tak jak zwykïe obiekty? Okazuje siÚ, e jÚzyk Ruby oferuje z my lÈ o podob-
nych operacjach gotowe mechanizmy.
W jÚzyku Ruby Proc jest obiektem reprezentujÈcym fragment kodu. Najpopularniej-
szym sposobem konstruowania obiektu Proc jest stosowanie metody lambda:
hello = lambda do
puts('Witaj')
puts('Jestem wewnÈtrz obiektu Proc.')
end
W jÚzyku Ruby fragment kodu zawarty pomiÚdzy sïowem kluczowym do a sïowem
1
end okre la siÚ mianem bloku kodu . Metoda lambda zwraca nowy obiekt Proc, czyli
1
Stosuje siÚ te terminy domkniÚcia (ang. closure) oraz lambdy (stÈd nazwa naszej metody tworzÈcej
obiekt Proc).
16. 108 CzÚ Ê II • WZORCE PROJEKTOWE W J}ZYKU RUBY
kontener obejmujÈcy caïy kod zdefiniowany pomiÚdzy sïowami do i end. W przed-
stawionym przykïadzie obiekt Proc jest wskazywany przez zmiennÈ hello. Kod
reprezentowany przez obiekt Proc mo emy wykonaÊ, wywoïujÈc metodÚ call (trudno
sobie wyobraziÊ lepszÈ nazwÚ). Je li w przedstawionym przykïadzie wywoïamy metodÚ
call obiektu Proc:
hello.call
otrzymamy komunikaty:
Witaj
Jestem wewnÈtrz obiektu Proc.
WyjÈtkowo cennym aspektem obiektów Proc jest zdolno Ê do funkcjonowania w ota-
czajÈcym je rodowisku. Oznacza to, e wszelkie zmienne widoczne w momencie
tworzenia obiektu Proc pozostajÈ dla tego obiektu dostÚpne tak e w czasie wykony-
wania programu. Na przykïad w poni szym fragmencie kodu istnieje tylko jedna zmien-
na name:
name = 'Jan'
proc = Proc.new do
name = 'Maria'
end
proc.call
puts(name)
Kiedy wykonamy ten kod, zmiennej name w pierwszej kolejno ci zostanie przypisany
ïañcuch "Jan", po czym (po wywoïaniu obiektu Proc) warto Ê tej zmiennej zostanie
zastÈpiona przez ïañcuch "Maria".
Oznacza to, e ostatecznie Ruby wy wietli ïañcuch "Maria". Z my lÈ o programistach,
którym konstrukcja do-end wydaje siÚ zbyt rozbudowana, Ruby oferuje krótszÈ skïad-
niÚ zïo onÈ tylko z nawiasów klamrowych. Poni ej przedstawiono przykïad u ycia
tej skïadni do utworzenia naszego obiektu Proc:
hello = lambda {
puts('Witaj, jestem wewnÈtrz obiektu Proc')
}
Wielu programistów jÚzyka Ruby przyjÚïo konwencjÚ, zgodnie z którÈ konstrukcjÚ
do-end stosuje siÚ do bloków wielowierszowych, a nawiasy klamrowe stosuje siÚ do
bloków jednowierszowych2. W tej sytuacji najlepsza wersja prezentowanego przy-
kïadu bÚdzie miaïa nastÚpujÈcÈ postaÊ:
hello = lambda {puts('Witaj, jestem wewnÈtrz obiektu Proc')}
2
Uczciwo Ê nakazuje wspomnieÊ o istotnej ró nicy dzielÈcej obie konstrukcje. W wyra eniach jÚzyka
Ruby nawiasy klamrowe majÈ wy szy priorytet od pary sïów kluczowych do i end. Krótko mówiÈc,
nawiasy klamrowe sÈ ci lej zwiÈzane z wyra eniami. Specyfika nawiasów klamrowych jest wi-
doczna dopiero wtedy, gdy zrezygnujemy z opcjonalnych nawiasów okrÈgïych.
17. Rozdziaï 4. • ZAST}POWANIE ALGORYTMU STRATEGIk 109
Obiekty Proc pod wieloma wzglÚdami przypominajÈ metody. Podobnie jak metody,
obiekty Proc nie tylko reprezentujÈ fragmenty kodu, ale te mogÈ zwracaÊ warto ci.
Obiekt Proc zawsze zwraca ostatniÈ warto Ê wyznaczonÈ w danym bloku — aby
zwróciÊ warto Ê z poziomu takiego obiektu, wystarczy siÚ upewniÊ, e jest ona wy-
znaczana przez jego ostatnie wyra enie. Wszelkie warto ci zwracane przez obiekty
Proc sÈ przekazywane do kodu wywoïujÈcego za po rednictwem metody call.
Oznacza to, e kod w postaci:
return_24 = lambda {24}
puts(return_24.call)
wy wietli warto Ê:
24
Dla obiektów Proc mo na te definiowaÊ parametry, choÊ skïadnia tego rodzaju defini-
cji jest do Ê nietypowa. Zamiast otaczaÊ listÚ parametrów tradycyjnymi nawiasami
okrÈgïymi, nale y je umieszczaÊ pomiÚdzy dwoma symbolami |:
multiply = lambda {|x, y| x * y}
Kod w tej formie definiuje obiekt Proc otrzymujÈcy dwa parametry, wyznaczajÈcy ich
iloczyn i zwracajÈcy otrzymanÈ warto Ê. Aby wywoïaÊ obiekt Proc z parametrami,
wystarczy je przekazaÊ na wej ciu metody call:
n = multiply.call(20, 3)
puts(n)
n = multiply.call(10, 50)
puts(n)
Po wykonaniu tego kodu otrzymamy nastÚpujÈce warto ci:
60
500
Mo liwo Ê przekazywania bloków kodu jest na tyle przydatna, e twórcy Ruby zde-
cydowali siÚ zdefiniowaÊ z my lÈ o tej technice specjalnÈ, skróconÈ konstrukcjÚ skïa-
dniowÈ. Je li chcemy przekazaÊ blok kodu na wej ciu metody, wystarczy go doïÈczyÊ
do jej wywoïania. Tak wywoïana metoda mo e nastÚpnie wykonaÊ ten blok kodu
za pomocÈ sïowa kluczowego yield. Poni ej zdefiniowano przykïadowÈ metodÚ
wy wietlajÈcÈ komunikat, wykonujÈcÈ otrzymany blok kodu i wy wietlajÈcÈ kolejny
komunikat:
def run_it
puts("Przed wykonaniem yield")
yield
puts("Po wykonaniu yield")
end
A tak mo e wyglÈdaÊ wywoïanie metody run_it. Warto pamiÚtaÊ, e w ten sposób
dopisujemy blok kodu na koniec wywoïania naszej metody:
18. 110 CzÚ Ê II • WZORCE PROJEKTOWE W J}ZYKU RUBY
run_it do
puts('Witaj')
puts('Przybywam do Ciebie z wnÚtrza bloku kodu')
end
Kiedy doklejamy blok kodu do wywoïania metody (jak w powy szym przykïadzie),
wspomniany blok (który w rzeczywisto ci ma postaÊ obiektu Proc) jest przekazy-
wany do tej metody w formie dodatkowego, niewidocznego parametru. Sïowo kluczo-
we yield powoduje wiÚc wykonanie tego parametru. W wyniku wykonania powy -
szego kodu na ekranie zostanÈ wy wietlone nastÚpujÈce komunikaty:
Przed wykonaniem yield
Witaj
Przybywam do Ciebie z wnÚtrza bloku kodu
Po wykonaniu yield
Je li przekazany blok kodu sam otrzymuje jakie parametry, nale y je przekazaÊ za
po rednictwem sïowa kluczowego yield. Oznacza to, e poni szy kod:
def run_it_with_parameter
puts("Przed wykonaniem yield")
yield(24)
puts("Po wykonaniu yield")
end
run_it_with_parameter do |x|
puts('Witaj z wnÚtrza bloku kodu')
puts("Parametr x ma warto Ê #{x}")
end
wy wietli nastÚpujÈce komunikaty:
Przed wykonaniem yield
Witaj z wnÚtrza bloku kodu
Parametr x ma warto Ê 24
Po wykonaniu yield
W niektórych przypadkach warto zdefiniowaÊ wprost parametr reprezentujÈcy blok
kodu — w takim przypadku blok przekazany na wej ciu naszej metody ma postaÊ ar-
gumentu przypominajÈcego typowe parametry. W tym celu nale y umie ciÊ specjalny
parametr na koñcu listy parametrów tej metody. Tak przekazany parametr (poprze-
dzony znakiem &) zostanie przypisany obiektowi Proc utworzonemu na podstawie
bloku kodu doïÈczonego do wywoïania danej metody. Oznacza to, e odpowiednikiem
przedstawionej powy ej metody run_it_with_parameters bÚdzie nastÚpujÈca
konstrukcja:
def run_it_with_parameter(&block)
puts("Przed wykonaniem yield")
block.call(24)
puts("Po wykonaniu yield")
end
19. Rozdziaï 4. • ZAST}POWANIE ALGORYTMU STRATEGIk 111
Symbol & mo na z powodzeniem stosowaÊ tak e w przeciwnym kierunku. Gdyby my
umie cili obiekt Proc w zmiennej i chcieli go przekazaÊ na wej ciu metody oczeku-
jÈcej bloku kodu, mogliby my przekonwertowaÊ ten obiekt do postaci wymaganego
bloku, poprzedzajÈc odpowiedni parametr wïa nie znakiem &:
my_proc = lambda {|x| puts("Parametr x ma warto Ê #{x}")}
run_it_with_parameter(&my_proc)
KRÓTKA ANALIZA
KILKU PROSTYCH STRATEGII
Co bloki kodu i obiekty Proc majÈ wspólnego ze wzorcem projektowym Strategy?
Najkrócej mówiÈc, strategiÚ mo na postrzegaÊ jako blok wykonywalnego kodu,
który „wie”, jak zrealizowaÊ okre lone zadanie (np. formatowanie tekstu) i który
opakowano w formie obiektu. Przytoczona definicja brzmi znajomo — obiekt Proc
bywa okre lany wïa nie jako fragment kodu opakowany w formie obiektu.
WróÊmy teraz do naszego przykïadowego systemu formatujÈcego raporty, gdzie za-
stosowanie strategii w formie obiektu Proc okazuje siÚ wyjÈtkowo proste. Zmiany,
które musimy wprowadziÊ w kodzie klasy Report, ograniczajÈ siÚ do poprzedzenia
parametru przekazywanego na wej ciu metody initialize symbolem & i zmiany
nazwy wywoïywanej metody z output_report na call:
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(&formatter)
@title = 'Raport miesiÚczny'
@text = [ 'Wszystko idzie', 'naprawdÚ dobrze.' ]
@formatter = formatter
end
def output_report
@formatter.call( self )
end
end
Z nieco innÈ sytuacjÈ mamy jednak do czynienia w przypadku klas odpowiedzial-
nych za wïa ciwe formatowanie. Musimy teraz stworzyÊ obiekty Proc zamiast eg-
zemplarzy naszych wyspecjalizowanych klas formatujÈcych:
HTML_FORMATTER = lambda do |context|
puts('<html>')
puts(' <head>')
puts(" <title>#{context.title}</title>")
puts(' </head>')
puts(' <body>')
context.text.each do |line|
20. 112 CzÚ Ê II • WZORCE PROJEKTOWE W J}ZYKU RUBY
puts(" <p>#{line}</p>" )
end
puts(' </body>')
puts
Skoro dysponujemy ju kodem formatujÈcym w formie obiektu Proc, mo emy przy-
stÈpiÊ do tworzenia raportów. Poniewa dysponujemy obiektem Proc, a konstruktor
klasy Report oczekuje bloku kodu, tworzÈc nowy obiekt tej klasy musimy poprze-
dziÊ wspomniany obiekt Proc znakiem &:
report = Report.new &HTML_FORMATTER
report.output_report
Po co w ogóle zajmujemy siÚ problemem implementacji strategii w formie obiektu Proc?
Jednym z powodów jest brak konieczno ci definiowania specjalnych klas dla poszcze-
gólnych strategii — wystarczy opakowaÊ kod w ramach obiektu Proc. Co wiÚcej, sto-
sujÈc tÚ technikÚ mo emy tworzyÊ strategie niemal z niczego, przekazujÈc bloki kodu
na wej ciu istniejÈcej metody. Mo emy zaimplementowaÊ na przykïad mechanizm
formatujÈcy raporty tekstowe w formie nastÚpujÈcego bloku kodu:
report = Report.new do |context|
puts("***** #{context.title} *****")
context.text.each do |line|
puts(line)
end
end
Programistom nieprzyzwyczajonym do tego rodzaju konstrukcji bloki kodu mogÈ
siÚ wydawaÊ dziwaczne. Z drugiej strony powinni my pamiÚtaÊ, e proponowana
technika implementacji wzorca Strategy umo liwia zastÈpienie klasÚ kontekstu, klasÚ
bazowÈ strategii i wiele konkretnych strategii (wraz ze wszystkimi niezbÚdnymi
obiektami) pojedynczÈ klasÈ kontekstu i kilkoma blokami kodu.
Czy to oznacza, e powinni my raz na zawsze zapomnieÊ o strategiach implementowa-
nych w postaci odrÚbnych klas? Nie do koñca. Strategie w formie bloków kodu zdajÈ
egzamin tylko wtedy, gdy ich interfejs jest stosunkowo prosty i obejmuje zaledwie jednÈ
metodÚ. Trudno siÚ temu dziwiÊ, skoro call jest jedynÈ metodÈ, którÈ mo emy wy-
woïywaÊ dla obiektów Proc. Je li implementowane strategie wymagajÈ interfejsu
zïo onego z wiÚkszej liczby metod, nie mamy wyboru — musimy skorzystaÊ z tra-
dycyjnych klas. Je li jednak interfejs strategii jest prosty, koniecznie powinni my
rozwa yÊ u ycie bloków kodu.
U¿YWANIE I NADU¿YWANIE
WZORCA PROJEKTOWEGO STRATEGY
NajczÚstszÈ przyczynÈ bïÚdnego implementowania wzorca projektowego Strategy
jest niewïa ciwe definiowanie interfejsu pomiÚdzy kontekstem a strategiami. Musimy
pamiÚtaÊ, e naszym celem jest wyodrÚbnienie kompletnego, spójnego i mniej lub
21. Rozdziaï 4. • ZAST}POWANIE ALGORYTMU STRATEGIk 113
bardziej autonomicznego zadania poza obiekt kontekstu i delegowanie go do obiektu
strategii. Powinni my zwracaÊ szczególnÈ uwagÚ zarówno na szczegóïy interfejsu
ïÈczÈcego kontekst ze strategiÈ, jak i na zwiÈzki wystÚpujÈce pomiÚdzy obiema stro-
nami tej relacji. Stosowanie wzorca Strategy bÚdzie bezcelowe, je li zwiÈ emy kontekst
z pierwszÈ strategiÈ na tyle mocno, e uzupeïnienie projektu o drugÈ, trzeciÈ i kolejne
strategie oka e siÚ niemo liwe.
WZORZEC STRATEGY
W PRAKTYCZNYCH ZASTOSOWANIACH
NarzÚdzie rdoc (doïÈczane do dystrybucji jÚzyka Ruby) zawiera wiele mechanizmów
opracowanych na podstawie klasycznego, zaproponowanego przez BandÚ Czworga
i opartego na klasach wzorca projektowego Strategy. Zadaniem tego narzÚdzia jest
generowanie dokumentacji na podstawie kodu ródïowego. rdoc oferuje mo liwo Ê
dokumentowania zarówno programów napisanych w Ruby, jak i programów opra-
cowanych w C i (ratunku!) programów stworzonych w jÚzyku FORTRAN. NarzÚdzie
rdoc wykorzystuje wzorzec Strategy do obsïugi poszczególnych jÚzyków programo-
wania — ka dy analizator skïadniowy (wïa ciwy dla jÚzyków C, Ruby i FORTRAN)
ma postaÊ strategii stworzonej z my lÈ o innych danych wej ciowych.
NarzÚdzie rdoc oferuje te u ytkownikowi wybór formatu wyj ciowego — mo emy
wybraÊ jednÈ z wielu odmian jÚzyka HTML, jÚzyk XML bÈd jeden z formatów wyko-
rzystywanych przez polecenie ri jÚzyka Ruby. Jak nietrudno odgadnÈÊ, ka dy z tych
formatów wyj ciowych tak e jest obsïugiwany przez wyspecjalizowanÈ strategiÚ.
Relacje ïÈczÈce poszczególne klasy strategii programu rdoc dobrze ilustrujÈ ogólne
podej cie do problemu dziedziczenia stosowane w jÚzyku Ruby. ZwiÈzki klas repre-
zentujÈcych poszczególne strategie przedstawiono na rysunku 4.2.
RYSUNEK 4.2.
Klasy generujÈce
narzÚdzia rdoc
Jak widaÊ na rysunku 4.2, istniejÈ cztery powiÈzane ze sobÈ strategie formatowania
danych wyj ciowych (okre lanych w programie rdoc mianem generatorów) i jedna
strategia autonomiczna. Wszystkie cztery powiÈzane strategie generujÈ dokumentacjÚ
w podobnym formacie — znane wszystkim konstrukcje <co tam> otoczone nawiasami
22. 114 CzÚ Ê II • WZORCE PROJEKTOWE W J}ZYKU RUBY
ostrymi </co tam>3. Ostatnia strategia generuje dane wyj ciowe na potrzeby polecenia
ri jÚzyka Ruby, które nie przypominajÈ ani kodu XML-a, ani kodu HTML-a. Jak
wynika z diagramu UML przedstawionego na rysunku 4.2, relacje ïÈczÈce poszczególne
klasy odzwierciedlajÈ szczegóïy implementacyjne: klasy generujÈce dokumentacjÚ
w formatach HTML, CHM i XML z natury rzeczy wspóïdzielÈ znacznÈ czÚ Ê kodu,
stÈd decyzja twórców rdoc o zastosowaniu relacji dziedziczenia. Klasa RIGenerator
generuje zupeïnie inne dane wynikowe (w formacie caïkowicie niezwiÈzanym z rodzinÈ
jÚzyków XML i HTML). Twórcy rdoc nie zdecydowali siÚ na wspóïdzielenie jednej
nadklasy przez wszystkie klasy generatorów tylko dlatego, e wszystkie te klasy im-
plementujÈ ten sam interfejs. Ograniczyli siÚ jedynie do zaimplementowania wïa-
ciwych metod w opisanych klasach.
Okazuje siÚ, e z dobrym przykïadem wykorzystania obiektu Proc w roli lekkiej strategii
mamy do czynienia na co dzieñ — takie rozwiÈzanie zastosowano w przypadku tablic.
Je li chcemy posortowaÊ zawarto Ê tablicy jÚzyka Ruby, wywoïujemy metodÚ sort:
a = ['ryszard', 'michaï', 'jan', 'daniel', 'robert']
a.sort
Metoda sort domy lnie sortuje obiekty skïadowane w tabeli, stosujÈc „naturalny”
porzÈdek. Co w takim razie powinni my zrobiÊ, je li chcemy u yÊ innego schematu
porzÈdkowania elementów? Jak nale aïoby na przykïad posortowaÊ ïañcuchy wedïug
dïugo ci? Wystarczy przekazaÊ strategiÚ porównywania obiektów w formie bloku
kodu:
a.sort { |a,b| a.length <=> b.length }
Metoda sort wywoïuje nasz blok kodu za ka dym razem, gdy bÚdzie zmuszona po-
równaÊ dwa elementy sortowanej tablicy. Nasz blok powinien zwracaÊ warto Ê 1, je li
pierwszy element jest wiÚkszy od drugiego; warto Ê 0, je li oba elementy sÈ równe,
i warto Ê –1, je li wiÚkszy jest drugi element. Nieprzypadkowo dziaïanie tego kodu
przypomina zachowanie operatora <=>.
PODSUMOWANIE
Wzorzec projektowy Strategy reprezentuje nieco innÈ, opartÈ na delegowaniu zadañ
propozycjÚ rozwiÈzywania tego samego problemu, który jest równie rozwiÈzywany
przez wzorzec Template Method. Zamiast upychania zmiennych czÚ ci algorytmu
w podklasach, implementujemy ka dÈ z wersji tego algorytmu w odrÚbnym obiekcie.
Mo emy nastÚpnie urozmaicaÊ dziaïania tego algorytmu przez wskazywanie obiektowi
kontekstu ró nych obiektów strategii. Oznacza to, e o ile jedna strategia na przykïad
generuje raport w formacie HTML, o tyle inna mo e generowaÊ ten sam raport
3
Format CHM jest odmianÈ HTML-a wykorzystywanÈ do generowania plików pomocy firmy Microsoft.
Chciaïbym te podkre liÊ, e to kod narzÚdzia rdoc — nie ja — sugeruje, e XML jest odmianÈ jÚ-
zyka HTML.
23. Rozdziaï 4. • ZAST}POWANIE ALGORYTMU STRATEGIk 115
w formacie PDF; podobnie, jedna strategia mo e wyznaczaÊ wysoko Ê podatku od-
prowadzanego przez pracownika etatowego, by inna wyliczaïa podatek nale ny od
pracownika zatrudnionego na podstawie umowy o dzieïo.
Mamy do dyspozycji kilka rozwiÈzañ dotyczÈcych przekazywania niezbÚdnych da-
nych pomiÚdzy obiektem kontekstu a obiektem strategii. Mo emy albo przekazywaÊ
wszystkie dane w formie parametrów wywoïywanych metod obiektu strategii, albo
po prostu przekazywaÊ obiektowi strategii referencjÚ do caïego obiektu kontekstu.
Bloki kodu jÚzyka Ruby, które w istocie majÈ postaÊ kodu opakowywanego w ramach
tworzonego na bie Èco obiektu (a konkretnie obiektu Proc), wprost doskonale na-
dajÈ siÚ do bïyskawicznego konstruowania prostych obiektów strategii.
Jak siÚ niedïugo przekonamy, wzorzec projektowy Strategy przypomina (przynajm-
niej na pierwszy rzut oka) wiele innych wzorców. Wzorzec Strategy wymusza stoso-
wanie obiektu (nazywanego kontekstem), który odpowiada za realizacjÚ okre lonego
zadania. Okazuje siÚ jednak, e wykonanie tego zadania wymaga od kontekstu
skorzystania z pomocy innego obiektu (okre lanego mianem strategii). Z bardzo
podobnym modelem mamy do czynienia w przypadku wzorca projektowego Ob-
server (obserwatora), gdzie obiekt realizujÈcy pewne zadania kieruje wywoïania do
drugiego obiektu, bez którego jego funkcjonowanie byïoby niemo liwe.
Okazuje siÚ, e jedynÈ ró nicÈ dzielÈcÈ oba te wzorce jest intencja programisty. Celem
wzorca projektowego Strategy jest dostarczenie obiektowi kontekstu innego obiektu,
który „wie”, jak wykonaÊ okre lonÈ wersjÚ algorytmu. Zupeïnie inny cel przy wieca
programi cie stosujÈcemu wzorzec Observer — otó stosujemy go wtedy, gdy…
Chyba powinni my te rozwa ania przenie Ê do innego rozdziaïu (tak siÚ skïada, e
bÚdzie to kolejny rozdziaï).