Ruby. Wzorce projektowe

654 views
568 views

Published on

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!

Published in: Technology, Education
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
654
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Ruby. Wzorce projektowe

  1. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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ï).

×