SlideShare a Scribd company logo
IDZ DO
         PRZYK£ADOWY ROZDZIA£

                           SPIS TREœCI
                                         Ajax w akcji
                                         Autorzy: Dave Crane, Eric Pascarello, Darren James
                                         ISBN: 83-246-0495-2
           KATALOG KSI¥¯EK               Tytu³ orygina³u: Ajax in Action
                                         Format: B5, stron: 712
                      KATALOG ONLINE

       ZAMÓW DRUKOWANY KATALOG


              TWÓJ KOSZYK                    Praktyczne zastosowania technologii, która zrewolucjonizowa³a aplikacje sieciowe
                                             • Podstawy Ajaksa
                    DODAJ DO KOSZYKA         • Metodologie tworzenia aplikacji internetowych
                                             • Optymalizacja i zabezpieczanie aplikacji
                                         Ajax to technologia tworzenia aplikacji i witryn WWW, która zyskuje coraz wiêksze
         CENNIK I INFORMACJE             uznanie wœród programistów. Dziêki po³¹czeniu jêzyków JavaScript i XML
                                         z asynchroniczn¹ komunikacj¹ miêdzy przegl¹dark¹ i serwerem twórcom tej technologii
                   ZAMÓW INFORMACJE      uda³o siê wyeliminowaæ podstawow¹ wadê aplikacji bazuj¹cych na przegl¹darce
                     O NOWOœCIACH        internetowej, czyli koniecznoœæ oczekiwania na „prze³adowanie” strony. Ajax sprawia, ¿e
                                         niezbêdne dane s¹ pobierane w tle. Zastosowanie tej technologii powoduje, ¿e aplikacje
                       ZAMÓW CENNIK      sieciowe pod wzglêdem obs³ugi coraz bardziej przypominaj¹ tradycyjne programy.
                                         Efekty dzia³ania Ajaksa mo¿na zaobserwowaæ na coraz wiêkszej iloœci witryn WWW.
                                         „Ajax w akcji” to praktyczny przewodnik po zagadnieniach zwi¹zanych z projektowaniem
                 CZYTELNIA               witryn i aplikacji WWW w tej technologii. Czytaj¹c go, poznasz genezê Ajaksa
                                         i podstawy korzystania z niego. Nauczysz siê stosowaæ wzorce projektowe, model MVC
          FRAGMENTY KSI¥¯EK ONLINE
                                         oraz frameworki i biblioteki wspomagaj¹ce tworzenie witryn WWW. Przeczytasz
                                         o profesjonalnych technikach budowania aplikacji sieciowych i zastosujesz je
                                         w praktyce, pisz¹c w³asne systemy. Znajdziesz tu równie¿ informacje o optymalizowaniu
                                         wydajnoœci aplikacji oraz tworzeniu mechanizmów zabezpieczeñ. Dziêki przyk³adom
                                         zgromadzonym w kolejnych rozdzia³ach ksi¹¿ki, takim jak dynamiczna lista rozwijana,
                                         mechanizm podpowiedzi, rozbudowany portal sieciowy oraz interfejsy u¿ytkownika,
                                         poznasz praktyczne aspekty stosowania Ajaksa w projektach.
                                             • Podstawowe zastosowania Ajaksa
                                             • Dostosowywanie istniej¹cych aplikacji do Ajaksa
                                             • Wzorzec MVC w aplikacjach
                                             • Biblioteki i frameworki wspomagaj¹ce pracê nad aplikacjami
Wydawnictwo Helion                           • Oddzielanie logiki od widoku
ul. Koœciuszki 1c                            • Wymiana danych pomiêdzy przegl¹dark¹ i serwerem
44-100 Gliwice                               • Zabezpieczanie aplikacji
tel. 032 230 98 63                           • Optymalizacja wydajnoœci
e-mail: helion@helion.pl
                                            Twórz aplikacje, które bêd¹ nie tylko funkcjonalne, ale równie¿ wygodne w obs³udze
Spis treści


Przedmowa                                                                                                                             13

Podziękowania                                                                                                                         15

O książce                                                                                                                             19

Część I Reorganizacja pojęcia aplikacji internetowej                                                                                  27
1. Nowe projektowanie dla serwisów internetowych                                                                                      29
  1.1. Dlaczego mówi się o wzbogaconych klientach ajaksowych? ............................................ 30
       1.1.1. Porównanie odczuć użytkowników .......................................................................... 31
       1.1.2. Opóźnienia sieci ........................................................................................................ 35
       1.1.3. Interakcje asynchroniczne ........................................................................................ 37
       1.1.4. Długotrwałe i chwilowe wzorce użytkowania ......................................................... 40
       1.1.5. „Oduczanie się” internetu ......................................................................................... 41
  1.2. Cztery zasady definiujące Ajaksa ....................................................................................... 42
       1.2.1. Przeglądarka przechowuje aplikację, nie zawartość ................................................ 42
       1.2.2. Serwer dostarcza dane, nie zawartość ...................................................................... 44
       1.2.3. Interakcja użytkownika z aplikacją może być płynna i ciągła ................................. 46
       1.2.4. To jest prawdziwe pisanie programów i wymaga dyscypliny ................................. 47
  1.3. Wzbogacone klienty ajaksowe w rzeczywistym świecie ................................................... 48
       1.3.1. Badanie terenu .......................................................................................................... 48
       1.3.2. Google Maps .............................................................................................................. 49
  1.4. Alternatywy dla Ajaksa ........................................................................................................ 52
       1.4.1. Rozwiązania oparte na Macromedia Flash .............................................................. 52
       1.4.2. Java Web Start i związane z nią technologie ........................................................... 53
6                 Spis treści



    1.5. Podsumowanie ...................................................................................................................... 53
    1.6. Zasoby .................................................................................................................................... 54

2. Pierwsze kroki z Ajaksem                                                                                                                       55
    2.1. Kluczowe elementy Ajaksa .................................................................................................. 56
    2.2. Sprawianie dobrego wrażenia na użytkownikach za pomocą JavaScriptu .................... 58
    2.3. Definiowanie wyglądu i zachowania przy użyciu CSS ..................................................... 59
          2.3.1. Selektory CSS ............................................................................................................ 60
          2.3.2. Właściwości stylów CSS ........................................................................................... 62
          2.3.3. Prosty przykład CSS .................................................................................................. 63
    2.4. Organizowanie widoku za pomocą DOM .......................................................................... 68
          2.4.1. Praca z DOM przy użyciu JavaScriptu .................................................................... 70
          2.4.2. Znajdowanie węzła DOM ......................................................................................... 72
          2.4.3. Tworzenie węzła DOM ............................................................................................. 73
          2.4.4. Dodawanie stylów do dokumentu ............................................................................ 74
          2.4.5. Droga na skróty: użycie właściwości innerHTML .................................................. 75
    2.5. Asynchroniczne ładowanie danych przy użyciu technologii XML ................................. 76
          2.5.1. <iframe> ................................................................................................................... 77
          2.5.2. Obiekty XmlDocument i XMLHttpRequest ........................................................... 79
          2.5.3. Wysyłanie żądań do serwera ..................................................................................... 81
          2.5.4. Używanie funkcji zwrotnych do monitorowania żądania ........................................ 83
          2.5.5. Pełny cykl życia ......................................................................................................... 84
    2.6. Czym wyróżnia się Ajax? ...................................................................................................... 87
    2.7. Podsumowanie ...................................................................................................................... 89
    2.8. Zasoby .................................................................................................................................... 90

3. Porządkowanie aplikacji wykorzystujących technologię Ajax                                                                                      91
    3.1. Porządkowanie chaosu ......................................................................................................... 93
          3.1.1. Wzorce: tworzenie wspólnego słownictwa .............................................................. 93
          3.1.2. Refaktoryzacja a Ajax ................................................................................................. 94
          3.1.3. Zachowanie właściwych proporcji ........................................................................... 95
          3.1.4. Refaktoryzacja w działaniu ....................................................................................... 96
    3.2. Niewielkie przykłady refaktoryzacji ................................................................................... 99
          3.2.1. Niezgodności przeglądarek: wzorce Façade oraz Adapter ..................................... 99
          3.2.2. Zarządzanie procedurami obsługi zdarzeń: wzorzec Observer ............................ 103
          3.2.3. Wielokrotne stosowanie funkcji obsługujących
                 działania użytkownika: wzorzec Command ........................................................... 107
          3.2.4. Przechowywanie tylko jednej referencji do zasobu: wzorzec Singleton .............. 110
    3.3. Model-View-Controller ...................................................................................................... 114
    3.4. Wzorzec MVC stosowany na serwerze ............................................................................. 117
          3.4.1. Serwerowa część aplikacji tworzona bez użycia wzorców .................................... 117
          3.4.2. Refaktoryzacja modelu dziedziny ........................................................................... 121
          3.4.3. Separacja zawartości od prezentacji ....................................................................... 124
    3.5. Biblioteki i frameworki ...................................................................................................... 128
          3.5.1. Biblioteki zapewniające poprawne działanie skryptów w różnych przeglądarkach ......128
          3.5.2. Kontrolki oraz bazujące na nich szkielety .............................................................. 133
          3.5.3. Frameworki aplikacji ............................................................................................... 136
    3.6. Podsumowanie .................................................................................................................... 140
    3.7. Zasoby .................................................................................................................................. 142
Spis treści                       7



Część II Podstawowe techniki                                                                                                                143
4. Strona WWW jako aplikacja                                                                                                                 145
  4.1. Inny rodzaj wzorca MVC ................................................................................................... 146
        4.1.1. Powielanie wzorca w różnych skalach ................................................................... 146
        4.1.2. Stosowanie wzorca MVC w przeglądarce .............................................................. 147
  4.2. Widoki w aplikacjach Ajax ................................................................................................ 150
        4.2.1. Oddzielanie logiki od widoku ................................................................................. 150
        4.2.2. Oddzielanie widoku od logiki ................................................................................. 156
  4.3. Kontroler w aplikacjach wykorzystujących technologię Ajax ........................................ 160
        4.3.1. Klasyczne procedury obsługi zdarzeń .................................................................... 160
        4.3.2. Model obsługi zdarzeń W3C .................................................................................. 164
        4.3.3. Implementacja elastycznego modelu obsługi zdarzeń w języku JavaScript ........ 165
  4.4. Model w aplikacjach wykorzystujących technologię Ajax ............................................. 170
        4.4.1. Zastosowanie JavaScriptu do stworzenia modelu dziedziny biznesowej ............. 171
        4.4.2. Wymiana danych z serwerem ................................................................................. 173
  4.5. Generacja widoku na podstawie modelu ......................................................................... 175
        4.5.1. Refleksja obiektów JavaScript ................................................................................ 175
        4.5.2. Obsługa tablic i obiektów ....................................................................................... 179
        4.5.3. Dodawanie kontrolera ............................................................................................. 182
  4.6. Podsumowanie .................................................................................................................... 185
  4.7. Zasoby .................................................................................................................................. 186

5. Rola serwera                                                                                                                              187
  5.1. Współpraca z aplikacjami działającymi na serwerze ..................................................... 188
  5.2. Tworzenie aplikacji działającej po stronie serwera ........................................................ 189
        5.2.1. Najczęściej stosowane języki programowania ....................................................... 189
        5.2.2. Architektury n-warstwowe ...................................................................................... 190
        5.2.3. Utrzymanie modeli dziedziny po stronie klienta i serwera ................................ 191
  5.3. Postać ogólna: najczęściej stosowane projekty aplikacji wykonywanych na serwerze ........ 193
        5.3.1. Naiwne tworzenie kodu działającego
               na serwerze bez stosowania żadnego frameworka ................................................. 193
        5.3.2. Korzystanie z frameworków Model2 ...................................................................... 195
        5.3.3. Frameworki bazujące na komponentach ............................................................... 196
        5.3.4. Stosowanie architektur bazujących na usługach ................................................... 199
  5.4. Informacje szczegółowe: wymiana danych ...................................................................... 204
        5.4.1. Interakcje klienckie ................................................................................................. 205
        5.4.2. Prezentacja przykładowej aplikacji przeglądarki planet ........................................... 205
        5.4.3. Myśl jak strona WWW — interakcje operujące na zawartości ............................ 208
        5.4.4. Myśl jak plugin — interakcje operujące na skryptach .......................................... 212
        5.4.5. Myśl jak aplikacja — interakcje operujące na danych .......................................... 218
  5.5. Przekazywanie danych na serwer ..................................................................................... 223
        5.5.1. Stosowanie formularzy HTML ............................................................................... 224
        5.5.2. Stosowanie obiektu XMLHttpRequest .................................................................. 226
        5.5.3. Efektywna obsługa aktualizacji danych ................................................................. 227
  5.6. Podsumowanie .................................................................................................................... 236
  5.7. Zasoby .................................................................................................................................. 237
8                 Spis treści



Część III Profesjonalny Ajax                                                                                                                  239
6. Wrażenia użytkownika                                                                                                                        241
    6.1. Róbmy to dobrze — pisanie dobrych aplikacji ............................................................... 242
          6.1.1. Łatwość i szybkość reakcji ...................................................................................... 243
          6.1.2. Solidność .................................................................................................................. 244
          6.1.3. Konsekwencja .......................................................................................................... 244
          6.1.4. Prostota .................................................................................................................... 245
          6.1.5. Zapewnienie działania ............................................................................................. 246
    6.2. Informowanie użytkownika ............................................................................................... 246
          6.2.1. Obsługa odpowiedzi na nasze żądania ................................................................... 247
          6.2.2. Obsługa modyfikacji wprowadzanych przez inne osoby ...................................... 248
    6.3. Projektowanie systemu powiadomień dla aplikacji Ajax ............................................... 253
          6.3.1. Modelowanie powiadomień .................................................................................... 254
          6.3.2. Definiowanie wymagań interfejsu .......................................................................... 256
    6.4. Implementacja frameworka do obsługi powiadomień ................................................... 257
          6.4.1. Prezentacja ikony na pasku stanu ........................................................................... 257
          6.4.2. Prezentacja szczegółów powiadomienia ................................................................ 260
          6.4.3. Scalanie poszczególnych elementów systemu ....................................................... 261
    6.5. Zastosowanie frameworka podczas obsługi żądań sieciowych ...................................... 267
    6.6. Sygnalizacja aktualności danych ....................................................................................... 271
          6.6.1. Zdefiniowanie prostego sposobu wyróżniania ....................................................... 272
          6.6.2. Wyróżnianie przy użyciu biblioteki efektów Scriptaculous .................................. 274
    6.7. Podsumowanie .................................................................................................................... 275
    6.8. Zasoby .................................................................................................................................. 275

7. Bezpieczeństwo a Ajax                                                                                                                       277
    7.1. Bezpieczeństwo języka JavaScript i przeglądarki ........................................................... 278
          7.1.1. Wprowadzanie polityki „serwera pochodzenia” .................................................... 279
          7.1.2. Problemy do rozważenia w aplikacjach Ajax ......................................................... 280
          7.1.3. Problemy z poddomenami ...................................................................................... 280
          7.1.4. Bezpieczeństwo w różnych przeglądarkach .......................................................... 281
    7.2. Komunikacja ze zdalnymi usługami ................................................................................. 283
          7.2.1. Pośredniczenie w korzystaniu ze zdalnych usług .................................................. 283
          7.2.2. Korzystanie z usług WWW ..................................................................................... 284
    7.3. Ochrona poufnych danych ................................................................................................. 295
          7.3.1. Atak „man in the middle” ........................................................................................ 295
          7.3.2. Stosowanie bezpiecznego protokołu HTTP .......................................................... 296
          7.3.3. Użycie JavaScriptu do szyfrowania danych przesyłanych protokołem HTTP .... 297
    7.4. Zabezpieczanie transmisji danych w aplikacjach wykorzystujących technologię Ajax ....... 300
          7.4.1. Projektowanie bezpiecznej warstwy internetowej ................................................ 301
          7.4.2. Ograniczanie dostępu do danych ........................................................................... 305
    7.5. Podsumowanie .................................................................................................................... 310
    7.6. Zasoby .................................................................................................................................. 310

8. Wydajność                                                                                                                                   313
    8.1. Czym jest wydajność? ......................................................................................................... 314
    8.2. Szybkość wykonywania kodu JavaScript .......................................................................... 315
          8.2.1. Profilowanie aplikacji w najprostszy możliwy sposób ........................................... 316
          8.2.2. Użycie programu profilującego Venkman ............................................................. 322
          8.2.3. Optymalizacja szybkości wykonywania kodu JavaScript ........................................... 324
Spis treści                       9



  8.3. Wymagania pamięciowe kodu JavaScript ....................................................................... 336
        8.3.1. Zapobieganie „wyciekom” pamięci ........................................................................ 337
        8.3.2. Zagadnienia odnoszące się do technologii Ajax ..................................................... 341
  8.4. Projektowanie pod kątem wydajności .............................................................................. 346
        8.4.1. Mierzenie zajętości pamięci ................................................................................... 346
        8.4.2. Prosty przykład ........................................................................................................ 351
        8.4.3. Wyniki: jak 150-krotnie zmniejszyć zużycie pamięci? .......................................... 355
  8.5. Podsumowanie .................................................................................................................... 358
  8.6. Zasoby .................................................................................................................................. 358

Część IV Ajax w przykładach                                                                                                                 361
9. Podwójna dynamiczna lista rozwijalna                                                                                                      363
  9.1. Skrypt podwójnej listy rozwijalnej ................................................................................... 364
        9.1.1. Ograniczenia rozwiązania działającego po stronie klienta .................................... 364
        9.1.2. Ograniczenia rozwiązania działającego po stronie serwera .................................. 365
        9.1.3. Rozwiązanie wykorzystujące Ajaksa ....................................................................... 366
  9.2. Architektura warstwy klienta ............................................................................................ 367
        9.2.1. Projektowanie formularza ....................................................................................... 368
        9.2.2. Projektowanie interakcji klient-serwer .................................................................. 369
  9.3. Implementacja serwera — VB.NET ................................................................................ 371
        9.3.1. Definiowanie formatu odpowiedzi XML ............................................................... 372
        9.3.2. Pisanie kodu wykonywanego na serwerze ............................................................. 372
  9.4. Przetwarzanie wyników ..................................................................................................... 375
        9.4.1. Przeglądanie dokumentu XML .............................................................................. 376
        9.4.2. Zastosowanie kaskadowych arkuszy stylów ........................................................... 378
  9.5. Zagadnienia zaawansowane .............................................................................................. 379
        9.5.1. Zapewnienie możliwości wyboru większej ilości opcji ......................................... 380
        9.5.2. Zamiana listy podwójnej na potrójną ..................................................................... 381
  9.6. Refaktoring .......................................................................................................................... 382
        9.6.1. Nowy i poprawiony komponent net.ContentLoader ............................................ 383
        9.6.2. Tworzenie komponentu podwójnej listy rozwijalnej ............................................ 389
  9.7. Podsumowanie .................................................................................................................... 396

10. Sugestie prezentowane podczas wpisywania                                                                                                 399
  10.1. Zastosowania i cechy mechanizmu prezentacji sugestii ................................................. 400
        10.1.1. Cechy mechanizmów prezentacji sugestii ............................................................. 400
        10.1.2. Google Suggest ........................................................................................................ 402
        10.1.3. Autorski mechanizm prezentowania sugestii ......................................................... 403
  10.2. Skrypt wykonywany na serwerze — C# .......................................................................... 404
        10.2.1. Serwer oraz baza danych ......................................................................................... 404
        10.2.2. Testowanie kodu wykonywanego na serwerze ...................................................... 407
  10.3. Framework działający po stronie klienta ........................................................................ 408
        10.3.1. HTML ...................................................................................................................... 408
        10.3.2. JavaScript ................................................................................................................. 409
        10.3.3. Pobieranie danych z serwera .................................................................................. 419
  10.4. Rozbudowa możliwości funkcjonalnych:
           więcej elementów używających różnych zapytań ...................................................... 431
10                Spis treści



     10.5. Refaktoring .......................................................................................................................... 432
           10.5.1. Dzień 1. Określenie planu prac nad komponentem TextSuggest ........................ 433
           10.5.2. Dzień 2. Tworzenie komponentu TextSuggest — „eleganckiego”
                   i konfigurowalnego .................................................................................................. 437
           10.5.3. Dzień 3. Zastosowanie Ajaksa ................................................................................. 441
           10.5.4. Dzień 4. Obsługa zdarzeń ....................................................................................... 446
           10.5.5. Dzień 5. Interfejs użytkownika listy sugestii ......................................................... 453
           10.5.6. Podsumowanie prac nad komponentem ................................................................ 462
     10.6. Podsumowanie .................................................................................................................... 462

11. Rozbudowany portal wykorzystujący Ajaksa                                                                                                  463
     11.1. Ewolucja portali .................................................................................................................. 464
           11.1.1. Klasyczne portale ..................................................................................................... 465
           11.1.2. Portale o zaawansowanym interfejsie użytkownika ............................................... 465
     11.2. Architektura portalu używającego Ajaksa i Javy ............................................................ 467
     11.3. Logowanie wykorzystujące Ajaksa ................................................................................... 469
           11.3.1. Tabela użytkowników .............................................................................................. 470
           11.3.2. Kod obsługujący logowanie na serwerze: Java ...................................................... 471
           11.3.3. Mechanizm logowania działający po stronie klienta ............................................. 474
     11.4. Implementacja okien DHTML ......................................................................................... 480
           11.4.1. Baza danych okien ................................................................................................... 480
           11.4.2. Kod obsługi okien działający na serwerze .............................................................. 482
           11.4.3. Dodawanie zewnętrznej biblioteki JavaScript ....................................................... 486
     11.5. Implementacja automatycznego zapisu właściwości ...................................................... 489
           11.5.1. Dostosowanie biblioteki .......................................................................................... 489
           11.5.2. Automatyczny zapis informacji w bazie danych .................................................... 491
     11.6. Refaktoring .......................................................................................................................... 494
           11.6.1. Definiowanie konstruktora ...................................................................................... 497
           11.6.2. Dostosowanie biblioteki AjaxWindows.js .............................................................. 497
           11.6.3. Określanie poleceń obsługiwanych przez portal ................................................... 500
           11.6.4. Wykorzystanie technologii Ajax .............................................................................. 504
           11.6.5. Podsumowanie prac nad komponentem ................................................................ 505
     11.7. Podsumowanie .................................................................................................................... 506

12. Dynamiczne wyszukiwanie przy użyciu XSLT                                                                                                  507
     12.1. Analiza technik wyszukiwania ........................................................................................... 508
           12.1.1. Klasyczne rozwiązania wyszukiwania ..................................................................... 509
           12.1.2. Wady metody wykorzystującej ramki i wyskakujące okienka ............................... 510
           12.1.3. Analiza dynamicznego wyszukiwania przy użyciu technologii Ajax i XSLT ........ 512
           12.1.4. Przesyłanie wyników do klienta .............................................................................. 513
     12.2. Kod klienta .......................................................................................................................... 514
           12.2.1. Konfiguracja klienta ................................................................................................. 515
           12.2.2. Rozpoczęcie procesu ............................................................................................... 516
     12.3. Kod działający na serwerze — PHP ................................................................................. 518
           12.3.1. Generacja dokumentu XML ................................................................................... 518
           12.3.2. Tworzenie dokumentu XSLT ................................................................................. 521
     12.4. Łączenie dokumentów XML i XSLT ................................................................................ 523
           12.4.1. Przekształcenia XSLT w Internet Explorerze ....................................................... 525
           12.4.2. Przekształcenia XSLT w przeglądarkach Mozilla i Firefox .................................. 526
Spis treści                     11



  12.5. Dopracowanie wyszukiwania ............................................................................................ 527
        12.5.1. Zastosowanie CSS .................................................................................................... 528
        12.5.2. Usprawnianie wyszukiwania ................................................................................... 529
        12.5.3. Podejmowanie decyzji o zastosowaniu XSLT ........................................................ 531
        12.5.4. Rozwiązanie problemu zakładek ............................................................................ 533
  12.6. Refaktoring .......................................................................................................................... 534
        12.6.1. XSLTHelper ............................................................................................................ 535
        12.6.2. Komponent dynamicznego wyszukiwania ............................................................. 539
        12.6.3. Podsumowanie prac nad komponentem ................................................................ 544
  12.7. Podsumowanie .................................................................................................................... 544

13. Tworzenie niezależnych aplikacji wykorzystujących technologię Ajax                                                                     547
  13.1. Odczytywanie informacji ze świata .................................................................................. 548
        13.1.1. Poszukiwanie kanałów XML ................................................................................... 549
        13.1.2. Struktura formatu RSS ............................................................................................ 550
  13.2. Tworzenie bogatego interfejsu użytkownika ................................................................... 553
        13.2.1. Proces ....................................................................................................................... 554
        13.2.2. Strona HTML bez tabel .......................................................................................... 555
        13.2.3. Formatowanie przy użyciu CSS ............................................................................. 557
  13.3. Pobieranie zawartości kanałów RSS ................................................................................. 563
        13.3.1. Zasięg globalny ........................................................................................................ 563
        13.3.2. Wczytywanie danych ............................................................................................... 565
  13.4. Utworzenie wizualnego efektu przejścia .......................................................................... 569
        13.4.1. Nieprzezroczystość w różnych przeglądarkach ..................................................... 569
        13.4.2. Implementacja efektu przejścia .............................................................................. 570
        13.4.3. Zastosowanie liczników czasu ................................................................................. 572
  13.5. Dodatkowe możliwości ....................................................................................................... 574
        13.5.1. Dodawanie kolejnych kanałów ............................................................................... 575
        13.5.2. Implementacja możliwości przechodzenia
                do innych artykułów i wstrzymywania czytnika .................................................... 577
  13.6. Unikanie ograniczeń projektu ........................................................................................... 579
        13.6.1. Problemy z systemem zabezpieczeń przeglądarek fundacji Mozilla .................... 580
        13.6.2. Zmiana zakresu aplikacji ......................................................................................... 583
  13.7. Refaktoring ......................................................................................................................... 584
        13.7.1. Model używany w czytniku RSS ............................................................................ 584
        13.7.2. Widok stosowany w czytniku RSS .......................................................................... 587
        13.7.3. Kontroler czytnika RSS ........................................................................................... 591
        13.7.4. Podsumowanie wprowadzonych zmian .................................................................. 605
  13.8. Podsumowanie .................................................................................................................... 605

Część V Dodatki                                                                                                                           607
A Przybornik programisty aplikacji Ajax                                                                                                    609

B JavaScript dla programistów obiektowych                                                                                                  639

C Frameworki i biblioteki ułatwiające stosowanie technologii Ajax                                                                          671

Skorowidz                                                                                                                                  691
Porządkowanie
      aplikacji wykorzystujących
                 technologię Ajax


Zagadnienia opisywane w tym rozdziale:
♦ Tworzenie i pielęgnacja rozbudowanego
  kodu wykorzystującego możliwości
  technologii Ajax.
♦ Refaktoryzacja kodu JavaScript
  wykorzystującego technologię Ajax.
♦ Wzorce projektowe najczęściej używane
  w aplikacjach wykorzystujących Ajaksa.
♦ Stosowanie wzorca projektowego MVC
  w aplikacjach wykonywanych po stronie
  serwera.
♦ Przegląd dostępnych bibliotek ułatwiających
  korzystanie z technologii Ajax.
92    ROZDZIAŁ 3
      Porządkowanie aplikacji wykorzystujących technologię Ajax


     W rozdziale 2. opisaliśmy wszystkie podstawowe technologie, które ogólnie
     określamy jako Ajax. Dysponując wiedzą, jaką już zdobyliśmy, możemy napi-
     sać niesamowitą aplikację wykorzystującą technologię Ajax, o jakiej zawsze ma-
     rzyliśmy. Równie dobrze możemy jednak wpaść w bardzo poważne kłopoty
     i skończyć na całej masie złożonego kodu JavaScript, HTML i arkuszy stylów,
     których utrzymanie i pielęgnacja są praktycznie niemożliwe i który, z jakichś
     całkowicie niewyjaśnionych przyczyn, pewnego dnia może przestać działać.
     Albo może być jeszcze gorzej — możemy stworzyć aplikację działającą tak długo,
     jak długo nie będziemy przy niej nic robić, oddychać ani wydawać żadnych gło-
     śniejszych dźwięków. Znalezienie się w takiej sytuacji podczas prac nad naszym
     własnym, prywatnym projektem może być jedynie zniechęcające. Jeśli jednak
     znajdziemy się w takiej sytuacji podczas modyfikacji witryny pracodawcy lub
     klienta, który płaci za jej utrzymanie i zażądał właśnie wprowadzenia kilku zmian
     tu i tam, to bez wątpienia będzie to przerażające.
         Na szczęście takie problemy występowały powszechnie już od samego po-
     czątku ery komputerów, a można sądzić, że nawet wcześniej! Wiele osób łamało
     sobie głowy nad znalezieniem sposobu zarządzania złożonością oraz utrzymaniem
     coraz większych ilości kodu źródłowego w takim porządku, który umożliwiałby
     jego dalsze rozwijanie i pielęgnację. W tym rozdziale przedstawimy podstawowe
     narzędzia, które pozwolą nam zachować orientację w tym, co dzieje się w naszym
     kodzie, pisać aplikacje wykorzystujące technologię Ajax, jakie spełnią oczeki-
     wania naszych klientów, a jednocześnie sprawią, że będziemy mogli wracać do
     domu o normalnej porze.
         Ajax stanowi przełom w porównaniu z wcześniej stosowanymi technologiami
     DHTML i to nie tylko pod względem sposobu połączenia poszczególnych tech-
     nologii, lecz także skali, na jaką są one stosowane. Aplikacje wykorzystujące
     technologię Ajax w znacznie większym stopniu używają skryptów JavaScript, a ich
     kod jest przechowywany w przeglądarce znacznie dłużej. W efekcie muszą one
     rozwiązywać znacznie poważniejsze problemy ze złożonością, które nie wystę-
     powały we wcześniejszych aplikacjach DHTML.
         W tym rozdziale przedstawimy narzędzia i techniki, które pozwolą Czytel-
     nikowi zachować przejrzystość i prostotę tworzonego kodu. Z naszego doświad-
     czenia wynika, iż są one najbardziej skuteczne w przypadku tworzenia dużych
     i złożonych aplikacji wykorzystujących technologię Ajax. W przypadku pisania
     prostych aplikacji sugerujemy pominąć tę część książki i przejść bezpośrednio
     do rozdziałów prezentujących konkretne rozwiązania, czyli do rozdziału 9. i kolej-
     nych. Jeśli Czytelnik już doskonale zna zasady refaktoryzacji oraz wszystkie
     wzorce projektowe, to może pominąć ten rozdział i zająć się lekturą rozdziałów
     opisujących zastosowanie tych technik w aplikacjach wykorzystujących technolo-
     gię Ajax (czyli w rozdziałach od 4. do 6.). Niemniej jednak podstawowe informacje,
     jakie prezentujemy w tym rozdziale, mają duże znaczenie dla zapewniania
     wysokiej jakości kodu JavaScript, dlatego też przypuszczamy, że wcześniej lub
     później Czytelnik i tak tu zajrzy. Skorzystamy także z nadarzającej się sposobności
     i pod koniec rozdziału przedstawimy dostępne biblioteki ułatwiające korzysta-
     nie z technologii Ajax; a zatem jeśli Czytelnik szuka biblioteki lub frameworka,
     który mógłby mu ułatwić pracę nad projektem, to może zajrzeć do podroz-
     działu 3.5.
3.1.                                                   Porządkowanie chaosu           93



3.1.   Porządkowanie chaosu
       Podstawowym narzędziem, jakie zastosujemy, będzie tzw. refaktoryzacja — czyli
       proces polegający na przepisywaniu kodu lub wprowadzaniu w nim modyfikacji,
       które nie mają na celu rozbudowy jego możliwości funkcjonalnych, lecz popra-
       wienie jego przejrzystości. Poprawianie przejrzystości samo w sobie może być
       doskonałym powodem do wprowadzania zmian w kodzie, niemniej jednak ma
       ono także i inne zalety, które powinny przekonać każdego.
           Zazwyczaj znacznie łatwiej jest dodawać nowe możliwości do opracowanego
       wcześniej kodu, modyfikować już zaimplementowane lub usuwać je. Najpro-
       ściej rzecz ujmując, takie operacje są zrozumiałe. Jeśli jednak kod aplikacji nie
       zostanie dobrze opracowany i zaprojektowany, to chociaż będzie on poprawnie
       funkcjonował i spełniał wymagania zleceniodawcy, to jednak osoby pracujące nad
       nim mogą nie do końca rozumieć, jak on działa oraz dlaczego działa poprawnie.
           Zmiany wymagań aplikacji, często połączone z krótkim okresem realizacji,
       są przykrą codziennością dla programistów. Refaktoryzacja pozwala na zapew-
       nienie przejrzystości kodu, ułatwia jego utrzymanie i pozwala, by perspektywa
       implementacji zmian w założeniach projektu nie napawała nas przerażeniem.
           Podstawowe sposoby refaktoryzacji poznaliśmy już w rozdziale 2., przy okazji
       przenoszenia kodu JavaScript, HTML oraz definicji stylów do osobnych plików.
       Jednak kod JavaScript staje się już długi, gdy objętość pliku przekroczy około
       120 wierszy oraz gdy zaczniemy w nim stosować możliwości funkcjonalne niskiego
       poziomu (takie jak generowanie żądań i przesyłanie ich na zdalny serwer) z kodem
       operującym na obiektach. Kiedy zaczniemy tworzyć większe projekty, umiesz-
       czanie całego kodu JavaScript w jednym pliku przestanie być dobrym rozwią-
       zaniem (to samo zresztą dotyczy arkuszy stylów). Cel, do którego dążymy — two-
       rzenie niewielkich, przejrzystych, zrozumiałych i łatwych do modyfikacji fragmen-
       tów kodu obsługujących konkretne zagadnienia — jest często określany jako
       separacja obowiązków (ang. separation of responsibility).
           Jednak refaktoryzacja jest często przeprowadzana także z innego powodu,
       pozwala ona bowiem na wskazanie często stosowanych rozwiązań, sposobów
       realizacji konkretnych zadań i tworzenie kodu na podstawie określonych wzorców.
       Także te cele są dostateczną motywacją do wprowadzania zmian w kodzie, a dąże-
       nie do nich ma bardzo praktyczne konsekwencje. Przedstawimy je w kolejnym
       punkcie rozdziału.


3.1.1. Wzorce: tworzenie wspólnego słownictwa
       Prawdopodobieństwo, że kod spełniający założenia jakiegoś dobrze znanego
       i powszechnie stosowanego wzorca będzie działać poprawnie, jest bardzo duże.
       Wynika to z prostego faktu, iż taki kod był już wielokrotnie tworzony i stosowany.
       Wiele problemów, jakie występują podczas tworzenia konkretnego rozwiązania
       zostało już dokładnie przemyślanych i, miejmy nadzieję, skutecznie rozwiąza-
       nych. A jeśli mamy szczęście, to może się okazać, że ktoś już napisał nadający się
       do wielokrotnego stosowania kod, realizujący dokładnie te możliwości funkcjo-
       nalne, o jakie nam chodzi.
94     ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


          Taki sposób wykonywania konkretnego zadania nazywamy wzorcem projek-
      towym. Pojęcie wzorca pojawiło się w latach 70. ubiegłego wieku w odniesie-
      niu do sposobów rozwiązywania problemów występujących w architekturze i pla-
      nowaniu. Później zapożyczono je także do świata programowania komputerów,
      gdzie z powodzeniem jest stosowane od blisko dziesięciu lat. Wzorce projek-
      towe są bardzo popularne w środowisku programistów wykorzystujących język
      Java, a ostatnio firma Microsoft usilnie stara się wprowadzić je także w platformie
      .NET. Termin „wzorzec projektowy” bardzo często jest kojarzony z czymś aka-
      demickim, teoretycznym i odpychającym; równie często nadużywa się go lub
      stosuje błędnie, by wywrzeć większe wrażenie na rozmówcach. Jednak wzo-
      rzec projektowy jest jedynie opisem sposobu rozwiązania pewnego konkretne-
      go problemu, który bardzo często występuje podczas projektowania i tworzenia
      oprogramowania. Warto zauważyć, iż wzorce projektowe określają i nadają na-
      zwy abstrakcyjnym, technicznym rozwiązaniom, dzięki czemu nie tylko łatwiej
      można o nich rozmawiać, lecz także łatwiej jest je zrozumieć.
          Wzorce projektowe mogą być ważne w kontekście refaktoryzacji kodu, gdyż
      pozwalają nam na zwięzłe i precyzyjne opisanie celów, do jakich dążymy. Stwier-
      dzenie, iż chodzi nam o „zaimplementowanie, w formie obiektów tych fragmen-
      tów kodu, które realizują proces obsługi konkretnej czynności wykonywanej
      przez użytkownika” jest długie, złożone i raczej trudno się na nim skoncentro-
      wać podczas wprowadzania zmian w kodzie. Jeśli jednak stwierdzimy, że chcemy
      zastosować wzorzec projektowy Command, to nasz cel nie tylko będzie bardziej
      precyzyjnie określony, lecz także łatwiej będzie nam o nim rozmawiać.
          Jeśli Czytelnik zna się na pisaniu w Javie programów wykonywanych po
      stronie serwera bądź jest architektem oprogramowania, który zna się na rzeczy,
      to zapewne zacznie się zastanawiać, cóż takiego odkrywczego jest w tym, co przed
      chwilą napisaliśmy. Z kolei, jeśli Czytelnik wywodzi się z kręgów projektantów
      stron WWW lub osób zajmujących się nowymi rodzajami mediów, to pewnie
      pomyśli, że jesteśmy tymi natchnionymi dziwakami, którzy wolą rysowanie śmiesz-
      nych schematów i diagramów od tworzenia kodu. Jednak niezależnie od tego,
      kim jest Czytelnik i jakie ma doświadczenia, to mógł pomyśleć, co to wszystko
      ma wspólnego z aplikacjami wykorzystującymi technologię Ajax. Otóż ma, i to
      całkiem sporo. Przekonajmy się zatem, co programista tworzący aplikacje wyko-
      rzystujące Ajaksa może zyskać dzięki refaktoryzacji.


3.1.2. Refaktoryzacja a Ajax
      Wspominaliśmy już, że aplikacje wykorzystujące technologię Ajax będą zazwy-
      czaj zawierać więcej kodu JavaScript oraz że ten kod będzie dłużej działać w śro-
      dowisku przeglądarki niż w tradycyjnych aplikacjach internetowych.
          W tradycyjnych aplikacjach internetowych najbardziej złożony kod jest wy-
      konywany na serwerze, a wzorce projektowe są używane przy pisaniu skryptów
      i aplikacji w językach PHP, Java oraz językach używanych na platformie .NET.
      Jednak w aplikacjach wykorzystujących technologię Ajax te same techniki możemy
      zastosować podczas tworzenia kodu wykonywanego po stronie przeglądarki.
3.1.                                                  Porządkowanie chaosu           95



           Można nawet podać argument sugerujący, iż zastosowanie takiego sposobu
       organizacji kodu jest znacznie bardziej potrzebne w języku JavaScript niż w języ-
       kach o rygorystycznej kontroli typów, takich jak Java lub C#. Pomimo tego iż
       składnia języka JavaScript bardzo przypomina składnię C, to jednak sam język
       jest znacznie bardziej podobny do języków skryptowych, takich jak Ruby, Python,
       bądź nawet Common Lisp niż do Javy lub C#. JavaScript zapewnia ogromną
       elastyczność i przestrzeń do opracowywania własnych rozwiązań i metodologii.
       W rękach doświadczonego programisty jest to wspaniałe narzędzie, jednak
       przed przeciętnymi programistami stawia trudne i niebezpieczne wyzwania.
       Języki najczęściej stosowane do tworzenia aplikacji korporacyjnych, takie jak
       Java lub C# zostały zaprojektowane w taki sposób, by ułatwiały pracę zespołów
       przeciętnych programistów i dawały możliwość szybkiego zdobywania nowych
       użytkowników. Jednak JavaScript jest zupełnie innym językiem.
           W przypadku stosowania JavaScriptu prawdopodobieństwo tworzenia za-
       wikłanego, niezrozumiałego kodu jest bardzo wysokie; a kiedy zaczniemy używać
       go nie tylko do tworzenia prostych sztuczek, lecz także do pisania złożonych
       aplikacji wykorzystujących Ajaksa, to wszystkie te zagrożenia staną się znacznie
       bardziej realne i zauważalne. Właśnie dlatego gorąco zachęcamy, by znacznie
       częściej stosować refaktoryzację w aplikacjach wykorzystujących technologię
       Ajax niż w aplikacjach pisanych w językach Java lub C#, czyli językach bezpiecz-
       nych, w przypadku których społeczność programistów już dawno opracowała
       i zaczęła stosować wzorce projektowe.


3.1.3. Zachowanie właściwych proporcji
       Zanim zajmiemy się kolejnymi zagadnieniami, koniecznie należy podkreślić, że
       zarówno refaktoryzacja, jak i wzorce projektowe są jedynie narzędziami i należy
       je stosować wyłącznie w przypadku, gdy może to zapewnić wymierne korzyści.
       Jeśli popadniemy w przesadę, to możemy doprowadzić do sytuacji określanej
       jako paraliż przez analizę (ang. paralysis by analisys), w której implementacja
       aplikacji jest wstrzymywana przez ciągłe zmiany projektu, mające na celu popra-
       wienie elastyczności struktury kodu i uwzględnienie potencjalnych zmian wy-
       magań, jakie mogą, lecz nie muszą, się pojawić.
           Errich Gamma, ekspert zajmujący się wzorcami projektowymi, bardzo traf-
       nie podsumował tę sytuację w jednym z ostatnich wywiadów (patrz podrozdział
       „Zasoby” zamieszczony na samym końcu tego rozdziału) — opowiadał w nim
       czytelniku, który zwrócił się do niego z prośbą o pomoc, gdyż w tworzonej aplika-
       cji udało mu się zaimplementować jedynie 21 z 23 wzorców projektowych opi-
       sanych w książce Design Patterns. Należy pamiętać, że wzorce projektowe nadają
       się do zastosowania wyłącznie w określonych sytuacjach, tak samo jak różne
       typy danych — przecież nikt przy zdrowych zmysłach nie starałby się używać
       wszystkich dostępnych typów danych, liczb całkowitych, zmiennoprzecinkowych,
       łańcuchów znaków, tablic, itd. — w każdym fragmencie kodu.
           Gamma stwierdza, że właśnie refaktoryzacja jest najlepszą okazją do wpro-
       wadzania wzorców projektowych. Najpierw należy napisać działający kod
       w możliwie jak najprostszy sposób, a dopiero potem starać się stosować wzorce
96     ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      do rozwiązywania określonych problemów, jakie udało się nam wyróżnić. Jeśli
      Czytelnik sam jest autorem kodu lub jeśli musi „opiekować się” kodem, w którym
      „nabałaganił” ktoś inny, to zapewne aż do tej chwili odczuwa nieprzyjemny
      skurcz żołądka. Na szczęście wzorce projektowe można także stosować z po-
      wodzeniem w już istniejącym kodzie i to bez względu na to, jak został on napisany.
      W następnym punkcie rozdziału przypomnimy jakiś fragment kodu zapisany
      w rozdziale 2. i przekonamy się, co będziemy z niego mogli zrobić dzięki zasto-
      sowaniu refaktoryzacji.


3.1.4. Refaktoryzacja w działaniu
      Być może niektórym z Czytelników refaktoryzacja już się spodobała, jednak
      bardziej praktyczne osoby, zanim się nią zachwycą, wolałyby zapewne ujrzeć ją
      w działaniu. Poświęćmy zatem nieco czasu na wprowadzenie zmian w kodzie,
      który napisaliśmy w poprzednim rozdziale i przedstawiliśmy na listingu 2.11.
      Kod ten składa się z funkcji sendRequest(), która odpowiada za przesyłanie żądań
      na serwer. Funkcja sendRequest() wywołuje z kolei funkcję initHttpRequest(),
      która tworzy odpowiedni obiekt XMLHttpRequest i definiuje funkcję obsługującą
      odpowiedzi — onReadyState(). Obiekt XMLHttpRequest został zapisany w zmiennej
      globalnej, dzięki czemu wszystkie używane przez nas funkcje mogły bez trudu
      pobrać referencję do niego. Funkcja zwrotna obsługująca odpowiedzi spraw-
      dzała stan obiektu żądania i generowała stosowne informacje testowe.
          Kod przedstawiony na listingu 2.11 wykonuje dokładnie to, o co nam chodziło,
      jednak jego ponowne wykorzystanie przysporzyłoby nam sporych problemów.
      Zazwyczaj jeśli decydujemy się na przesyłanie żądania na serwer, chcemy prze-
      twarzać odpowiedzi i wykonywać pewne operacje charakterystyczne jedynie
      dla tej konkretnej aplikacji. W celu zastosowania nowej logiki biznesowej w ist-
      niejącym już kodzie, będziemy musieli zmodyfikować pewne fragmenty funkcji
      onReadyState().
          Sporym problemem może być także zastosowanie zmiennych globalnych.
      Jeśli jednocześnie będziemy chcieli przesłać na serwer kilka żądań, musimy mieć
      możliwość określenia odpowiedniej funkcji zwrotnej w każdym z nich. Jeżeli
      np. będziemy pobierali listę zasobów do aktualizacji oraz drugą listę zasobów
      przeznaczonych do aktualizacji, to przecież należałoby je w jakiś sposób roz-
      różnić!
          W przypadku zastosowania technik programowania obiektowego rozwiąza-
      niem takiego problemu byłoby zaimplementowanie niezbędnych możliwości
      funkcjonalnych w formie obiektu. Język JavaScript zapewnia niezbędne moż-
      liwości, które z powodzeniem będziemy mogli zastosować. Obiekt, jaki utwo-
      rzymy w tym rozdziale, nazwiemy ContentLoader, gdyż będziemy go używać do
      pobierania zawartości z serwera. Ale jak ten obiekt powinien wyglądać? Opty-
      malnie byłoby, gdybyśmy mogli utworzyć go, przekazując w wywołaniu kon-
      struktora adres URL, na jaki należy przesłać żądanie. Oprócz tego powinniśmy
      mieć możliwość przekazania referencji do zdefiniowanej przez nas funkcji
      zwrotnej, która zostanie wywołana w razie pomyślnego zakończenia obsługi
3.1.                                                    Porządkowanie chaosu            97



       żądania, oraz drugiej funkcji — wywoływanej w razie wystąpienia jakichś błędów.
       Instrukcja tworząca ten obiekt mogłaby mieć zatem następującą postać:
          new loader = new ContentLoader('mydata.xml', parseMyData);

       Przy czym parseMyData jest funkcją, którą należy wywołać w momencie ode-
       brania odpowiedzi na żądanie. Kod obiektu ContentLoader przedstawiliśmy na
       listingu 3.1. Zastosowaliśmy w nim kilka nowych rozwiązań, które wymagają
       dokładniejszego opisu.

        Listing 3.1. Obiekt ContentLoader
          var net=new Object();        Obiekt pełniący funkcję przestrzeni nazw
          net.READY_STATE_UNINITIALIZED=0;
          net.READY_STATE_LOADING=1;
          net.READY_STATE_LOADED=2;
          net.READY_STATE_INTERACTIVE=3;
          net.READY_STATE_COMPLETE=4;
          net.ContentLoader=function(url,onload,onerror){          Funkcja konstruktora
            this.url=url;
            this.req=null;
            this.onload=onload;
            this.onerror=(onerror) ? onerror : this.defaultError;
            this.loadXMLDoc(url);
          }
          net.ContentLoader.prototype = {
            loadXMLDoc:function(url){       Funkcja initXMLHttpRequest ze zmienioną nazwą
               if (window.XMLHttpRequest){                              Zmodyfikowana
                 this.req=new XMLHttpRequest();                          funkcja loadXML
               } else if (window.ActiveXObject){
                 this.req=new ActiveXObject("Microsoft.XMLHTTP");
               }
               if (this.req){
                 try{
                   var loader=this;
                   this.req.onreadystatechange=function(){
                     net.ContentLoader.onReadyState.call(loader);
                   }
                   this.req.open('GET',url,true);     Zmodyfikowana funkcja sendRequest
                   this.req.send(null);
                 }catch (err){
                   this.onerror.call(this);
                 }
               }
            },
            onReadyState:function(){        Zmodyfikowana funkcja zwrotna
               var req=this.req;
               var ready=req.readyState;
               var httpStatus=req.status;
               if (ready==net.READY_STATE_COMPLETE){
                 if (httpStatus==200 || httpStatus==0){
                   this.onload.call(this);
                 }else{
                   this.onerror.call(this);
                 }
               }
            },
98    ROZDZIAŁ 3
      Porządkowanie aplikacji wykorzystujących technologię Ajax

            defaultError:function(){
              alert("Błąd podczas pobierania danych!"
              +"nnreadyState:"+this.req.readyState
              +"nstatus: "+this.req.status
              +"nnagłówki: "+this.req.getAllResponseHeaders());
            }
        }


     Pierwszą rzeczą, na jaką należy zwrócić uwagę w powyższym kodzie, jest zde-
     finiowanie zmiennej globalnej net i skojarzenie z nią wszystkich pozostałych
     referencji. Dzięki temu minimalizujemy ryzyko wystąpienia konfliktów nazw
     z innymi zmiennymi i grupujemy cały kod związany z generacją i obsługą żądań
     w jednym miejscu.
         Następnie zdefiniowaliśmy funkcję konstruktora naszego obiektu . Wymaga
     ona przekazania trzech argumentów, jednak tylko dwa pierwsze są obowiązkowe.
     Trzeci argument, określający funkcję wywoływaną w przypadku wystąpienia
     błędów, jest opcjonalny — jeśli jego wartość będzie równa null, to wywołamy
     funkcję domyślną. Dla osób, które mają doświadczenie w programowaniu obiek-
     towym, możliwość przekazywania zmiennej ilości argumentów w wywołaniach
     funkcji może się wydawać dziwna, podobnie jak przekazywanie funkcji jako
     referencji. Jednak są to często stosowane możliwości języka JavaScript. Szcze-
     gółowe informacje na ich temat oraz dokładny opis samego języka można znaleźć
     w Dodatku B.
         Znaczne fragmenty kodu funkcji initXMLHttpRequest()       oraz sendRequest()
        z listingu 2.11 przenieśliśmy do metod tworzonego obiektu ContentLoader.
     Przy okazji zmieniliśmy trochę nazwy metod, by lepiej odpowiadały nieco więk-
     szemu zakresowi ich działania. Teraz kod generujący żądania znajduje się w meto-
     dzie o nazwie loadXMLDoc(). Obiekt XMLHttpRequest odnajdujemy i tworzymy
     dokładnie w taki sam sposób jak poprzednio, jednak użytkownik naszego obiektu
     ContentLoader nie musi nic na ten temat wiedzieć. Funkcja zwrotna onReadyState()
        także powinna wyglądać bardzo podobnie do swego pierwowzoru z listingu
     2.11. Wywołania generujące komunikaty na konsoli do testów zastąpiliśmy w tym
     przypadku wywołaniami funkcji onload oraz onerror. Składnia używana do
     wykonania tych wywołań może się wydawać nieco dziwna, zatem przyjrzyjmy
     się jej dokładniej. We właściwościach onload oraz onerror są zapisane obiekty
     Function języka JavaScript, a obiekty tego typu udostępniają metodę call().
     Pierwszym argumentem wywołania metody Function.call() jest obiekt, który
     stanie się kontekstem dla wywoływanej funkcji, czyli wewnątrz niej będzie się
     można odwoływać do niego przy wykorzystaniu słowa kluczowego this.
         Napisanie funkcji zwrotnej, którą przekażemy do obiektu ContentLoader,
     także nie przysporzy nam większych problemów. Jeśli w kodzie funkcji zwrotnej
     będziemy musieli odwołać się do jakiejkolwiek właściwości obiektu ContentLoader,
     to wystarczy, że jej nazwę poprzedzimy słowem kluczowym this. Np.:
        function myCallBack(){
          alert( "Plik "
            +this.url
            +" został wczytany! Oto jego zawartość:nn"
3.2.                                        Niewielkie przykłady refaktoryzacji     99



                   +this.req.responseText
              );
          }

       Utworzenie takiego kontekstu wymaga napisania dodatkowego kodu i doskonałej
       znajomości tajników JavaScriptu, jednak kiedy go już napiszemy i zastosujemy
       w obiekcie, to użytkownik końcowy nie będzie musiał o niczym wiedzieć.
           Taka sytuacja, w której użytkownik obiektu nie musi się interesować szcze-
       gółami jego implementacji, bardzo często jest sygnałem dobrze przeprowadzonej
       refaktoryzacji. Oznacza bowiem, że wszystkie złożone czynności zostały umiesz-
       czone wewnątrz obiektu, a użytkownik może się posługiwać łatwym interfejsem.
       W ten sposób jest on chroniony przed całą złożonością działania obiektu, a eks-
       pert, który go stworzył i odpowiada za jego pielęgnację, umieścił cały złożony
       kod w jednym miejscu. Wszelkie ewentualne zmiany będą musiały być wyko-
       nane tylko jeden raz i w jednym, precyzyjnie określonym miejscu, a ich efekt
       zostanie zauważony i uwzględniony w całym kodzie aplikacji.
           W ten sposób przedstawiliśmy podstawowe założenia refaktoryzacji i pokazali-
       śmy, jakie korzyści nam ona zapewnia. W następnym podrozdziale przedstawimy
       problemy, które najczęściej występują podczas pisania aplikacji wykorzystujących
       technologię Ajax, oraz jak można je rozwiązywać poprzez zastosowanie refakto-
       ryzacji. Jednocześnie zaprezentujemy kilka przydatnych sztuczek, które my
       z powodzeniem będziemy mogli wykorzystać w następnych rozdziałach książki,
       a Czytelnik — we własnych projektach.



3.2.   Niewielkie przykłady refaktoryzacji
       W tym podrozdziale zaprezentujemy kilka problemów występujących podczas
       wykorzystywania technologii Ajax oraz najczęściej stosowane sposoby ich roz-
       wiązywania. W każdym z przykładów pokażemy, jak zastosowanie refaktoryza-
       cji może nam pomóc w rozwiązaniu problemu, a następnie wskażemy elementy
       rozwiązania, które będzie można wykorzystać przy innych okazjach.
           Nawiązując do zaszczytnych tradycji literatury poświęconej wzorcom pro-
       jektowym, w pierwszej kolejności opiszemy problem, następnie przedstawimy
       jego techniczne rozwiązanie, a na końcu opiszemy je w szerszym kontekście.


3.2.1. Niezgodności przeglądarek: wzorce Façade
       oraz Adapter
       Gdyby zapytać osoby zajmujące się tworzeniem witryn i aplikacji internetowych
       — niezależnie od tego, czy są to programiści, projektanci, graficy, czy osoby
       zajmujące się każdym z tych aspektów tworzenia stron WWW — co je najbardziej
       denerwuje w ich pracy, to istnieje bardzo duże prawdopodobieństwo, iż na liście
       odpowiedzi znajdzie się konieczność zapewniania poprawnego wyglądu stron
       w różnych przeglądarkach. Na WWW istnieje wiele standardów przeróżnych
       technologii, a większość nowoczesnych przeglądarek implementuje zazwyczaj
100    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      w całości lub prawie w całości znaczną większość tych standardów. Czasami
      jednak standardy te nie są precyzyjne i zostawiają miejsce dla dowolnej inter-
      pretacji, czasami twórcy przeglądarek rozszerzają je w sposób przydatny, choć
      niespójny, a zdarza się również, że w przeglądarkach pojawiają się stare, wszech-
      obecne błędy programistyczne.
          Programiści używający języka JavaScript już od momentu jego pojawienia
      się nawykli do sprawdzania tego, jaka przeglądarka jest aktualnie używana bądź
      to testowania oraz czy konkretny obiekt jest dostępny, czy nie. Przeanalizujmy
      teraz bardzo prosty przykład.

      Operowanie na elementach DOM
      Zgodnie z informacjami podanymi w rozdziale 2. zawartość strony WWW jest
      udostępniana w kodzie JavaScript w formie obiektowego modelu dokumentu
      (ang. Document Object Model, w skrócie DOM) — struktury drzewiastej, której
      węzły odpowiadają elementom HTML umieszczonym w kodzie strony. W przy-
      padku programowego manipulowania drzewem DOM bardzo często pojawia się
      konieczność określenia położenia elementu na stronie. Niestety, twórcy przeglą-
      darek udostępnili różne, niestandardowe sposoby określania tego położenia,
      przez co napisanie jednego, ogólnego kodu wykonującego to zadanie prawidłowo
      w różnych przeglądarkach jest dosyć trudne. Listing 3.2 przedstawia uprosz-
      czoną wersję funkcji pochodzącej z biblioteki x Mike’a Fostera (patrz podroz-
      dział 3.5), która rozwiązuje problem określenia położenia wskazanego elementu
      od lewej krawędzi strony. W tym przypadku element jest przekazywany jako
      argument wywołania funkcji.

       Listing 3.2. Funkcja getLeft()
         function getLeft(e){
           if(!(e=xGetElementById(e))){
             return 0;
           }
           var css=xDef(e.style);
           if (css && xStr(e.style.left)) {
             iX=parseInt(e.style.left);
             if(isNaN(iX)) iX=0;
           }else if(css && xDef(e.style.pixelLeft)) {
             iX=e.style.pixelLeft;
           }
           return iX;
         }


      Różne przeglądarki udostępniają wiele sposobów określania położenia węzła przy
      wykorzystaniu tablicy stylów, którą przedstawiliśmy w rozdziale 2. Standard
      W3C CSS2 udostępnia właściwość style.left, zawierającą łańcuch znaków
      składający się z wartości liczbowej oraz określenia jednostki, np. 100px. Należy
      pamiętać, że mogą być stosowane inne jednostki niż piksele. Z kolei właściwość
      style.pixelLeft zawiera wartość numeryczną i to określającą przesunięcie
      elementu liczone w pikselach. Jednak właściwość ta jest dostępna wyłącznie
3.2.                                        Niewielkie przykłady refaktoryzacji        101



       w przeglądarce Internet Explorer. Przedstawiona na powyższym listingu funkcja
       getLeft() w pierwszej kolejności sprawdza, czy jest dostępny obiekt stylów CSS
       — jeśli jest, to najpierw stara się pobrać wartość właściwości style.left, zaś jeśli
       nie jest ona dostępna, to właściwości style.pixelLeft. Jeżeli nie uda się pobrać
       żadnej z tych wartości, funkcja zwraca domyślną wartość 0. Warto zauważyć, że
       metoda ta nie sprawdza nazw ani wersji używanej przeglądarki, a zamiast tego
       korzysta ze znacznie lepszego sposobu polegającego na testowaniu dostępności
       obiektów (opisaliśmy go w rozdziale 2.).
           Pisanie takich funkcji, ukrywających różnice pomiędzy poszczególnymi prze-
       glądarkami, jest zadaniem żmudnym i czasochłonnym; jednak kiedy się już z nim
       uporamy, będziemy mogli zająć się pisaniem aplikacji bez zwracania uwagi na
       te drobne, lecz niezwykle denerwujące szczegóły. A dzięki zastosowaniu dobrze
       napisanych i przetestowanych bibliotek, takich jak biblioteka x, możemy się
       uchronić przed koniecznością wykonywania większości najtrudniejszej pracy.
       Możliwość zastosowania godnych zaufania, ogólnych funkcji pozwalających na
       określanie położenia elementów DOM na stronie może w znaczny sposób przy-
       spieszyć tworzenie interfejsu użytkownika aplikacji wykorzystujących techno-
       logię Ajax.

       Przesyłanie żądania na serwer
       Już w poprzednim rozdziale zetknęliśmy się z podobnym problemem niezgod-
       ności pomiędzy różnymi przeglądarkami. Wynikał on z faktu, iż różne przeglą-
       darki udostępniają różne sposoby tworzenia obiektu XMLHttpRequest, używanego
       do asynchronicznego przesyłania żądań na serwer. Chcąc pobrać z serwera
       dokument XML, musieliśmy sprawdzić, którą z możliwości należy zastosować.
           Internet Explorer pozwoli nam na asynchroniczne pobieranie plików wyłącz-
       nie w przypadku gdy poprosimy o odpowiedni komponent ActiveX, natomiast
       przeglądarki fundacji Mozilla i Safari doskonale poradzą sobie, używając wbu-
       dowanego obiektu XMLHttpRequest. Jednak o tych rozbieżnościach „wiedział”
       jedynie kod bezpośrednio odpowiedzialny za wczytywanie dokumentów XML.
       Kiedy obiekt XMLHttpRequest został już pobrany, to dalszy sposób korzystania
       z niego był identyczny we wszystkich przeglądarkach. Jednak kod skryptu, który
       chce pobrać jakiś dokument XML z serwera, nie musi „wiedzieć” ani o obiek-
       tach ActiveX, ani o podsystemie obiektów udostępnianych przez przeglądarkę:
       wystarczy, że będzie „wiedział”, jak wywołać konstruktor net.ContentLoader().

       Wzorzec Façade
       W obu przedstawionych wcześniej funkcjach, zarówno getLeft(), jak i net.Con-
       tentLoader(), kod służący do wykrywania aktualnie używanej przeglądarki jest
       paskudny i nużący. Dzięki stworzeniu funkcji, która ukryje go przed resztą nasze-
       go kodu, nie tylko poprawimy jego czytelność, lecz także umieścimy wszystkie
       możliwości funkcjonalne związane z wykrywaniem przeglądarki w jednym,
       precyzyjnie określonym miejscu. To jedna z podstawowych zasad refaktoryzacji
       — nie należy się powtarzać. Jeśli odkryjemy jakąś szczególną sytuację, w której
102    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      nasz obiektowy kod nie działa zbyt dobrze, wystarczy wprowadzić zmiany w jed-
      nym miejscu, a zostaną one uwzględnione we wszystkich wywołaniach określają-
      cych położenie lewej krawędzi elementu DOM, tworzenia obiektu XMLHttpRequest
      bądź też jakiejkolwiek innej metody.
         W języku wzorców projektowych mówimy, iż używamy wzorca o nazwie
      Façade (fasada). Wzorzec ten służy do tworzenia wspólnego punktu dostępu
      do różnych implementacji pewnej usługi lub możliwości funkcjonalnej. Taką
      użyteczną usługę udostępnia np. obiekt XMLHttpRequest, a naszej aplikacji tak
      naprawdę nie „obchodzi”, w jaki sposób jest on tworzony — ważne, by działał
      prawidłowo (patrz rysunek 3.1).




      Rysunek 3.1. Schemat wzorca Façade oraz jego związków z obiektem
      XMLHttpRequest w przeglądarkach. Funkcja loadXML() wymaga obiektu
      XMLHttpRequest, jednak nie „interesuje” jej faktyczna implementacja tego
      obiektu. Obie faktyczne implementacje mogą udostępniać znacznie bogatsze
      możliwości obsługi obiektów żądań HTTP, jednak w tym przypadku zostają one
      ograniczone od minimalnych możliwości wymaganych przez funkcję wywołującą

      W wielu przypadkach zdarza się, że będziemy chcieli uprościć dostęp do pew-
      nego podsystemu. Chcąc np. określić położenie lewej krawędzi elementu DOM,
      możemy to zrobić na wiele różnych sposobów, a zwrócona wartość może być
      wyrażona w pikselach, punktach lub wielu innych jednostkach. Ta dowolność
      może być nam zupełnie nieprzydatna. Przedstawiona wcześniej funkcja getLeft()
      będzie działać, jeśli określając układ strony, konsekwentnie będziemy to robić
      przy wykorzystaniu pikseli. Taki sposób uproszczenia podsystemu jest kolejną
      cechą wzorca Façade.
3.2.                                      Niewielkie przykłady refaktoryzacji      103



       Wzorzec Adapter
       Wzorzec Adapter jest powiązany ze wzorcem Façade. Także w jego przypadku
       operujemy na dwóch podsystemach, które wykonują te same zadania, takich jak
       dwa różne sposoby tworzenia obiektu XMLHttpRequest używane w przeglądarkach
       Internet Explorer i Mozilla. Jednak zamiast tworzyć fasadę dla każdego z tych
       dwóch podsystemów, to dla jednego z nich tworzymy dodatkową warstwę abstrak-
       cji udostępniającą taki sam interfejs, jakim dysponuje drugi. To właśnie ta war-
       stwa stanowi wzorzec Adapter. Biblioteka Sarissa, którą przedstawimy w punk-
       cie 3.5.1, używa tego wzorca, by upodobnić komponent ActiveX stosowany
       w Internet Explorerze do wbudowanego obiektu XMLHttpRequest stosowanego
       w przeglądarkach fundacji Mozilla. Oba te wzorce projektowe są dobre i mogą
       znacząco pomóc w wykorzystaniu istniejącego już kodu w tworzonej aplikacji
       (jak widać, dotyczy to także rozwiązań udostępnianych przez przeglądarki).
           Zajmijmy się kolejnym studium, w którym zastanowimy się nad problemami
       związanymi z modelem obsługi zdarzeń stosowanym w języku JavaScript.


3.2.2. Zarządzanie procedurami obsługi zdarzeń:
       wzorzec Observer
       Trudno sobie wyobrazić pisanie aplikacji wykorzystujących technologię Ajax
       bez zastosowania technik programowania bazującego na zdarzeniach. Interfejsy
       użytkownika tworzone i obsługiwane w języku JavaScript powszechnie korzystają
       ze zdarzeń, a zastosowanie w aplikacji asynchronicznych żądań wysyłanych
       i odbieranych przy użyciu technologii Ajax powoduje, że w aplikacji pojawia się
       kolejna grupa zdarzeń oraz obsługujących je funkcji. W stosunkowo prostych
       aplikacjach zdarzenia takie, jak kliknięcie przycisku lub odebranie odpowiedzi
       przesłanej z serwera można obsługiwać przy wykorzystaniu jednej funkcji. Jednak
       wraz z powiększaniem się aplikacji zarówno pod względem ilości kodu, jak i jego
       złożoności może się pojawić konieczność, aby informacja o odebraniu jednego
       zdarzenia trafiła do kilku niezależnych podsystemów. W skrajnym przypadku
       może się nawet okazać, że konieczne jest udostępnienie mechanizmu pozwalają-
       cego zainteresowanym obiektom na rejestrowane się i otrzymywanie informacji
       o konkretnych zdarzeniach. Przyjrzyjmy się przykładowi, który zilustruje taką
       sytuację.

       Stosowanie wielu procedur obsługi zdarzeń
       Podczas programowego operowania na węzłach DOM często stosowana jest
       technika polegająca na umieszczaniu skryptu w procedurze obsługi zdarzeń
       onload (wykonywanej po załadowaniu całej strony, a co za tym idzie, po zakoń-
       czeniu tworzenia drzewa DOM). Załóżmy, że na naszej stronie jest umieszczony
       element, w którym chcemy dynamicznie wyświetlać dane, jakie po załadowaniu
       strony będą cyklicznie pobierane z serwera. Kod JavaScript koordynujący pobie-
       ranie danych oraz ich wyświetlanie musi odwoływać się do węzła DOM, a zatem
       wszystkie operacje zaczynają się w procedurze obsługi zdarzeń onload:
104    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax

         window.onload=function() {
           displayDiv=document.getElementById('display');
         }

      Jak na razie wszystko idzie doskonale. A teraz załóżmy, że chcemy umieścić na
      stronie drugi obszar, prezentujący komunikaty pochodzące z kanałów RSS (jeśli
      Czytelnik jest zainteresowany mechanizmem implementującym takie możliwości,
      to może go znaleźć w rozdziale 13.). Kod aktualizujący prezentowane powia-
      domienia, także podczas uruchamiania strony, musi pobrać referencję do węzła
      DOM. Zatem także dla niego definiujemy procedurę obsługi zdarzeń onload:
         window.onload=function() {
           feedDiv=document.getElementById('feeds');
         }

      Przetestowaliśmy oba te rozwiązania na osobnych stronach i okazało się, że dzia-
      łają doskonale. A zatem w ramach kolejnego etapu prac połączyliśmy je i wtedy
      okazało się, że druga procedura obsługi zdarzeń onload przesłoniła pierwszą,
      dane nie wyświetlają się na stronie, a co gorsza zamiast nich na stronie pojawia
      się cała masa komunikatów o błędach JavaScriptu. W tym przypadku cały pro-
      blem polegał na tym, iż obiekt window pozwala na określenie tylko jednej pro-
      cedury obsługi zdarzeń onload.

      Ograniczenia złożonej procedury obsługi zdarzeń
      Jak już napisaliśmy, w powyższym przykładzie druga procedura obsługi zdarzeń
      onload przesłania pierwszą. Problem ten możemy w prosty sposób rozwiązać
      poprzez napisanie jednej, złożonej funkcji:
         window.onload=function() {
           displayDiv=document.getElementById('display');
           feedDiv=document.getElementById('feeds');
         }

      W naszym przykładzie takie rozwiązanie zda egzamin, choć ma tę wadę, iż łączy
      w sobie kod związany z prezentacją danych i pobieraniem zawartości kanałów
      RSS, które nie są ze sobą w żaden sposób powiązane. Jeśli w tej aplikacji będą
      stosowane nie 2, lecz 20 takich niezależnych podsystemów, i każdy będzie mu-
      siał pobrać referencję do jakiegoś węzła DOM, to kod takiej złożonej procedury
      obsługi zdarzeń stanie się trudny do pielęgnacji. Poza tym takie rozwiązanie
      znacznie utrudniłoby dodawanie do aplikacji i usuwanie z niej komponentów
      oraz sprawiłoby, że takie modyfikacje zostaną obciążone dodatkowym ryzykiem
      wystąpienia błędów. Innymi słowy, w większej aplikacji zastosowanie takiej
      złożonej procedury obsługi zdarzeń mogłoby doprowadzić do opisanej wcześniej
      sytuacji, w której nikt nie chciałby zmieniać kodu, obawiając się, iż przestanie
      on działać. Spróbujmy zatem nieco zmodyfikować nasz kod i zdefiniować osobne
      funkcje inicjujące dla każdego z podsystemów:
         window.onload=function(){
           getDisplayElements();
           getFeedElements();
         }
3.2.                                       Niewielkie przykłady refaktoryzacji        105



          function getDisplayElements(){
            displayDiv=document.getElementById('display');
          }
          function getFeedElements(){
            feedDiv=document.getElementById('feeds');
          }

       Jak widać, udało nam się nieco poprawić przejrzystość i czytelność kodu oraz
       zredukować objętość funkcji window.onload do jednego wiersza kodu dla każdego
       podsystemu. Niemniej jednak ta złożona funkcja wciąż jest słabym punktem
       całego projektu i potencjalną przyczyną problemów. W następnym podpunkcie
       przedstawimy nieco bardziej złożone, lecz jednocześnie bardziej skalowalne
       rozwiązanie.

       Wzorzec Observer
       Czasami warto się zastanowić, na kim spoczywa odpowiedzialność za wykonanie
       pewnej akcji. W przypadku przedstawionej wcześniej funkcji złożonej odpowie-
       dzialność za pobranie referencji do węzłów DOM spoczywała na obiekcie window,
       co oznacza, że musi on „wiedzieć”, które z nich będą używane na danej stronie.
       W idealnym przypadku każdy z podsystemów sam powinien być odpowiedzialny
       za pobranie potrzebnej referencji. Dzięki temu jeśli podsystem zostanie użyty
       na stronie, to pobierze referencję, w przeciwnym razie żadne operacje inicjaliza-
       cyjne tego modułu nie zostaną wykonane.
           Aby jasno i precyzyjnie określić obowiązki poszczególnych podsystemów,
       możemy zapewnić im możliwość rejestrowania się i uzyskiwania powiadomień
       o zgłoszeniu zdarzenia load. W tym celu podsystemy będą przekazywały funk-
       cje zwrotne, które należy wywołać w procedurze obsługi zdarzeń onload. Poniżej
       przedstawiliśmy prostą implementację takiego rozwiązania:
          window.onloadListeners=new Array();
          window.addOnLoadListener(listener){
            window.onloadListeners[window.onloadListeners.length]=listener;
          }

       Po wczytaniu całej zawartości strony, podczas obsługi zdarzenia load, musimy
       jedynie przejrzeć całą zawartość tablicy window.onloadListeners i wywołać każdą
       z zapisanych w niej funkcji:
          window.onload=function(){
            for(var i=0;i<window.onloadListeners.length;i++){
              var func=window.onlloadListeners[i];
              func.call();
            }
          }

       Jeśli założymy, że każdy podsystem wykorzysta to rozwiązanie, będziemy mogli
       zaoferować znacznie bardziej przejrzysty sposób ich inicjalizacji i to bez koniecz-
       ności niepotrzebnego kojarzenia wszystkich podsystemów ze sobą w jednym
       fragmencie kodu. Oczywiście, wystarczy tylko jeden nieostrożny wiersz kodu, by
       jawnie przesłonić naszą funkcję window.onload i sprawić, że całe rozwiązanie
106    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      przestanie działać. Jednak w jakimś momencie będziemy musieli zatroszczyć
      się o nasz kod i zadbać o to, by takie błędy się w nim nie pojawiały.
          Przy tej okazji warto zwrócić uwagę Czytelnika na fakt, iż nowy model obsługi
      zdarzeń W3C także implementuje podobne rozwiązanie, w którym w jednym
      obiekcie można rejestrować wiele funkcji obsługujących to samo zdarzenie.
      Zdecydowaliśmy się jednak na zaimplementowanie własnego rozwiązania ba-
      zującego na starym modelu obsługi zdarzeń języka JavaScript, gdyż nowy model
      W3C nie jest spójnie obsługiwany we wszystkich przeglądarkach. Zagadnieniem
      tym zajmiemy się bardziej szczegółowo w rozdziale 4.
          Wzorzec projektowy, który zastosowaliśmy, tworząc powyższe rozwiązanie,
      nosi nazwę Observer (obserwator). Definiuje on obiekt „obserwowany” (w naszym
      przypadku jest to wbudowany obiekt window przeglądarki) oraz grupę obiektów
      „obserwatorów” lub „słuchaczy”, które mogą się w nim rejestrować (patrz rysu-
      nek 3.2).




      Rysunek 3.2. Rozdzielenie obowiązków we wzorcu projektowym Observer.
      Obiekty, które „chcą” być informowane o zdarzeniach, czyli obserwatory,
      mogą się rejestrować i usuwać ze źródła zdarzeń, które z kolei będzie
      przekazywać im stosowne informacje w momencie zajścia zdarzenia

      W tym wzorcu obowiązki są prawidłowo rozdzielone pomiędzy źródło zdarzeń
      oraz procedurę obsługi tego zdarzenia. Funkcje obsługujące zdarzenie same
      powinny się zarejestrować oraz, ewentualnie, poinformować obiekt obserwo-
      wany, że nie są już zainteresowane generowanymi przez niego zdarzeniami.
      Obiekt stanowiący źródło zdarzeń odpowiada za przechowywanie listy wszyst-
      kich „zainteresowanych” funkcji oraz generowanie powiadomień w przypadku
      zgłoszenia zdarzenia. Wzorzec Observer od dawna jest stosowany podczas pro-
      gramowej obsługi interfejsów użytkownika sterowanych zdarzeniami i wrócimy
      do niego w rozdziale 4., przy okazji szczegółowej analizy obsługi zdarzeń w ję-
      zyku JavaScript. Jak się okaże, wzorzec ten z powodzeniem można zastosować
      we własnych obiektach, które nie mają nic wspólnego z obsługą zdarzeń gene-
      rowanych przez klawiaturę lub mysz.
         Teraz zajmiemy się kolejnym często występującym problemem, który mo-
      żemy rozwiązać przy użyciu refaktoryzacji.
3.2.                                      Niewielkie przykłady refaktoryzacji      107



3.2.3. Wielokrotne stosowanie funkcji obsługujących
       działania użytkownika: wzorzec Command
       Być może takie stwierdzenie będzie dla Czytelnika oczywiste, jednak w więk-
       szości przypadków, to użytkownik „informuje” aplikację (przy wykorzystaniu
       klawiatury lub myszy) o tym, co należy zrobić, a ta wykonuje odpowiednie czynno-
       ści. W prostych programach możemy udostępnić użytkownikowi tylko jedną
       możliwość wykonania pewnej czynności, jednak w przypadku tworzenia bardziej
       złożonych interfejsów możemy chcieć, aby mógł on doprowadzić do wykonania
       tej samej operacji na kilka różnych sposobów.

       Implementacja kontrolki przycisku
       Załóżmy, że na naszej stronie znajduje się element DOM, któremu, przy użyciu
       stylów CSS, nadaliśmy wygląd przypominający przycisk. Kliknięcie tego przyci-
       sku powoduje wykonanie pewnych obliczeń i wyświetlenie wyników w tabeli
       HTML. Procedurę obsługującą zdarzenia kliknięcia takiego przycisku mogli-
       byśmy zdefiniować w następujący sposób:
          function buttonOnclickHandler(event){
            var data=new Array();
            data[0]=6;
            data[1]=data[0]/3;
            data[2]=data[0]*data[1]+7;
            var newRow=createTableRow(dataTable);
            for (var i=0;i<data.length;i++){
              createTableCell(newRow,data[i]);
            }
          }

       Zakładamy przy tym, że zmienna dataTable zawiera referencję do faktycznie
       istniejącej tabeli, a funkcje createTabelRow() oraz createTableCell() potrafią
       prawidłowo operować na węzłach DOM. Najciekawszym fragmentem powyż-
       szego kodu jest fragment odpowiadający za wykonanie obliczeń; w rzeczywistej
       aplikacji może on mieć długość kilkuset wierszy kodu. Powyższą funkcję koja-
       rzymy z przyciskiem w następujący sposób:
          buttonDiv.onclick=buttonOnclickHandler;

       Obsługa zdarzeń wielu różnych typów
       Załóżmy teraz, że rozbudowaliśmy naszą aplikację o możliwości wykorzysty-
       wania technologii Ajax. Pobieramy z serwera aktualizacje danych i chcemy wy-
       konywać powyższe obliczenia jedynie w przypadku gdy pewna wartość została
       zmodyfikowana, a oprócz tego chcemy aktualizować inną tabelę danych. Tym
       razem nie będziemy się zajmowali konfiguracją cyklicznego przesyłania żądań
       na serwer. Załóżmy, że dysponujemy obiektem, który zapewnia odpowiednie
       możliwości. Używa on obiektu XMLHttpRequest i konfiguruje go w taki sposób,
       by po odebraniu odpowiedzi z serwera wywoływana była funkcja onload().
       Możemy wydzielić fragmenty kodu związane z dokonywaniem odpowiednich
108    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      obliczeń oraz z aktualizacją strony i umieścić je w dwóch niezależnych funkcjach
      przedstawionych poniżej:
         function buttonOnclickHandler(event){
           var data=calculate();
           showData(dataTable,data);
         }
         function ajaxOnloadHandler(){
           var data=calculate();
           showData(otherDataTable,data);
         }
         function calculate(){
           var data=new Array();
           data[0]=6;
           data[1]=data[0]/3;
           data[2]=data[0]*data[1]+7;
           return data;
         }
         function showData(table,data){
           var newRow=createTableRow(table);
           for (var i=0;i<data.length;i++){
             createTableCell(newRow,data[i]);
           }
         }
         buttonDiv.onclick=buttonOnclickHandler;
         poller.onload=ajaxOnloadHandler;

      Cały wielokrotnie używany kod został wydzielony i umieszczony w funkcjach
      calculate() oraz showData(); dzięki temu znacznie ograniczyliśmy powielanie
      się fragmentów kodu w całej aplikacji (aktualnie powtarzają się w niej jedynie dwa
      wiersze, używane w funkcjach buttonOnclickHandler() oraz ajaxOnloadHan-
      dler()).
          Dzięki zastosowanemu rozwiązaniu znacznie lepiej rozdzieliliśmy kod odpo-
      wiedzialny za realizację logiki biznesowej oraz za aktualizację interfejsu użyt-
      kownika. Także w tym przypadku udało nam się określić przydatne rozwiązanie
      nadające się do wielokrotnego wykorzystania. Wzorzec projektowy Command
      definiuje pewną czynność o dowolnym stopniu złożoności, którą bez trudu będzie
      można przekazywać i używać w różnych miejscach kodu oraz w różnych ele-
      mentach interfejsu użytkownika. W klasycznym wzorcu Command stosowanym
      w obiektowych językach programowania wszystkie czynności wykonywane przez
      użytkownika zostają umieszczone w obiektach Command, które zazwyczaj dzie-
      dziczą do pewnej wspólnej klasy bazowej (może to także być interfejs bazowy).
      W przedstawionym przykładzie ten sam problem rozwiązaliśmy w nieco inny
      sposób. Ponieważ w języku JavaScript funkcje są obiektami, same funkcje mo-
      żemy potraktować jako obiekty Command. Jednak nawet takie nieco uprosz-
      czone rozwiązanie gwarantuje nam ten sam poziom abstrakcji.
          Choć obsługa wszystkich czynności wykonywanych przez użytkownika
      w obiektach Command może się wydawać rozwiązaniem nieco dziwnym i niepo-
      ręcznym, to jednak posiada ono konkretne zalety. Kiedy np. wszystkie czynności
      wykonywane przez użytkownika zastaną zaimplementowane w formie obiektów
      Command, bardzo łatwo będziemy mogli z nimi skojarzyć inne, standardowe
3.2.                                      Niewielkie przykłady refaktoryzacji     109



       możliwości funkcjonalne. Jednym z najczęściej opisywanych rozszerzeń tego
       typu jest dodanie metody undo(). W ten sposób można stworzyć zalążek ogólnego
       mechanizmu o zasięgu całej aplikacji, pozwalającego na odtwarzanie skutków
       wcześniej wykonanych czynności. Można też sobie wyobrazić bardziej złożone
       rozwiązanie, w którym każdy wykonywany obiekt Command będzie także zapi-
       sywany na stosie, a użytkownik, klikając odpowiedni przycisk, będzie mógł zdej-
       mować te obiekty ze stosu, przywracając tym samym aplikację do wcześniejszego
       stanu (patrz rysunek 3.3).




       Rysunek 3.3. Zastosowanie wzorca Command do utworzenia ogólnego stosu
       poleceń w aplikacji edytora tekstów. Wszystkie czynności wykonywane przez
       użytkownika są reprezentowane jako obiekty Command, dzięki temu czynności
       te można nie tylko wykonywać, lecz także odtwarzać
           Każde nowe polecenie jest umieszczane na wierzchołku stosu, a następnie
       można je kolejno z niego zdejmować. Wyobraźmy sobie sytuację, w której użyt-
       kownik tworzy dokument, wykonując sekwencję akcji zapisu tekstu, a następnie,
       przypadkowo, zaznacza całą zawartość dokumentu i usuwa ją. W takim przypadku,
       jeśli kliknie on przycisk Cofnij, zdejmie ze stosu ostatni umieszczony na nim
       obiekt Command i wywoła jego metodę undo(), co sprawi, że usunięty tekst
       znów pojawi się w dokumencie. Dalsze kliknięcia przycisku będą powodowały
       usuwanie z dokumentu kolejnych fragmentów tekstu.
110    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


          Oczywiście, z punktu widzenia programisty, zastosowanie obiektów Com-
      mand do zaimplementowania stosu operacji, które można odtworzyć, oznacza
      konieczność wykonania dodatkowej pracy. Jest ona związana z zapewnieniem,
      by kombinacja wykonania pewnej czynności oraz jej odtworzenia spowodowała
      przywrócenie systemu do początkowego stanu. Niemniej jednak udostępnienie
      takiego mechanizmu może być ważnym czynnikiem świadczącym o jakości i moż-
      liwościach produktu. W szczególności dotyczy to aplikacji, które są używane
      długo i intensywnie, a zgodnie z informacjami podanymi w rozdziale 1. są to
      dwie podstawowe cechy aplikacji wykorzystujących technologię Ajax.
          Obiekty Command można także zastosować w sytuacjach, gdy konieczne
      jest przenoszenie danych przez granice rozdzielające różne podsystemy aplikacji.
      Oczywiście jedną z takich granic jest sam internet, zatem wrócimy do wzorca
      projektowego Command w rozdziale 5., poświęconym interakcjom pomiędzy
      klientem i serwerem.


3.2.4. Przechowywanie tylko jednej referencji do zasobu:
       wzorzec Singleton
      W niektórych sytuacjach bardzo duże znaczenie ma zapewnienie, by w aplikacji
      istniał tylko jeden punkt kontaktu z konkretnym zasobem. Także to zagadnienie
      najlepiej będzie przedstawić na konkretnym przykładzie.

      Prosty przykład handlowy
      Załóżmy, że nasza aplikacja używająca technologii Ajax operuje na danych
      giełdowych, pozwalając nam na przeprowadzanie faktycznych operacji na pa-
      pierach wartościowych, wykonywanie różnych obliczeń typu „co się stanie, jeśli”
      oraz uczestniczenie w internetowych grach symulacyjnych z innymi użytkow-
      nikami. Nasza aplikacja będzie mogła działać w trzech trybach, których nazwy
      odpowiadają kolorom świateł sygnalizacji ulicznej. I tak, tryb czasu rzeczywi-
      stego (tryb zielony) pozwala na wykonywanie faktycznych operacji giełdowych,
      czyli sprzedawanie i kupowanie papierów wartościowych w czasie gdy giełdy są
      otwarte oraz przeprowadzenie różnego typu obliczeń na zgromadzonych danych.
      Kiedy rynki zostaną zamknięte, aplikacja zaczyna działać w trybie analiz (trybie
      czerwonym), w którym wciąż pozwala na przeprowadzanie obliczeń, lecz nie
      daje możliwości handlowania papierami wartościowymi. Trzecim dostępnym
      trybem jest tryb symulacji (czyli żółty); możemy w nim realizować wszystkie
      czynności dostępne w trybie zielonym, lecz w tym przypadku nie są one wykony-
      wane na faktycznych danych giełdowych, lecz na zestawie danych testowych.
          Kod wykonywany w przeglądarce rozróżnia te wszystkie tryby przy wyko-
      rzystaniu obiektu JavaScript przedstawionego na poniższym przykładzie:
         var MODE_RED=1;
         var MODE_AMBER=2;
         var MODE_GREEN=2;
         function TradingMode(){
           this.mode=MODE_RED;
         }
3.2.                                      Niewielkie przykłady refaktoryzacji       111



       Możemy zarówno sprawdzać, jak i ustawiać tryb stosowany w tym obiekcie,
       a kod naszej aplikacji robi to w wielu miejscach. Moglibyśmy zdefiniować takie
       metody, jak getMode() oraz setMode(), które sprawdzałyby wiele różnych wa-
       runków, takich jak np. czy giełdy są w danej chwili otwarte itd., jak na razie
       jednak nie będziemy sobie komplikować życia.
           Załóżmy, że użytkownicy mogą korzystać z dwóch możliwości aplikacji:
       sprzedawania i kupowania papierów wartościowych oraz wyliczania, przed wyko-
       naniem operacji, potencjalnych zysków i strat, jakie ona wygeneruje. W zależności
       od trybu działania aplikacji akcje związane z kupowaniem i sprzedawaniem
       papierów wartościowych będą odwoływać się do różnych usług sieciowych
       — wewnętrznych — w trybie żółtym, usług dostępnych na serwerze naszego
       brokera — w trybie zielonym; natomiast w trybie czerwonym operacje te będą
       niedostępne. Podobnie analizy będę wykonywane w oparciu o pobierane z ser-
       wera dane o wcześniejszych i aktualnych cenach akcji, przy czym w trybie
       zielonym będą to faktyczne dane giełdowe, a w trybie żółtym — generowane
       przez nasz system dane testowe. Aby wiedzieć, z jakich źródeł danych skorzystać,
       aplikacja będzie korzystać z obiektu TradingMode (patrz rysunek 3.4).




       Rysunek 3.4. W naszej przykładowej aplikacji do handlu akcjami, wykorzystującej
       technologię Ajax, zarówno funkcje obsługujące operacje kupna i sprzedaży,
       jak i funkcje analityczne określają, czy należy używać rzeczywistej bazy danych,
       czy też zestawu danych testowych. Wybór zestawu danych odbywa się na podstawie
       wartości statusu obiektu TradingMode. Dane testowe używane są w trybie żółtym
       oraz odnoszą się do serwera z rzeczywistymi danymi w trybie zielonym.
       Jeśli w systemie pojawiłby się więcej niż jeden obiekt TradingMode, to mógłby
       on stracić stabilność
112    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      Kluczowe znaczenie dla poprawnego działania aplikacji ma zapewnienie, by obie
      akcje korzystały z tego samego obiektu TradingMode. Jeśli użytkownik będzie
      handlować akacjami w grze symulacyjnej, podejmując decyzje na podstawie
      faktycznych kursów papierów wartościowych, to można przypuszczać, że przegra.
      Jeśli natomiast użytkownik będzie handlować prawdziwymi akcjami na podstawie
      analiz i symulacji wykonywanych na danych testowych, to zapewne szybko straci
      pracę!
          Obiekt, którego w całej aplikacji można utworzyć tylko jeden egzemplarz,
      nazywamy często singletonem. W dalszej części rozdziału w pierwszej kolejno-
      ści zobaczymy, jak singletony są tworzone w językach obiektowych, a następnie
      opracujemy sposób ich tworzenia w języku JavaScript.

      Singletony w Javie
      W językach obiektowych takich jak Java w implementacjach singletonów zazwy-
      czaj ukrywamy konstruktor, a sam obiekt można pobierać przy użyciu odpowied-
      niej metody pobierającej. Implementację takiego singletonu przedstawiliśmy
      na listingu 3.3.

       Listing 3.3. Obiekt TradingMode jako singleton zaimplementowany
       w Javie
         public class TradingMode{
           private static TradingMode instance=null;
           public int mode;
           private TradingMode(){
             mode=MODE_RED;
           }
           public static TradingMode getInstance(){
             if (instance==null){
               instance=new TradingMode();
             }
             return instance;
           }
           public void setMode(int mode){
             ...
           }
         }


      Jak widać, powyższe rozwiązanie napisane w języku Java wymusza odpowiednie
      działanie singletonu poprzez zastosowanie modyfikatorów dostępu: public oraz
      private. Jeśli spróbujemy użyć poniższego fragmentu kodu:
         new TradingMode().setMode(MODE_AMBER);

      to okaże się, iż nie można go skompilować, gdyż konstruktor obiektu nie jest
      publicznie dostępny. Nasz obiekt da się natomiast pobrać przy użyciu poniższe-
      go wywołania:
         TradingMode.getInstance().setMode(MODE_AMBER);
3.2.                                        Niewielkie przykłady refaktoryzacji    113



       Powyższy kod zapewnia, że wszystkie odwołania trafią do tego samego egzem-
       plarza obiektu TradingMode. Jednak w powyższym rozwiązaniu zastosowaliśmy
       kilka możliwości Javy, które nie są dostępne w języku JavaScript. Przekonajmy
       się zatem, w jaki sposób możemy rozwiązać te problemy.

       Singletony w języku JavaScript
       Język JavaScript nie udostępnia modyfikatorów dostępu, niemniej jednak mo-
       żemy „ukryć” konstruktor obiektu — wystarczy w ogóle go nie implementować.
       JavaScript jest językiem wykorzystującym prototypy, w którym konstruktory
       są zwyczajnymi obiektami Function (jeśli Czytelnik nie wie, co to oznacza, to
       wszystkie niezbędne informacje można znaleźć w Dodatku B.). Moglibyśmy
       zaimplementować obiekt TradingMode w konwencjonalny sposób:
          function TradingMode(){
            this.mode=MODE_RED;
          }
          TradingMode.prototype.setMode=function(){
          }

       i udostępnić zmienną globalną „udającą” singleton:
          TradingMode.instance = new TradingMode();

       Jednak takie rozwiązanie nie uchroniłoby nas przed możliwością wywołania
       konstruktora. Z drugiej strony, cały obiekt możemy także stworzyć ręcznie, bez
       stosowania prototypu:
          var TradingMode=new Object();
          TradingMode.mode=MODE_RED;
          TradingMode.setMode=function(){
            ...
          }

       Ten sam efekt możemy uzyskać, stosując nieco bardziej zwartą formę zapisu:
          var TradingMode={
            mode:MODE_RED,
            setMode:function(){
              ...
            }
          };

       W obu powyższych przypadkach wygenerowany obiekt będzie identyczny.
       Pierwsze rozwiązanie będzie zapewne bardziej zrozumiałe dla osób znających
       języki Java lub C#, natomiast drugi sposób tworzenia obiektów przedstawiliśmy
       we wcześniejszej części książki, gdyż jest on często stosowany w bibliotece Pro-
       totype oraz we wszystkich frameworkach, które z niej korzystają.
           Powyższe rozwiązanie działa dobrze w zakresie jednego kontekstu skryp-
       towego. Jeśli jednak skrypt zostanie wyczytany do osobnego elementu <iframe>,
       to spowoduje to utworzenie drugiego egzemplarza singletonu. Problem ten
       można jednak rozwiązać, jawnie żądając, by obiekt singletonu był pobierany
       z dokumentu głównego (w języku JavaScript odwołanie do tego dokumentu
       można pobrać przy wykorzystaniu właściwości top). Takie rozwiązanie przed-
       stawiliśmy na listingu 3.4.
114     ROZDZIAŁ 3
        Porządkowanie aplikacji wykorzystujących technologię Ajax


        Listing 3.4. Obiekt TradingMode jako singleton zaimplementowany
        w języku JavaScript
          Function getTradingMode(){
            if (!top.TradingMode){
              top.TradingMode=new Object();
              top.TradingMode.mode=MODE_RED;
              top.TradingMode.setMode=function(){
                ...
              }
            }
            return top.TradingMode;
          }


       Takie rozwiązanie sprawia, że skrypt będzie działał poprawnie nawet w przy-
       padku dołączania go w różnych elementach <iframe>, a jednocześnie zapewnia
       unikalność obiektu singletonu. (Jeśli Czytelnik planuje obsługę singletonów
       w aplikacji, w której może być kilka okien głównych, to konieczne będzie spraw-
       dzanie właściwości top.opener. Jednak ze względu na ograniczoną objętość
       niniejszej książki nie możemy szczegółowo opisać tych zagadnień i zostawiamy
       je jako ćwiczenie do samodzielnego wykonania przez Czytelnika).
           Zazwyczaj obsługa interfejsu użytkownika nie będzie wymagała od nas sto-
       sowania singletonów, niemniej jednak mogą one być niezwykle przydatne pod-
       czas modelowania procesów biznesowych w języku JavaScript. W tradycyjnych
       aplikacjach internetowych logika biznesowa jest modelowana i wykonywana
       wyłącznie na serwerze; jednak w aplikacjach wykorzystujących technologię Ajax
       wszystko się zmienia, więc warto pamiętać o czymś takim jak singletony.
           Prezentacją wzorca projektowego Singleton kończymy część rozdziału, której
       zadaniem było bardzo pobieżne przedstawienie praktycznych możliwości, jakie
       zapewnia nam refaktoryzacja. Oczywiście należy pamiętać, że wszystkie zapre-
       zentowane przykłady były stosunkowo proste; jednak pomimo to zastosowanie
       refaktoryzacji do poprawienia przejrzystości kodu pomogło nam wyeliminować
       kilka jego słabych punktów, które w przyszłości, po powiększeniu się aplikacji,
       mogłyby jeszcze bardzo dać się nam we znaki.
           Jednocześnie przedstawiliśmy kilka wzorców projektowych. W następnym
       podrozdziale zaprezentujemy duży i popularny wzorzec projektowy używany
       w aplikacjach wykonywanych po stronie serwera i przyjrzymy się, jak za jego
       pomocą możemy poprawić przejrzystość i elastyczność początkowo bardzo
       skomplikowanego kodu.



3.3.   Model-View-Controller
       Niewielkie wzorce projektowe, które przedstawiliśmy w poprzednim podrozdziale,
       mogą być przydatne podczas realizacji konkretnych zadań programistycznych.
       Jednak istnieją także wzorce opracowywane z myślą o określaniu sposobu organi-
       zacji całych aplikacji. Czasami określa się je jako wzorce architektoniczne. W tym
       podrozdziale przeanalizujemy jeden z takich wzorców architektonicznych, który
3.3.                                                   Model-View-Controller          115



       pomoże nam uporządkować kod aplikacji wykorzystującej technologię Ajax
       i sprawi, że zarówno jej tworzenie, jak i późniejsza pielęgnacja, będą łatwiejsze.
           Wzorzec Model-View-Controller (w skrócie: MVC, Model-Widok-Kontroler)
       jest doskonałym sposobem opisu wzajemnej separacji fragmentów programu
       odpowiedzialnych za obsługę interakcji z użytkownikiem oraz za wykonywanie
       różnych poważnych, „biznesowych” operacji.
           Wzorze MVC jest zazwyczaj stosowany w dużej skali — obejmuje całe war-
       stwy aplikacji, a czasami nawet kilka warstw. W tym podrozdziale opiszemy go
       i pokażemy, w jaki sposób można go zastosować do zaimplementowania serwe-
       rowej części aplikacji, która będzie zwracać do niej dane w odpowiedzi na żądania
       przesyłane asynchronicznie przy użyciu technologii Ajax. W rozdziale 4. zajmiemy
       się nieco trudniejszym zagadnieniem, jakim jest zastosowanie wzorca MVC
       w kodzie pisanym w języku JavaScript i wykonywanym po stronie przeglądarki.
           Wzorzec MVC określa trzy role, jakie mogą pełnić komponenty programu.
       Model jest reprezentacją dziedziny problemu, czyli tym, czym ma się zajmować
       aplikacja. Np. w edytorze tekstów modelem będzie dokument, a w elektro-
       nicznej mapie — punkty na siatce, linie itd.
           Drugim elementem wzorca MVC jest widok. Jest to ta część programu, która
       odpowiada za prezentację — to ona wyświetla na ekranie elementy formularzy,
       obrazki, teksty oraz wszelkie inne kontrolki. Nie należy jednak sądzić, że widoki
       mogą mieć jedynie charakter graficzny. Np. w aplikacjach sterowanych głosem
       widokiem będą komunikaty dźwiękowe generowane przez aplikację.
           Jedna z podstawowych zasad wzorca MVC zaleca, iż model i widok nie po-
       winny się „komunikować” ze sobą w sposób bezpośredni. Łatwo zauważyć, iż
       programy składające się wyłącznie z modelu i widoku byłby raczej mało przy-
       datne, dlatego też całość rozwiązania dopełnia trzeci element wzorca MVC
       — kontroler. Kiedy użytkownik kliknie przycisk lub wypełni pola formularza,
       widok poinformuje o tym fakcie kontroler. Kontroler wykona stosowne operacje
       na modelu i określi, czy zmiany, jakie zostały wprowadzone w modelu, wyma-
       gają aktualizacji widoku. Jeśli taka aktualizacja okaże się konieczna, to kontroler
       wydaje odpowiednie polecenie widokowi (patrz rysunek 3.5).




       Rysunek 3.5. Podstawowe komponenty wzorca Model-View-Controller.
       Model oraz widok nie wymieniają informacji pomiędzy sobą w sposób bezpośredni,
       lecz za pośrednictwem kontrolera. Kontroler można sobie wyobrazić jako cienką
       warstwę pozwalającą na wymianę danych pomiędzy modelem i widokiem,
       która jednocześnie wymusza precyzyjne rozdzielenie obowiązków pomiędzy
       poszczególnymi fragmentami kodu, poprawiając w ten sposób jego elastyczność
       i ułatwiając utrzymanie
116    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      Zaleta tego rozwiązania polega na tym, iż widok i model są ze sobą powiązane
      w bardziej luźny sposób, czyli żaden z tych elementów aplikacji nie musi „wie-
      dzieć” zbyt wiele na temat drugiego. Oczywiście, powinny one „wiedzieć” na
      tyle dużo, by mogły realizować swoje zadania, niemniej jednak muszą dyspo-
      nować jedynie bardzo ogólną znajomością modelu.
          Przeanalizujmy przykład programu do zarządzania stanem magazynu. W takim
      programie kontroler może udostępniać widokowi funkcję zwracającą listę wszyst-
      kich linii produktów należących do kategorii o podanym identyfikatorze; jednak
      sam widok nie „wie”, w jaki sposób ta lista jest tworzona. Może się okazać, że
      w pierwszej wersji programu dane niezbędne do wygenerowania takiej listy są
      gromadzone w pamięci lub odczytywane z pliku. Z kolei w drugiej wersji pro-
      gramu pojawił się wymóg obsługi bardzo dużych zbiorów danych, w związku
      z tym do architektury systemu został dodany serwer relacyjnej bazy danych.
      Z punktu widzenia modelu zmiany, jakich trzeba dokonać w celu obsługi nowego
      źródła danych, są bardzo duże. Niemniej jednak, jeśli założymy, że kontroler
      wciąż będzie przekazywał widokowi listę linii produktów spełniającą zadane kryte-
      ria, to okaże się, że w kodzie widoku nie trzeba wprowadzać żadnych zmian.
          Podobnie projektanci pracujący nad widokiem powinni być w stanie popra-
      wiać i rozszerzać jego możliwości funkcjonalne bez obawy, że ich zmiany spo-
      wodują wystąpienia jakichś problemów w modelu, oczywiście o ile tylko będą
      stosowali prosty interfejs, jaki udostępnia im kontroler. A zatem, dzieląc cały
      system na trzy elementy, model MVC jest swoistą „polisą ubezpieczeniową”,
      która zabezpiecza nas przed sytuacją, gdy jakaś drobna zmiana zmusi nas do
      wprowadzania wielu innych zmian w całym kodzie aplikacji. Oprócz tego wzorzec
      ten zapewnia możliwość szybkiego i sprawnego działania zespołów pracujących
      nad poszczególnymi elementami aplikacji i minimalizuje niebezpieczeństwo,
      że będą one sobie wzajemnie przeszkadzać.
          Pewien szczególny sposób wykorzystania wzorca MVC jest bardzo często
      stosowany we frameworkach obsługujących klasyczne aplikacje internetowe.
      Otóż frameworki te generują statyczne strony WWW stanowiące interfejs użyt-
      kownika. W aplikacjach wykorzystujących technologię Ajax, działających i przesy-
      łających żądania na serwer, proces zwracania danych przypomina nieco działanie
      klasycznych aplikacji internetowych. Oznacza to, że także aplikacje wykorzy-
      stujące technologię Ajax mogą skorzystać na zastosowaniu tej specyficznej wersji
      wzorca MVC. Ponieważ wzorzec ten jest powszechnie znany, nasze rozważania
      zaczniemy właśnie do niego, by później przejść do innych jego zastosowań,
      bardziej charakterystycznych dla aplikacji wykorzystujących technologię Ajax.
          Jeśli Czytelnik nie zetknął się jeszcze z frameworkami używanymi do two-
      rzenia aplikacji internetowych, to w tej części rozdziału może znaleźć informacje,
      które pomogą mu zrozumieć, dlaczego narzędzia te poprawiają solidność i ska-
      lowalność aplikacji. Jeśli jednak Czytelnik zna narzędzia i technologie używane
      do tworzenia wielowarstwowych aplikacji internetowych, takie jak mechanizmy
      obsługi szablonów, mechanizmy odwzorowań obiektowo-relacyjnych (określane
      skrótowo jako mechanizm ORM), czy też frameworki, takie jak Struts, Spring lub
      Tapestry, to zapewne będzie doskonale wiedział, o czym będziemy tu mówić.
3.4.                                    Wzorzec MVC stosowany na serwerze          117



       W takim przypadku Czytelnik może pominąć dalszą część rozdziału i kontynu-
       ować lekturę od rozdziału 4., w którym zajmiemy się całkowicie innymi sposo-
       bami wykorzystania wzorca MVC.



3.4.   Wzorzec MVC stosowany na serwerze
       Wzorzec MVC jest od dawna stosowany w aplikacjach internetowych i to nawet
       w tych starych, bazujących na „normalnych” stronach, które tak mocno krytyku-
       jemy w niniejszej książce. Sam charakter tych aplikacji wymusza istnienie pewnej
       separacji pomiędzy widokiem i modelem, gdyż są one obsługiwane na dwóch
       różnych komputerach. A zatem czy aplikacje internetowe same w sobie są zgodne
       ze wzorcem MVC? Albo, ujmując to w inny sposób: czy można napisać aplikację
       internetową, w której role widoku i modelu zostaną połączone?
           Niestety, taka możliwość istnieje. Co więcej, można to zrobić bardzo łatwo
       i większość programistów tworzących takie aplikacje, włącznie z autorami niniej-
       szej książki, kiedyś tak postąpiła.
           Przeważająca większość zwolenników stosowania wzorca MVC w aplikacjach
       internetowych traktuje wygenerowaną stronę HTML, jak również kod, który ją
       generuje, jako widok. Nie uważają oni, że widokiem jest jedynie to, co zobaczą
       na ekranie po wygenerowaniu strony. W przypadku aplikacji wykorzystujących
       technologię Ajax, przekazujących dane do kodu JavaScript wykonywanego w prze-
       glądarce, widokiem jest dokument XML generowany przez serwer i zwracany
       w odpowiedzi na żądanie HTTP. Oznacza to, że rozdzielenie wygenerowanego
       kodu od logiki biznesowej będzie wymagać pewnej dyscypliny.


3.4.1. Serwerowa część aplikacji tworzona
       bez użycia wzorców
       Aby zilustrować analizowane zagadnienia na przykładzie, napiszemy fragment
       aplikacji wykorzystującej technologię Ajax działający po stronie serwera. Już
       wcześniej, rozdziale 2. oraz w podpunkcie 3.1.4, przedstawiliśmy proste przy-
       kłady kodu JavaScript wykorzystującego możliwości Ajaksa; kolejne przykłady
       przedstawimy w rozdziale 4. Na razie jednak skoncentrujemy się na tym, co się
       dzieje po stronie serwera WWW. Kod serwerowej części aplikacji napiszemy
       najpierw w najprostszy możliwy sposób, a potem będziemy go stopniowo prze-
       kształcać w postać zgodną ze wzorcem MVC. W ten sposób pokażemy, jakie
       zalety — pod względem łatwości wprowadzania różnego typu zmian — posia-
       da ten wzorzec. Zacznijmy zatem od przedstawienia aplikacji.
           Otóż dysponujemy listą ubrań oferowanych w sklepie odzieżowym; lista ta
       jest przechowywana w bazie danych, a my chcemy przeszukiwać tę bazę i wy-
       świetlać użytkownikom listę dostępnych produktów, prezentując dla każdego
       z nich obrazek, nazwę, krótki opis oraz cenę. Jeśli konkretny produkt będzie
       dostępny w kilku różnych kolorach, to chcemy, aby użytkownik mógł także
       określić kolor zamawianego produktu. Podstawowe komponenty tego systemu
118    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      zostały przedstawione na rysunku 3.6; są to: baza danych, struktura danych re-
      prezentujących konkretny produkt, dokument XML jaki ma być przesłany do
      przeglądarki oraz lista wszystkich produktów spełniających podane kryteria
      wyszukiwania.




      Rysunek 3.6. Podstawowe komponenty systemu używane w naszej
      przykładowej aplikacji sklepu internetowego do generacji danych XML.
      W ramach procesu generacji widoku pobieramy zbiór wyników z bazy danych,
      następnie na jego podstawie wypełniamy struktury danych reprezentujące
      poszczególne produkty, a w końcu przesyłamy wszystkie dane do klienta
      w postaci dokumentu XML

      Załóżmy, że użytkownik właśnie wszedł na witrynę naszego sklepu i może wejść
      do jednego z trzech głównych działów: odzieży męskiej, kobiecej oraz dziecięcej.
      Każdy produkt w bazie jest przypisany do jednej z tych trzech kategorii; odpo-
      wiada za to kolumna Category umieszczona w tabeli Garments. Poniżej przed-
      stawiliśmy proste polecenie SQL pobierające wszystkie produkty dostępne
      w kategorii odzieży męskiej:
         SELECT * FROM garments WHERE CATEGORY = 'Menswear';

      Naszym zadaniem jest pobranie wyników tego zapytania i przesłanie ich w postaci
      kodu XML do przeglądarki. Zobaczmy zatem, jak można to zrobić.

      Generacja danych XML w celu ich przesłania do przeglądarki
      Listing 3.5 ilustruje szybkie i niezbyt „eleganckie” rozwiązanie przedstawione-
      go wcześniej wymagania. Skrypt został napisany w języku PHP i korzysta z ba-
      zy danych MySQL. Jednak akurat ten jego aspekt nie ma większego znaczenia
      — w przypadku tego skryptu ważna jest jego ogólna struktura. Skrypty pisane
      w innych językach: ASP, JSP czy też Ruby, mogłyby zostać napisane w bardzo
      podobny sposób.

       Listing 3.5. Szybkie rozwiązanie problemu generacji danych XML na
       podstawie wyników zapytania
         <?php
         header("Content-type: application/xml"); ← Informujemy, że zwracany będzie
                                                     kod XML
         echo "<?xml version="1.0" encoding="UTF-8" ?>n";
3.4.                                    Wzorzec MVC stosowany na serwerze           119



          $db=mysql_connect("my_db_server","mysql_user");          Pobranie
          mysql_select_db("mydb",$db);                             danych z bazy
          $sql="SELECT id,title,description,price,colors,sizes"
            ."FROM garments WHERE category="{$cat}"";
          $result=mysql_query($sql,$db);
          echo "<garments>n";
          while ($myrow = mysql_fetch_row($result)) { ← Przejrzenie zwróconych wyników
          printf("<garment id="%s" title="%s">n"
          ."<description>%s</description>n<price>%s</price>n",
          $myrow["id"],
          $myrow["title"],
          $myrow["description"],
          $myrow["price"]);
          if (!is_null($myrow["colors"])){
          echo "<colors>{$myrow['colors']}</colors>n";
              }
          if (!is_null($myrow["sizes"])){
          echo "<sizes>{$myrow['sizes']}</sizes>n";
              }
              echo "</garment>n";
          }
          echo "</garments>n";
          ?>


       Powyższa strona PHP będzie generować kod XML przypominający ten przed-
       stawiony na listingu 3.6, przy czym w tym przypadku widać, że z bazy zostały
       pobrane dwa produkty. Dla poprawienia czytelności listingu dodaliśmy do niego
       odpowiednie wcięcia. Zdecydowaliśmy się przekazywać dane do klienta w formie
       dokumentu XML, ponieważ jest to bardzo często stosowane rozwiązanie, oraz
       dlatego, że w poprzednim rozdziale pokazaliśmy, jak można pobierać takie dane
       po stronie klienta, używając do tego celu obiektu XMLHttpRequest. W rozdziale 5.
       przedstawimy inne możliwości wymiany danych pomiędzy serwerem i klientem.

        Listing 3.6. Przykładowe wyniki wygenerowane przez skrypt z listingu 3.5
          <garments>
            <garment id="SCK001" title="Golfers’ Socks">
          <description>Garish diamond patterned socks. Real wool.
          Real itchy.</description>
          <price>$5.99</price>
          <colors>heather combo,hawaiian medley,wild turkey</colors>
          </garment>
          <garment id="HAT056" title="Deerstalker Cap">
          <description>Complete with big flappy bits.
          As worn by the great detective Sherlock Holmes.
          Pipe is model’s own.</description>
          <price>$79.99</price>
          <sizes>S, M, L, XL, egghead</sizes>
            </garment>
          </garments>
120           ROZDZIAŁ 3
              Porządkowanie aplikacji wykorzystujących technologię Ajax


             A zatem udało nam się stworzyć serwerową część aplikacji internetowej, przy
             czym założyliśmy, że istnieje już „śliczna” aplikacja działająca po stronie prze-
             glądarki, która, wykorzystując technologię Ajax, pobiera dane XML. Załóżmy, że
             wraz z powiększaniem się asortymentu naszego sklepu chcemy dodać do niego
             kilka nowych kategorii (takich jak: Na szczególne okazje, Codzienna, Dla aktyw-
             nych), opcję wyszukiwania odzieży „na wybraną porę roku” oraz ewentualnie
             opcję wyszukiwania po słowie kluczowym i łącze pozwalające na usunięcie
             wybranych wcześniej produktów. Wszystkie te opcje można by z powodzeniem
             obsłużyć przy użyciu podobnych dokumentów XML. Zobaczmy zatem, w jaki
             sposób moglibyśmy użyć istniejącego kodu do obsługi nowych możliwości oraz
             jakie problemy możemy przy tym napotkać.

             Problemy wielokrotnego stosowania kodu
             Istnieje kilka problemów utrudniających ponowne zastosowanie naszego kodu
             PHP w jego aktualnej postaci. Przede wszystkim zauważmy, że wykonywane
             przez skrypt zapytanie SQL zostało podane na stałe. Oznacza to, że chcąc po-
             nownie przeszukać bazę na podstawie kategorii lub słowa kluczowego, będziemy
             musieli zmienić sposób generacji zapytania SQL. W efekcie możemy uzyskać
             koszmarną sekwencję instrukcji warunkowych, która stopniowo, wraz z pojawia-
             niem się coraz to nowych opcji przeszukiwania, będzie stawać się coraz dłuższa.
                 Można sobie jednak wyobrazić jeszcze gorsze rozwiązanie: umieszczanie
             w klauzuli WHERE zapytania parametru przekazywanego w żądaniu:
                 $sql="SELECT id,title,description,price,colors,sizes"
                   ."FROM garments
                 WHERE ".$sqlWhere;

             W takim przypadku adres URL używany do wywołania skryptu mógłby mieć
             następującą postać:
                 garments.php?sqlWhere=CATEGORY="Menswear"

             Takie rozwiązanie może wprowadzić jeszcze większe zamieszanie w naszym
             modelu i widoku, gdyż kod prezentacji uzyskuje bezpośrednio dostęp do za-
             pytania SQL. Oprócz tego stanowi ono jawne zaproszenie do przeprowadzania
             ataków typu „SQL injection”1 i choć nowe wersje języka PHP posiadają wbu-
             dowane zabezpieczenia przeciwko takim atakom, to jednak bezkrytyczne pole-
             ganie na nich byłoby co najmniej nierozważne.
                 Co więcej, także struktura generowanego dokumentu XML została określona
             na stałe w kodzie PHP — gdzie jest generowana przy użyciu instrukcji printf
             oraz echo. Istnieje jednak wiele potencjalnych powodów, które mogą nas skło-
             nić do zmiany formatu danych. Może się np. zdarzyć, że oprócz ceny sprzedaży
             będziemy chcieli wyświetlić także oryginalną cenę produktu, by przekonać
             wszystkich niedowiarków, że zakup oferowanych przez nas golfów jest naprawdę
             świetnym interesem!
1
    Ataki tego typu polegają na przekazywaniu tak spreparowanych danych, że po zastosowaniu ich
    w poleceniu SQL atakujący uzyska dostęp do tych, których autor skryptu nie planował ujawniać.
    — przyp. tłum.
3.4.                                    Wzorzec MVC stosowany na serwerze          121



           W końcu ostatni problem wynika z faktu, iż dane XML są generowane bez-
       pośrednio na podstawie informacji pobranych z bazy. Można by sądzić, iż jest to
       wydajne rozwiązanie, jednak w rzeczywistości jest ono potencjalnym źródłem
       dwóch problemów. Przede wszystkim podczas generowania kodu XML połącze-
       nie z bazą danych jest cały czas otwarte. W tym konkretnym przypadku wewnątrz
       pętli while nie wykonujemy żadnych bardzo skomplikowanych operacji, zatem
       połączenie z bazą nie będzie otwarte zbyt długo. Niemniej jednak w przyszłości
       takie przetwarzanie wyników przy otwartym połączeniu z bazą może się stać
       wąskim gardłem całego systemu. Poza tym rozwiązanie to będzie działać wyłącznie
       w przypadku gdy będziemy traktować naszą bazę jako płaską strukturę danych.


3.4.2. Refaktoryzacja modelu dziedziny
       Używany przez nas obecnie sposób przechowywania listy kolorów i rozmiarów
       odzieży nie jest szczególnie wydajny — są one przechowywane w polach tabeli
       Garments jako łańcuchy znaków zawierające wartości oddzielone od siebie prze-
       cinkami. Jeśli znormalizujemy naszą bazę danych i zastosujemy się do zaleceń
       związanych z tworzeniem dobrych, relacyjnych modeli danych, to okaże się, że
       konieczne będzie utworzenie dwóch dodatkowych tabel — pierwszej, zawie-
       rającej listę wszystkich dostępnych kolorów oraz drugiej, kojarzącej konkretne
       produkty z kolorami (definiującej coś, co specjaliści od baz danych nazywają
       relacją „wiele-do-wielu”). Zastosowanie takiej relacji zostało przedstawione na
       rysunku 3.7.




       Rysunek 3.7. Relacje wiele-do-wielu w tabelach bazy danych. W tabeli Colors
       przechowywane są wszystkie kolory, jakie mogą mieć produkty, dzięki temu
       w tabeli Garments nie muszą być już przechowywane żadne informacje o kolorach

       Aby określić listę kolorów, w jakich jest dostępna oferowana w sklepie czapeczka
       z dwoma daszkami, przeglądamy tabelę Garments_to_Colors, używając przy tym
       klucza zewnętrznego garment_id. Kolejne porównanie, tym razem kolumny
       color_id z kluczem głównym tabeli Colors, pozwoli nam dowiedzieć się, że
       czapeczka jest dostępna w kolorach różowym i ciemnofiletowym, lecz nie w ko-
       lorze ciemnoszarym. Wykonując oba polecenia w odwrotnej kolejności, może-
       my użyć tabeli Garments_to_Colors do sporządzenia listy wszystkich produktów
       dostępnych w konkretnym kolorze.
122    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


         Obecnie używamy już bazy danych w nieco lepszy sposób, jednak kod SQL
      konieczny do obsłużenia wszystkich zapytań i ich opcji powoli staje się dosyć
      długi. Dlatego też zamiast samodzielnego tworzenia długich, łączonych zapytań,
      dobrze by było, gdybyśmy mogli potraktować nasze produkty jako obiekty, które
      zawierałyby tablice dostępnych kolorów i rozmiarów.

      Narzędzia obsługi odwzorowań obiektowo-relacyjnych
      Na szczęście istnieją biblioteki i narzędzia, które są w stanie przekształcić zbiór
      danych w obiekty; są to tzw. mechanizmy odwzorowań obiektowo-relacyjnych
      (ang. Object-Relational Mappig, w skrócie ORM). Narzędzia ORM automatycznie
      przekształcają obiekty bazy danych w obiekty dostępne dla skryptu PHP i są
      w stanie „stworzyć” za programistę cały niezbędny kod SQL. Programiści używają-
      cy języka PHP mogą się przyjrzeć takim rozwiązaniom jak obiekt DB_DataObject,
      pakiet Easy PHP Data Objects (EZPDO) czy też Metastorage. Programiści
      używający języka Java mają raczej prosty wybór — świadczy to tym fakt, iż ich
      przeważająca większość wybiera pakiet Hibernate (aktualnie trwają prace nad
      udostępnieniem go w wersji dla środowiska .NET). Narzędzia ORM stanowią
      bardzo obszerne zagadnienie, którym, niestety, nie możemy się w niniejszej
      książce zająć.
          Analizując naszą aplikacje pod kątem zastosowania w niej wzorca MVC,
      można zauważyć, iż użycie mechanizmu ORM daje tę dodatkową zaletę, że do-
      stajemy do dyspozycji doskonały obiektowy model. Teraz możemy zmienić kod
      generujący dokument XML w taki sposób, by korzystał z obiektu Garment i w ogóle
      nie „przejmował” się operacjami na bazie danych, które w całości będą realizo-
      wane przez mechanizm ORM. Co więcej, dzięki takiemu rozwiązaniu nie jesteśmy
      już uzależnienie od funkcji służących do obsługi konkretnej bazy danych. Listing
      3.7 przedstawia zmodyfikowaną wersję poprzedniego kodu, wykorzystującą
      mechanizm ORM.

       Listing 3.7. Model obiektów stosowany w sklepie odzieżowym
         require_once "DB/DataObject.php";
         class GarmentColor extends DB_DataObject {
           var $id;
           var $garment_id;
           var $color_id;
         }
         class Color extends DB_DataObject {
           var $id;
           var $name;
         }
         class Garment extends DB_DataObject {
           var $id;
           var $title;
           var $description;
           var $price;
           var $colors;
           var $category;
           function getColors(){
3.4.                                        Wzorzec MVC stosowany na serwerze     123



                  if (!isset($this->colors)){
                    $linkObject=new GarmentColor();
                    $linkObject->garment_id = $this->id;
                    $linkObject->find();
                    $colors=array();
                    while ($linkObject->fetch()){
                      $colorObject=new Color();
                      $colorObject->id=$linkObject->color_id;
                      $colorObject->find();
                      while ($colorObject->fetch()){
                        $colors[] = clone($colorObject);
                      }
                    }
                  }
                  return $colors;
              }
          }


       W tym przypadku obiekty biznesowe (czyli model), które będą używane w na-
       szym przykładowym sklepie, definiujemy w języku PHP, używając do tego klasy
       PEAR::DB_DataObject. Wymaga ona, żeby klasy definiowane przez programistę
       dziedziczyły po klasie DB_DataObject. W innych mechanizmach ORM mogą być
       stosowane zupełnie inne rozwiązania, jednak podstawowa zasada ich działania
       zawsze jest taka sama i polega na tym, że tworzymy grupę obiektów, które są
       używane tak samo jak wszystkie inne obiekty i ukrywają przed nami wszystkie
       problemy i uciążliwości związane ze stosowaniem języka SQL.
           Oprócz głównej klasy Grament zdefiniowaliśmy także klasę Color oraz metodę
       pozwalającą na pobieranie kolorów (obiektów Color), w jakich jest dostępny dany
       produkt (obiekt Garment). W bardzo podobny sposób można by zaimplementować
       obsługę rozmiarów, jednak dla uproszczenia przykładu pominęliśmy ją tutaj.
       Ponieważ zastosowana biblioteka nie obsługuje bezpośrednio relacji „wiele-do-
       wielu”, musimy zdefiniować klasę dla tabeli pośredniej i korzystać z niej w me-
       todzie getColors(). Pomimo to utworzony tak model obiektów jest stosunkowo
       kompletny i zrozumiały. A teraz przekonajmy się, w jaki sposób możemy wyko-
       rzystać ten model obiektów w naszym skrypcie.

       Zastosowanie poprawionego modelu
       Wygenerowaliśmy nowy model obiektów, bazujący na nowej, bardziej przej-
       rzystej strukturze bazy danych. Teraz musimy go użyć w kodzie skryptu PHP.
       Listing 3.8 przedstawia poprawiony kod strony, w której zastosowaliśmy nasze
       nowe klasy.

        Listing 3.8. Zmodyfikowana strona obsługująca bazę danych przy
        wykorzystaniu mechanizmu ORM
          <?php
          header("Content-type: application/xml");
          echo "<?xml version="1.0" encoding="UTF-8" ?>n";
          include "garment_business_objects.inc";
          $garment=new Garment;
124     ROZDZIAŁ 3
        Porządkowanie aplikacji wykorzystujących technologię Ajax

          $garment->category = $_GET["cat"];
          $number_of_rows = $garment->find();
          echo "<garments>n";
          while ($garment->fetch()) {
            printf("<garment id="%s" title="%s">n"
              ."<description>%s</description>n<price>%s</price>n",
              $garment->id,
              $garment->title,
              $garment->description,
              $garment->price);
            $colors=$garment->getColors();
            if (count($colors)>0){
              echo "<colors>n";
              for($i=0;$i<count($colors);$i++){
                echo "<color>{$colors[$i]}</color>n";
              }
              echo "</colors>n";
            }
            echo "</garment>n";
          }
          echo "</garments>n";
          ?>


       Na początku kodu skryptu dołączamy definicje klas, a w kolejnych wykonywanych
       operacjach posługujemy się już wyłącznie obiektami naszego modelu. Zamiast
       tworzenia jakiegoś doraźnego kodu SQL tworzymy pusty obiekt Garment i zapi-
       sujemy w nim kryteria wyszukiwania, jakimi dysponujemy. Ponieważ model
       obiektów jest dołączany w formie pliku zewnętrznego, będziemy go mogli używać
       także na innych stronach, obsługujących inne zapytania. Także kod XML sta-
       nowiący widok naszej aplikacji jest generowany na podstawie obiektów. Kolejną
       czynnością związaną z refaktoryzacją naszej aplikacji będzie oddzielenie formatu
       kodu XML od procesu, który ten kod generuje.


3.4.3. Separacja zawartości od prezentacji
       Widok naszej aplikacji wciąż jest powiązany z obiektem, gdyż z nim powiązany
       jest generowany kod XML. Jeśli aplikacja ta będzie się składać z kilku stron, to
       nie będziemy mogli zmienić formatu kodu XML w jednym, centralnym miejscu
       i liczyć na to, że wszystkie strony „zauważą” i uwzględnią te zmiany. W bardziej
       złożonych rozwiązaniach wykorzystujących kilka różnych formatów dokumentów
       XML, np. osobny format dla listy informacji skróconych, listy informacji pełnych
       oraz listy stanu magazynu, zapewne chcielibyśmy zdefiniować każdy z nich tylko
       jeden raz i dla każdego udostępnić jakieś scentralizowane rozwiązanie pozwala-
       jące na generację dokumentu XML w konkretnym formacie.

       Systemy wykorzystujące szablony
       Jednym z bardzo popularnych rozwiązań problemów tego typu jest użycie sys-
       temu pozwalającego na tworzenie dokumentów tekstowych zawierających pewne
       znaczniki, które podczas przetwarzania są zastępowane wartościami odpowiednich
3.4.                                    Wzorzec MVC stosowany na serwerze         125



       zmiennych. Skrypty PHP, ASP oraz JSP same są językami przetwarzającymi
       szablony, gdyż można je zapisać w formie strony HTML zawierającej znaczniki,
       które w odpowiednich miejscach wstawiają podane wartości. Ten sposób działa-
       nia odróżnia szablony od serwletów Javy oraz tradycyjnych skryptów CGI, które
       zawierają sekwencje instrukcji generujących kod HTML. Języki te pozwalają
       na stosowanie w szablonach wszystkich swoich możliwości, dzięki czemu tak
       łatwo można połączyć logikę biznesową z prezentacją.
           Z kolei w specjalistycznych językach przetwarzania szablonów, takich jak
       Smarty używany języku PHP lub Apache Velocity (system napisany oryginalnie
       w języku Java, a później przeniesiony także na platformę .NET pod nazwą NVelo-
       city), możliwości tworzonego kodu są zazwyczaj znacząco mniejsze i ograniczają
       się do kontroli przepływu sterowania (np. instrukcji warunkowych if) oraz pętli
       (takich jak for i while). Listing 3.9 przedstawia szablon Smarty generujący do-
       kument XML używany w naszej przykładowej aplikacji.

        Listing 3.9. Szablon Smarty służący do generacji kodu XML
          <?xml version="1.0" encoding="UTF-8" ?>
          <garments>
          {section name=garment loop=$garments}
            <garment id="{$garment.id}" title="{$garment.title}">
          <description>{$garment.description}</description>
          <price>{$garment.price}</price>
          {if count($garment.getColors())>0}
          <colors>
          {section name=color loop=$garment.getColors()}
          <color>$color->name</color>
          {/section}
          </colors>
          {/if}
            </garment>
          {/section}
          </garments>


       Powyższy szablon „oczekuje”, że jako dane wejściowe zostanie do niego prze-
       kazana tablica o nazwie garments, zawierająca obiekty Garment. Przeważająca
       część szablonu jest generowana w oryginalnej postaci, jednak jego fragmenty
       umieszczone w nawiasach klamrowych są interpretowane jako instrukcje i za-
       stępowane odpowiednimi zmiennymi bądź traktowane jako pętle lub instrukcje
       warunkowe. Struktura generowanego kodu XML jest znacznie wyraźniejsza
       w szablonie niż w kodzie skryptowym, o czym łatwo można się przekonać, po-
       równując powyższy kod z listingiem 3.7. A teraz zobaczmy, w jaki sposób moż-
       na wykorzystać taki szablon w naszym skrypcie.

       Poprawiony widok
       Udało nam się przenieść definicje formatu XML z głównego skryptu do nieza-
       leżnego pliku szablonu. W efekcie, musimy teraz w skrypcie jedynie zainicjować
       mechanizm obsługi szablonów i przekazać do niego odpowiednie dane. Zmiany,
       jakie należy w tym celu wprowadzić, przedstawiliśmy na listingu 3.10.
126    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


       Listing 3.10. Zastosowanie mechanizmu Smarty do generacji kodu XML
         <?php
         header("Content-type: application/xml");
         include "garment_business_objects.inc";
         include "smarty.class.php";
         $garment=new DataObjects_Garment;
         $garment->category = $_GET["cat"];
         $number_of_rows = $garment->find();
         $smarty=new Smarty;
         $smarty->assign('garments',$garments);
         $smarty->display('garments_xml.tpl');
         ?>


      Sposób stosowania mechanizm Smarty jest bardzo zwięzły i składa się z trzech
      etapów. Pierwszy z nich polega na utworzeniu samego mechanizmu. W ramach
      drugiego etapu należy określić wartości zmiennych, które będą potrzebne do
      wygenerowania szablonu. W naszym przypadku będzie to tylko jedna zmienna,
      jednak możemy ich określić dowolnie wiele — gdyby w sesji były przechowy-
      wane jakieś dane dotyczące użytkownika, to moglibyśmy je także przekazywać,
      by wyświetlić np. spersonalizowane powitanie. W końcu wywołujemy metodę
      display(), przekazując do niej nazwę pliku szablonu.
          Jak widać, udało się nam szczęśliwie odseparować widok od samej strony
      pobierającej dane z bazy. Postać dokumentu XML jest definiowana tylko jeden
      raz, a wykonanie jego generacji zajmuje nam kilka wierszy kodu. Strona z wy-
      nikami wyszukiwania jest ścisła i precyzyjna — zawiera tylko te informacje, które
      są jej potrzebne, konkretnie rzecz biorąc, są to wartości parametrów wyszuki-
      wania oraz dane określające format wyników. Być może Czytelnik pamięta, że
      wcześniej wspominaliśmy, iż byłoby cudownie, gdybyśmy mogli w dowolnej
      chwili zmieniać format XML używany do generacji danych wynikowych. Dzięki
      zastosowaniu mechanizmu Smarty nie przysparza nam to najmniejszych proble-
      mów — wystarczy go zdefiniować. Mechanizm Smarty zapewnia nawet możliwość
      wstawiania jednych szablonów wewnątrz innych, z której możemy skorzystać,
      jeśli zechcemy generować dane wynikowe w sposób bardzo strukturalny.
          Jeśli przyjrzymy się informacjom podanym na samym początku analizy wzorca
      MVC, przekonamy się, iż implementujemy go we właściwy sposób. Na rysunku
      3.8 przedstawiliśmy graficznie to, co do tej pory udało się nam osiągnąć.
          Nasz model jest kolekcją obiektów dziedziny, które są automatycznie zapi-
      sywane w bazie danych przy wykorzystaniu mechanizmu ORM. Rolę widoku
      pełni szablon określający postać generowanego kodu XML. I w końcu kontrole-
      rem jest strona PHP służąca do wyszukiwania produktów według kategorii, jak
      również wszystkie inne strony, które zdecydujemy się zdefiniować i które będą
      łączyć model oraz widok.
          W taki sposób wygląda klasyczne zastosowanie wzorca MVC w aplikacjach
      internetowych. W prezentowanym przykładzie zastosowaliśmy ten wzorzec
      w serwerowej części aplikacji wykorzystującej technologię Ajax, która generuje
3.4.                                     Wzorzec MVC stosowany na serwerze           127




       Rysunek 3.8. Wzorzec MVC w postaci często stosowanej w aplikacjach
       internetowych. Strona WWW lub serwlet działają jako kontroler i w pierwszej
       kolejności pobierają odpowiednie dane z obiektów modelu. Następnie przekazują
       te dane do pliku szablonu (widoku), który z kolei generuje zawartość przesyłaną
       do przeglądarki użytkownika. Warto zauważyć, że jest to rozwiązanie obsługujące
       jedynie operacje odczytu. W przypadku modyfikacji modelu sekwencja
       wykonywanych czynności i zdarzeń byłaby nieco inna, niemniej jednak role
       poszczególnych elementów pozostałyby takie same

       dokumenty XML. Niemniej jednak bez trudu można sobie wyobrazić, w jaki
       sposób można by zastosować ten sam wzorzec w klasycznej aplikacji interne-
       towej, generującej dokumenty HTML.
           W zależności od używanych technologii Czytelnik będzie mógł się spotkać
       z różnymi wersjami przedstawionego wzorca MVC; jednak zawsze podstawo-
       we zasady jego działania i konstrukcji będą identyczne. Np. komponenty EJB
       (ang. Enterprise JavaBean) używane w technologii J2EE definiują model oraz
       kontroler w taki sposób, by można je było przechowywać na różnych serwerach.
       Z kolei na platformie .NET rolę kontrolerów pełnią specjalne „ukryte” obiekty
       definiowane osobno dla poszczególnych stron. Jeszcze inaczej sytuacja wygląda
       we frameworkach takich jak Struts, które definiują tak zwany „kontroler frontonu”
       (ang. front controller) przechwytujący wszystkie żądania kierowane do aplikacji
       i przekazujący je w odpowiednie miejsca. W takich frameworkach działanie kon-
       trolera zostało zmodyfikowane tak, by nie tylko obsługiwał konkretne strony,
       lecz także zarządzał przemieszczaniem się użytkownika pomiędzy poszczegól-
       nymi stronami aplikacji. (W aplikacji wykorzystującej technologię Ajax podobne
       możliwości możemy zaimplementować przy użyciu języka JavaScript). Jednak
       we wszystkich tych przypadkach postać wzorca MVC jest w zasadzie taka sama
       i zakłada się, że właśnie tak powinno się ją rozmieć w kontekście aplikacji inter-
       netowych.
           Opisanie architektury naszej aplikacji przy użyciu wzorca MVC jest przy-
       datnym rozwiązaniem, a co więcej, będziemy mogli z niego korzystać, nawet jeśli
       zdecydujemy się wzbogacić klasyczną aplikację internetową o wykorzystanie
       technologii Ajax. Warto jednak zwrócić uwagę, iż nie jest to jedyna możliwość
       zastosowania wzorca MVC w aplikacjach używających Ajaksa. W rozdziale 4.
       przedstawimy inne wersje tego wzorca, dzięki którym jego zalety będziemy mogli
       wykorzystać także w innych miejscach aplikacji. Zanim to jednak zrobimy, przyj-
       rzyjmy się innym sposobom zapewniania porządku i organizacji w aplikacjach
       wykorzystujących Ajaksa.
128     ROZDZIAŁ 3
        Porządkowanie aplikacji wykorzystujących technologię Ajax


           Oprócz modyfikowania i poprawiania własnego kodu całość kodu aplikacji
       możemy często uprościć i skrócić poprzez zastosowanie różnego typu frame-
       worków i bibliotek. Wraz z ciągłym wzrostem zainteresowania i popularności
       technologii Ajax rośnie także ilość wykorzystujących ją frameworków i biblio-
       tek. Kilka najpopularniejszych rozwiązań tego typu przedstawiliśmy na końcu
       niniejszego rozdziału.



3.5.   Biblioteki i frameworki
       Jednym z podstawowych celów refaktoryzacji jest usuwanie powielającego się
       kodu poprzez umieszczanie go w funkcjach i obiektach, których będzie można
       używać w wielu miejscach aplikacji. Rozwijając tę logikę działania, takie wielo-
       krotnie używane możliwości funkcjonalne można umieścić w bibliotece (lub
       frameworku), której następnie będzie można używać także w innych projektach.
       W ten sposób można zmniejszyć ilość unikalnego kodu, jaki należy napisać pod-
       czas prac nad projektem, a tym samym poprawić wydajność pracy. Co więcej,
       ponieważ taka biblioteka została przetestowana podczas tworzenia wcześniej-
       szych projektów, można oczekiwać, że jej jakość będzie wysoka.
           W dalszej części niniejszej książki stworzymy kilka niewielkich frameworków,
       które Czytelnik będzie mógł zastosować we własnych projektach: w rozdzia-
       łach 4. i 5. stworzymy obiekt ObjectBrowser, w rozdziale 5. zaprezentujemy także
       obiekt CommandQueue, w rozdziale 6. — framework służący do prezentacji powia-
       domień, w rozdziale 8. — mechanizm profilujący StopWatch, a w Dodatku A
       — konsolę testującą. Będziemy także modyfikować aplikacje przykładowe, two-
       rzone w rozdziałach od 9. do 13., przekształcając je w postać komponentów
       nadających się do wielokrotnego stosowania.
           Oczywiście nie tylko my zajmujemy się tworzeniem takich rozwiązań — obec-
       nie na WWW dostępnych jest bardzo wiele frameworków ułatwiających pisanie
       aplikacji obsługiwanych przy użyciu skryptów JavaScript i wykorzystujących
       technologię Ajax. Najpopularniejsze z nich mają tę ogromną zaletę, iż zostały
       bardzo dokładnie przetestowane przez wielu programistów.
           W tym podrozdziale przedstawimy kilka bibliotek i frameworków, po które
       mogą sięgnąć osoby tworzące aplikacje wykorzystujące technologię Ajax. Obecnie
       frameworki do takich aplikacji rozwijają się bardzo dynamicznie, dlatego też nie
       jesteśmy w stanie szczegółowo opisać każdego z rozwiązań dostępnych na rynku;
       spróbujemy jednak pokazać, jakie rodzaje frameworków są dostępne, oraz w jaki
       sposób zastosowanie ich w tworzonych aplikacjach może ułatwić wprowadzanie
       porządku w pisanym przez nas kodzie.


3.5.1. Biblioteki zapewniające poprawne działanie skryptów
       w różnych przeglądarkach
       W punkcie 3.2.1 wspominaliśmy o tym, iż aplikacje wykorzystujące technologię
       Ajax muszą być pisane w nieco inny sposób — w zależności od przeglądarki,
       w jakiej mają działać. Wiele bibliotek ułatwia programistom zdanie, udostępniając
3.5.                                                   Biblioteki i frameworki        129



       wspólną fasadę, która niweluje rozbieżności pomiędzy poszczególnymi prze-
       glądarkami i zapewnia możliwość korzystania z technologii Ajax w jeden spójny
       sposób. Niektóre z nich koncentrują się na ściśle określonych możliwościach
       funkcjonalnych, natomiast inne starają się stworzyć środowiska programistyczne
       o znacznie większym zakresie. W dalszej części rozdziału opisaliśmy te spośród
       dostępnych bibliotek, które uznaliśmy na za najbardziej przydatne i użyteczne
       podczas pisania aplikacji wykorzystujących technologię Ajax.

       Biblioteka x
       Biblioteka x jest dojrzałą biblioteką ogólnego przeznaczenia, służącą do tworzenia
       aplikacji DHTML. Opublikowana po raz pierwszy w 2001 roku, zastąpiła wcze-
       śniejszą bibliotekę autora o nazwie CBE (ang. Cross-Browser Extensions) i wyko-
       rzystuje znacznie prostszy styl programowania. Biblioteka ta udostępnia działające
       w wielu przeglądarkach funkcje pozwalające na manipulację oraz określanie
       stylów elementów DOM, obsługę modelu zdarzeń przeglądarki oraz gotowe
       funkcje do tworzenia animacji oraz obsługi operacji typu „przeciągnij-i-upuść”.
       Można ją z powodzeniem stosować w przeglądarce Internet Explorer 4 (i now-
       szych), jak również w nowych wersjach Opery i przeglądarek fundacji Mozilla.
           Biblioteka ta używa prostego stylu programowania opierającego się na wy-
       korzystaniu funkcji i korzysta z dostępnej w języku JavaScript możliwości prze-
       kazywania do funkcji różnej ilości argumentów oraz braku rygorystycznej kontroli
       typów. Przesłania ona np. standardową metodę document.getElementById() wyma-
       gającą przekazywania łańcucha znaków bardziej elastyczną funkcją, która po-
       zwala na przekazywanie zarówno łańcuchów znaków, jak i elementów DOM.
       W razie przekazania łańcucha znaków funkcja działa normalnie, czyli odnajduje
       element o podanym identyfikatorze, jednak w przypadku przekazania elementu
       DOM funkcja nie wykonuje żadnych operacji i zwraca element przekazany
       w jej wywołaniu. Możliwość użycia elementu DOM zamiast jego identyfikatora
       jest szczególnie przydatna i użyteczna podczas stosowania dynamicznie genero-
       wanego kodu, np. kodu przekazywanego w wywołaniu metody setTimeout() lub
       podczas określania funkcji zwrotnych.
           Podobny, zwięzły styl jest używany także w metodach służących do okre-
       ślania stylów elementów DOM. W tym przypadku dokładnie ta sama funkcja
       służy zarówno do odczytu wartości stylu, jak i jej zapisu. Np. wywołanie o postaci:
          xWidth(myElement);

       zwróci szerokość elementu DOM określonego przez zmienną myElement; przy
       czym zmienna ta może zawierać zarówno sam element DOM, jak i jego identy-
       fikator. Jednak umieszczając w powyższym wywołaniu dodatkowy argument, np.:
          xWidth(myElement, 420);

       jesteśmy w stanie określić szerokość wskazanego elementu. A zatem, aby nadać
       jednemu elementowi taką samą szerokość, jaką ma inny, możemy użyć wywołania:
          xWidth(element2, xWidth(element1));
130    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      Biblioteka x nie zawiera żadnego kodu służącego do tworzenia i obsługi połą-
      czeń sieciowych, niemniej jednak z powodzeniem można jej użyć do tworzenia
      interfejsu użytkownika aplikacji wykorzystujących technologię Ajax. Warto pod-
      kreślić, iż została ona napisana w bardzo przejrzysty i zrozumiały sposób.

      Sarissa
      W porównaniu z biblioteką x Sarissa jest biblioteką o znacznie węższym obszarze
      zastosowań — służy ona głównie do obsługi danych XML w skryptach JavaScript.
      Biblioteka ta obsługuje komponenty ActiveX MSXML (w wersji 3. i wyższych)
      oraz zapewnia swoje podstawowe możliwości funkcjonalne także w przeglądar-
      kach fundacji Mozilla, Opera, Konqueror oraz Safari; choć niektóre, bardziej
      zaawansowane możliwości, takie jak obsługa wyrażeń XPath oraz przekształceń
      XSLT, nie są dostępne we wszystkich przeglądarkach.
          Z punktu widzenia aplikacji wykorzystujących technologię Ajax najważniej-
      szą cechą biblioteki Sarissa jest działająca we wszystkich przeglądarkach moż-
      liwość tworzenia obiektu XMLHttpRequest. Jednak zamiast stosowania swojej
      własnej implementacji wzorca Façade, Sarissa używa wzorca Adapter do utwo-
      rzenia obiektu, który symuluje obiekt XMLHttpRequest w przeglądarkach, jakie
      go nie udostępniają (głównie chodzi tu o Internet Explorer). Tworzony obiekt
      będzie oczywiście używał odpowiedniego obiektu ActiveX (który przedstawiliśmy
      w rozdziale 2.), jednak, z punktu widzenia programisty, po zaimportowaniu
      biblioteki Sarissa poniższy kod będzie działać na wszystkich przeglądarkach:
         var xhr = new XMLHttpRequest();
         xhr.open("GET", "myData.xml");
         xhr.onreadystatechange = function(){
           if(xhr.readyState == 4){
             alert(xhr.responseXML);
           }
         }
         xhr.send(null);

      Jeśli porównamy powyższy kod z listingiem 2.11, to zauważymy, że używane
      wywołania są identyczne do tych, używanych w przypadku stosowania wbudo-
      wanego obiektu XMLHttpRequest dostępnego w przeglądarkach fundacji Mozilla
      oraz w przeglądarce Opera.
          Jak już wcześniej wspominaliśmy, Sarissa udostępnia także grupę ogólnych
      mechanizmów pozwalających na wykonywanie różnego typu operacji na do-
      kumentach XML. Pozwala ona np. na zapisanie dowolnego obiektu JavaScript
      w postaci kodu XML. Mechanizmy te mogą się przydać podczas przetwarzania
      danych XML zwracanych przez serwer, zakładając oczywiście, że zdecydujemy
      się na przesyłanie kodu XML w odpowiedziach na nasze asynchroniczne żądania.
      (Szczegółowe informacje dotyczące możliwych sposobów prowadzenia wymiany
      danych pomiędzy klientem i serwerem zostały opisane w rozdziale 5.).
3.5.                                                   Biblioteki i frameworki        131



       Prototype
       Prototype jest biblioteką ogólnego przeznaczenia wspomagającą pisanie skryptów
       w języku JavaScript. Jej podstawowym celem jest rozszerzanie samego języka
       w celu umożliwienia stosowania bardziej obiektowego stylu programowania.
       Biblioteka ta wykorzystuje unikalny styl tworzenia kodu JavaScript, bazujący
       na wprowadzanych przez nią rozszerzeniach. Sam kod tworzony dzięki możli-
       wościom tej biblioteki może być trudny do analizy, gdyż znacznie odbiega on
       od kodu tworzonego w językach Java i C#, jednak stosowanie zarówno samej
       biblioteki Prototype, jak i innych bibliotek stworzonych w oparciu o nią jest dosyć
       proste. Prototype można traktować jako bibliotekę przeznaczoną dla twórców
       innych bibliotek. Programiści tworzący aplikacje wykorzystujące technologię Ajax
       nie będą zapewne korzystali bezpośrednio z biblioteki Prototype, lecz z innych
       bibliotek wykorzystujących jej możliwości. Biblioteki te przedstawimy w dalszej
       części rozdziału. Na razie jednak zaprezentujemy podstawowe możliwości, jakie
       daje Prototype, by na ich przykładzie pokazać stosowany w niej styl programowa-
       nia. Informacje te przydadzą nam się później, podczas opisywania takich roz-
       wiązań, jak Scriptaculous, Rico oraz Ruby on Rails.
           Prototype pozwala, aby jeden obiekt rozszerzał drugi — w tym celu wszystkie
       właściwości i metody obiektu bazowego są kopiowane do obiektu potomnego.
       Możliwość tę najlepiej jest przedstawić na przykładzie. Załóżmy, że zdefinio-
       waliśmy klasę bazową o nazwie Vehicle:
          function Vehicle(numWheels,maxSpeed){
            this.numWheels=numWheels;
            this.maxSpeed=maxSpeed;
          }

       Teraz chcemy utworzyć konkretny obiekt tej klasy, który będzie reprezentować
       pociąg pasażerski. W naszym nowym obiekcie chcemy określić ilość wagonów
       oraz udostępnić mechanizm ich dodawania i usuwania. W kodzie JavaScript
       możliwości te można by zaimplementować w następujący sposób:
          var passTrain=new Vehicle(24,100);
          passTrain.carriageCount=12;
          passTrain.addCarriage=function(){
          this.carriageCount++;
          }
          passTrain.removeCarriage=function(){
            this.carriageCount--;
          }

       Jak widać, udało nam się zaimplementować w nowym obiekcie wszystkie po-
       trzebne możliwości funkcjonalne. Jednak analizując ten kod pod względem archi-
       tektury, można łatwo zauważyć, iż te dodatkowe możliwości funkcjonalne nie
       zostały umieszczone w jakiejś ogólnie pojętej „jednostce”. Biblioteka Prototype
       może nam pomóc w rozwiązaniu tego problemu, pozwala ona bowiem na zdefi-
       niowanie rozszerzonych możliwości w postaci obiektu, a następnie na skopio-
       wanie ich do obiektu bazowego. W pierwszej kolejności musimy zdefiniować
       nowy obiekt, posiadający rozszerzone możliwości:
132    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax

         function CarriagePuller(carriageCount){
         this.carriageCount=carriageCount;
         this.addCarriage=function(){
             this.carriageCount++;
           }
           this.removeCarriage=function(){
             this.carriageCount--;
           }
         }

      Teraz możemy połączyć oba obiekty w jeden, dysponujący wszystkimi oczeki-
      wanymi możliwościami:
         var parent=new Vehicle(24,100);
         var extension=new CarriagePuller(12);
         var passTrain=Object.extend(parent,extension);

      Warto zauważyć, że w pierwszej kolejności definiujemy obiekt bazowy oraz
      obiekt rozszerzający, a dopiero później „scalamy” je ze sobą. W tym przypadku
      relacja występuje pomiędzy konkretnymi obiektami, a nie pomiędzy klasami
      Vehicle oraz CarrigePuller. Choć nie jest to klasyczne rozwiązanie obiektowe,
      niemniej jednak pozwala ono na umieszczenie kodu związanego z pewną funk-
      cją (taką jak ciągnięcie wagonów) w jednym, precyzyjnie określonym miejscu
      i bardzo upraszcza jego wielokrotnie stosowanie. Choć wykorzystywanie tych
      możliwości funkcjonalnych w niewielkim i prostym przykładzie, takim jak ten,
      może się wydawać raczej niepotrzebne, to jednak w dużych projektach taka
      hermetyzacja możliwości funkcjonalnych jest niezwykle przydatna.
          Biblioteka Prototype wspomaga także stosowanie technologii Ajax — udo-
      stępnia ona klasę Ajax oraz grupę klas potomnych „ukrywających” przed pro-
      gramistą wszelkie rozbieżności związane z tworzeniem obiektu XMLHttpRequest
      w różnych przeglądarkach i ułatwiających obsługę żądań. Jedną z takich klas jest
      klasa Ajax.Request, która pozwala na generowanie żądań przy użyciu obiektu
      XMLHttpRequest. Poniżej przedstawiliśmy przykład jej użycia:
         var req=new Ajax.Request('myData.xml');

      Powyższy konstruktor używa stylu, który często można zobaczyć także w innych
      bibliotekach korzystających z Prototype. Otóż pozwala on na przekazanie dodat-
      kowego argumentu, będącego tablicą asocjacyjną. W ten sposób, w razie koniecz-
      ności, można określić bardzo wiele aspektów działania tworzonego obiektu. Dla
      każdej z takich opcji konfiguracyjnych została określona sensowna wartość do-
      myślna, dzięki czemu przekazywanie takiego dodatkowego obiektu jest konieczne
      wyłącznie gdy chcemy przesłonić jakieś domyślne ustawienia. W przypadku
      konstruktora klasy Ajax.Request w tej dodatkowej tablicy można podać: infor-
      macje, jakie zostaną przesłane na serwer metodą POST, parametry żądania,
      metodę, jaka zostanie użyta do przesłania żądania na serwer, oraz funkcje zwrotne
      służące do obsługi odpowiedzi oraz ewentualnych błędów. Poniżej przedstawili-
      śmy znacznie bardziej rozbudowane wywołanie konstruktora Ajax.Request():
         var req=new Ajax.Request(
           'myData.xml',
3.5.                                                    Biblioteki i frameworki     133



            {
                method: 'get',
                parameters: { name:'dawid',likes:'czekoladę,rabarbar' },
                onLoaded: function(){ alert('plik wczytany!'); },
                onComplete: function(){
                  alert('gotowe!nn'+req.transport.responseText);
                }
            }
          );

       W powyższym przykładzie w tablicy zostały określone cztery parametry. Najpierw
       określiliśmy, że żądanie ma zostać przesłane metodą get; to ważne, gdyż Proto-
       type domyślnie używa metody post. Ponieważ użyjemy metody get, parametry
       określone w kolejnym elemencie tablic (parameters) zostaną umieszczone w łańcu-
       chu zapytania. W przypadku przesłania żądania metodą post parametry te zostały-
       by zapisane w treści żądania. I w końcu określamy funkcje zwrotne onLoad oraz
       onComplete, które zostaną wywołane po zmianie stanu właściwości readyState
       obiektu XMLHttpRequest. Zmienna req.transport zastosowana w kodzie funkcji
       onComplete zawiera referencję do obiektu XMLHttpRequest.
           Kolejną klasą związaną z obsługą żądań, udostępnianą przez bibliotekę Pro-
       totype, jest klasa Ajax.Updater. Obiekty tej klasy pobierają fragmenty kodu Java-
       Script przesyłane z serwera i przetwarzają je. Rozwiązanie to stanowi przykład
       „interakcji operujących na skryptach”, które opiszemy w rozdziale 5.; dokład-
       niejsza prezentacja tego zagadnienia wykracza poza ramy tematyczne niniej-
       szego rozdziału.
           Na tym zakończymy prezentację bibliotek ułatwiających tworzenie skryptów
       działających w wielu różnych przeglądarkach. Przedstawiliśmy jedynie kilka
       wybranych bibliotek, a zamieszczone tu informacje na pewno nie są wyczerpujące.
       Należy także pamiętać, iż biblioteki, frameworki oraz wszelkie innego typu na-
       rzędzia do tworzenia aplikacji wykorzystujących technologię Ajax rozwijają się
       obecnie bardzo dynamicznie, dlatego też musieliśmy ograniczyć zamieszczone
       tu informacje jedynie do rozwiązań, które cieszą się największą popularnością
       i są najlepsze. W następnym punkcie rozdziału przedstawimy szkielety bazujące
       na kontrolkach, utworzone w oparciu o inne biblioteki (w tym także biblioteki,
       o których wspominaliśmy wcześniej).


3.5.2. Kontrolki oraz bazujące na nich szkielety
       Biblioteki, które przedstawiliśmy w poprzedniej części rozdziału, zapewniały
       bezpieczne i pewne możliwości wykonywania operacji stosunkowo niskiego
       poziomu, takich jak operowanie na elementach DOM bądź pobieranie doku-
       mentów z serwera. Dzięki zastosowaniu takich narzędzi na pewno można sobie
       znacznie ułatwić tworzenie funkcjonalnych interfejsów użytkownika, jednak
       i tak zmuszają nas one do wykonania znacznej pracy — w porównaniu z takimi
       bibliotekami, jak Swing, MFC bądź Qt.
           Powoli zaczynają się jednak pojawiać gotowe kontrolki lub nawet całe pakiety
       kontrolek, których mogą używać programiści tworzący aplikacje w języku Java-
       Script. W typu punkcie rozdziału przedstawimy kilka wybranych rozwiązań tego
134    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      typu; chodzi nam raczej o ogólne zaprezentowanie możliwości, jakie stwarzają
      takie narzędzia, niż o przedstawienie ich wyczerpującego opisu.

      Scriptaculous
      Biblioteki Scriptaculous zawierają komponenty graficznego interfejsu użytkow-
      nika napisane w oparciu o bibliotekę Prototype (patrz poprzedni punkt rozdziału).
      Koncentrują się one na dwóch podstawowych grupach możliwości funkcjonal-
      nych, choć są cały czas aktywnie rozwijane.
          Biblioteka Effects definiuje wiele efektów wizualnych, których można używać
      do modyfikowania postaci i wyglądu elementów DOM, np. można zmieniać ich
      położenie, wielkość oraz przezroczystość. Poszczególne efekty może ze sobą
      łatwo łączyć, a dodatkowo przygotowano grupę gotowych efektów złożonych,
      takich jak Puff(), który powoduje, że wskazany element staje się coraz większy
      i bardziej przezroczysty, aż w końcu całkowicie znika. Kolejnym bardzo ważnym
      i przydatnym efektem jest efekt o nazwie Parallel() — pozwala on na równo-
      czesne wykonywanie innych, wskazanych efektów. Mogą one stanowić dosko-
      nały sposób na szybkie dodanie do interfejsu użytkownika aplikacji wizualnych
      sygnałów zwrotnych; zagadnieniem tym zajmiemy się bardziej szczegółowo
      w rozdziale 6.
          Wywołanie jednego z predefiniowanych efektów wizualnych jest niezwykle
      proste — sprowadza się do wywołania odpowiedniego konstruktora i przeka-
      zania do niego identyfikatora elementu DOM. Oto przykład:
         new Effect.SlideDown(myDOMElement);

      Podstawą tych wszystkich efektów jest obiekt przejścia, którego działanie można
      parametryzować, np. określając czas trwania przejścia oraz funkcje zwrotne, jakie
      mają być wykonana na jego początku i końcu. Dostępnych jest kilka podstawo-
      wych typów przejść, takich jak: linearne, sinusoidalne, pulsujące oraz drgające.
      Tworzenie własnych efektów sprowadza się do wybrania odpowiedniego efektu
      podstawowego (lub ich kombinacji) i określenia odpowiednich parametrów.
      Szczegółowy opis sposobu tworzenia takich efektów wykracza jednak poza ramy
      tematyczne tej krótkiej prezentacji. Przykład praktycznego zastosowania biblioteki
      Scriptaculous można znaleźć w rozdziale 6., w którym użyliśmy jej do tworzenia
      systemu prezentacji powiadomień.
          Kolejną możliwością, jaką zapewnia biblioteka Scriptaculous jest obsługa
      operacji typu „przeciągnij-i-upuść”. Do tego celu służy klasa Sortable. Wymaga
      ona określenia elementu rodzica i umożliwia przeciąganie i upuszczanie wszyst-
      kich jego elementów potomnych. Przy użyciu opcji podawanych w wywołaniu
      konstruktora można określić funkcje zwrotne wywoływane w momencie rozpo-
      czynania i kończenia przeciągania, typy elementów, jakie można przeciągać,
      oraz listę „celów” przeciągania (czyli elementów, w których można upuszczać
      przeciągane elementy). Istnieje także możliwość przekazywania jako opcji obiek-
      tów efektów wizualnych określających, jak element będzie się zachowywać
      w momencie rozpoczynania przeciągania, podczas samego przeciągania oraz
      w chwili upuszczania.
3.5.                                                  Biblioteki i frameworki      135



       Rico
       Pakiet Rico, podobnie jak biblioteki Scriptaculous, korzysta z możliwości, jakie
       daje biblioteka Prototype. Rico pozwala na stosowanie operacji „przeciągnij-i-
       upuść” oraz różnego rodzaju efektów wizualnych, stwarzając przy tym bardzo
       duże możliwości ich konfigurowania. Oprócz tego Rico udostępnia pojęcie
       obiektu zachowania — Behavior — czyli fragmentu kodu, który można dodać
       do fragmentu drzewa DOM, aby później rozszerzać jego działanie. Pakiet zawiera
       kilka przykładów zastosowania tych obiektów; jednym z nich jest komponent
       Accordion, który umieszcza kilka elementów DOM w jednym obszarze, a następ-
       nie pozwala na wyświetlanie zawartości wybranego z nich. (Komponenty o takim
       działaniu często określa się jako pasek programu Outlook, gdyż zostały one
       spopularyzowane w programie Microsoft Outlook).
           Spróbujmy zatem teraz utworzyć prosty przykład komponentu Accordion.
       W pierwszej kolejności potrzebujemy nadrzędnego elementu DOM — każdy
       z elementów umieszczonych wewnątrz niego stanie się jednym panelem tworzo-
       nego komponentu. Każdy panel definiujemy jako element <div>, wewnątrz
       którego zostają umieszczone dwa kolejne elementy <div> reprezentujące odpo-
       wiednio: nagłówek oraz treść panelu:
          <div id='myAccordion'>
            <div>
              <div>Definicja</div>
              <div>
                <ul>
                  <li><b>n.</b>instrument muzyczny z grupy idiofonów -
                     instrymentów perkusyjnych. Rodzaj harmonii, oparty na
                     stroikach przelotowych. Prąd powietrza, zagęszczonego
                     przez ręcznie poruszany miech, wywołuje dźwięk, wprawiając
                    w drganie stroik (źródłem dźwięku jest zatem drgająca
                     blaszka, a nie słup powietrza - dlatego
                     instrument perkusyjny, nie aerofon).</li>
                </ul>
              </div>
            </div>
            <div>
              <div>Rysunek</div>
              <div>
               <img src='monkey-accordion.jpg'></img>
              </div>
            </div>
          </div>

       Pierwszy panel zawiera fragment definicji opisującej, czym jest akordeon, nato-
       miast drugi przedstawia jego zdjęcie (patrz rysunek 3.9). Powyższy kod HTML
       wyświetlony w swojej oryginalnej postaci będzie przedstawiał oba panele — jeden
       nad drugim. Jednak w głównym elemencie naszego kodu określiliśmy identyfika-
       tor (jako wartość atrybutu id), co pozwala nam przekazać referencję do tego
       elementu w wywołaniu konstruktora obiektu Accordion. Obiekt ten tworzymy
       w następujący sposób:
136     ROZDZIAŁ 3
        Porządkowanie aplikacji wykorzystujących technologię Ajax

          var outer=$('myAccordion');
          outer.style.width='320px';
          new Rico.Accordion(
            outer,
            { panelHeight:400,
              expandedBg:'#909090',
              collapsedBg:'#404040',
            }
          );

       Pierwszy wiersz powyższego fragmentu kodu wygląda dosyć ciekawie. Okazuje
       się jednak, że znak $ jest normalną, dopuszczalną nazwą zmiennej w języku
       JavaScript, a biblioteka Prototype zastosowała go jako nazwę funkcji. A zatem
       funkcja $() przypomina nieco funkcję xGetElementById(), którą poznaliśmy przy
       okazji prezentacji biblioteki x. Pobraną referencję do elementu DOM przekazu-
       jemy w wywołaniu konstruktora obiektu Accordion wraz z dodatkową tablicą
       opcji. W naszym przypadku opcje określają jedynie style, jakie należy zastosować
       podczas prezentowania komponentu na stronie; jednak w tablicy opcji można
       także określić funkcje zwrotne, wywoływane w momencie otwierania i zamykania
       paneli. Efekty zastosowania obiektu Accordion do określenia postaci zwyczaj-
       nych elementów <div> zostały przedstawione na rysunku 3.9. Klasa Behavior
       pakietu Rico pozwala na bardzo łatwe tworzenie komponentów na podstawie
       kodu HTML, a oprócz tego oddziela ich zawartość od kodu określającego sposób
       ich działania. Zagadnienia związane ze stosowaniem dobrych i sprawdzonych
       rozwiązań podczas tworzenia interfejsów użytkownika w języku JavaScript przed-
       stawimy w rozdziale 4.
           Ostatnią cechą pakietu Rico, o jakiej chcieliśmy tu wspomnieć, są doskonałe
       narzędzia do generowania i obsługi asynchronicznych żądań. Do tego celu
       używany jest globalny obiekt AjaxEngine. Jest on czymś znacznie więcej niż je-
       dynie „pojemnikiem” ukrywającym w sobie zwyczajny obiekt XMLHttpRequest
       i pozwalającym na jego stosowanie w różnych przeglądarkach. Obiekt ten defi-
       niuje format odpowiedzi XML, który powinien się składać z sekwencji elementów
       <response>. Mechanizm udostępniany przez pakiet Rico potrafi automatycznie
       dekodować takie odpowiedzi, przy czym rozróżnia dwa ich rodzaje: odpowiedzi
       modyfikujące bezpośrednio zawartość elementów DOM oraz modyfikujące
       wartości przechowywane w obiektach JavaScript. Podobne rozwiązania przed-
       stawimy szczegółowo w punkcie 5.5.3 poświęconym zagadnieniom interakcji
       pomiędzy klientem a serwerem. Na razie zajmiemy się jednak frameworkami,
       które spajają części aplikacji działającej na serwerze oraz w kliencie.


3.5.3. Frameworki aplikacji
       Wszystkie frameworki, jakie przedstawiliśmy wcześniej, działają jedynie w prze-
       glądarce i można je stosować jako statyczne pliki JavaScript pobierane z serwera.
       Ostatnią grupą rozwiązań, jakie chcielibyśmy tu przedstawić, są frameworki,
       które są przechowywane na serwerze, lecz dynamicznie generują fragmenty kodu
       JavaScript i HTML.
3.5.                                                    Biblioteki i frameworki      137




Rysunek 3.9. Obiekty Behavior dostępne we frameworku Rico pozwalają na przekształcanie
zwyczajnych węzłów DOM w interaktywne kontrolki. W tym celu wystarczy przekazać
do konstruktora odpowiedniego obiektu Behavior identyfikator głównego węzła. W tym
przypadku grupa elementów <div> (widoczna po lewej stronie) została przekształcona
w interaktywną kontrolkę menu (pokazaną z prawej strony), w której kliknięcia otwierają
i zamykają poszczególne panele

          Są to zdecydowanie najbardziej złożone ze wszystkich frameworków, o jakich
          wspominamy w tej książce, i niestety, nie będziemy w stanie przedstawić ich
          wyczerpująco i szczegółowo. Pomimo to chcielibyśmy zamieścić krótki opis
          cech i możliwości takich rozwiązań. Do zagadnień związanych z takimi frame-
          workami wrócimy jednak w rozdziale 5.

          DWR, JSON-RPC oraz SAJAX
          Zaczniemy od przedstawienia trzech niewielkich frameworków działających po
          stronie serwera. Opiszemy je wspólnie, gdyż posiadają wiele tych samych cech,
          choć zostały napisane w różnych językach. Framework może współpracować
          z wieloma różnymi językami, takimi jak: PHP, Python, Perl oraz Ruby. DWR
          (ang. Direct Web Remoting) został napisany w języku Java, przy czym wykorzy-
          stuje to samo podejście, polegające na udostępnianiu metod obiektów, a nie
          samych funkcji. Trzeci z wymienionych frameworków, JSON-RPC (ang. Java-
          Script Object Notation-base Remote Procedure Calls), został zaprojektowany
          dokładnie w taki sam sposób. Także i on umożliwia korzystanie z języków Java-
          Script, Python, Ruby, Perl oraz Java.
              Wszystkie te frameworki pozwalają, by metody obiektów działających na
          serwerze były udostępniane jako żądania Ajaksa. Często zdarza się, że dyspo-
          nujemy wykonywaną na serwerze funkcją generującą przydatne informacje,
138    ROZDZIAŁ 3
       Porządkowanie aplikacji wykorzystujących technologię Ajax


      które następnie muszą być określone na serwerze, gdyż np. bazują na informacjach
      pobieranych z bazy danych. Opisywane tu frameworki zapewniają wygodny
      sposób dostępu do tych funkcji lub metod z poziomu przeglądarki i z powodze-
      niem można je zastosować do udostępnienia obiektów modelu dziedziny uży-
      wanego na serwerze w kodzie wykonywanym w przeglądarce.
          Przyjrzyjmy się przykładowi zastosowania frameworka SAJAX, w którym
      udostępnimy funkcję używaną na serwerze i napisaną w języku PHP. Nasza
      funkcja jest wyjątkowo prosta, gdyż jej działanie ogranicza się do zwrócenia
      łańcucha znaków:
         <?php
         function sayHello(name){
           return("Hello! {$name} Ajax in Action!!!!");
         }
         ?>

      Aby udostępnić tę funkcję dla kodu JavaScript wykonywanego w przeglądarce,
      należy zaimportować mechanizm SAJAX i wywołać funkcję sajax_export():
         <?php
         require('Sajax.php');
         sajax_init();
         sajax_export("sayHello");
         ?>

      Następnie, pisząc kod dynamicznie generowanej strony WWW, używany ko-
      lejnych funkcji frameworka do wygenerowania kodu pozwalającego na odwo-
      ływanie się do wyeksportowanej wcześniej funkcji. Ten wygenerowany kod
      definiuje w przeglądarce lokalną funkcję, której sygnatura odpowiada sygnaturze
      funkcji działającej na serwerze:
         <script type='text/javascript'>
         <?
         sajax_show_javascript();
         ?>
         ...
         alert(sayHello("Dawid"));
         ...
         </script>

      Wywołanie funkcji sayHello("Dawid") w przeglądarce spowoduje, że wygene-
      rowany kod JavaScript prześle na serwer żądanie, które spowoduje wykonanie
      odpowiedniej funkcji i przekazanie zwróconych przez nią wyników w odpowiedzi
      HTTP. Następnie, po stronie przeglądarki, odebrane wyniki zostaną przetwo-
      rzone i udostępnione dla kodu JavaScript. Programista nie musi zajmować się
      jakimikolwiek aspektami korzystania z technologii Ajax — wszystkie niezbędne
      operacje są wykonywane w niewidoczny sposób przez biblioteki SAJAX.
          Prezentowane tu frameworki kojarzą funkcje i obiekty dostępne na serwerze
      z kodem JavaScript wykonywanym w przeglądarce — na stosunkowo niskim
      poziomie abstrakcji. Automatyzują one czynności, których wykonanie byłoby
      żmudne i pracochłonne, jednak stosując je, ryzykujemy, że udostępnimy zbyt
      wiele możliwości funkcjonalnych zaimplementowanych na serwerze. Zagadnie-
      nia te opiszemy bardziej szczegółowo w rozdziale 5.
3.5.                                                  Biblioteki i frameworki       139



           Pozostałe frameworki, które opiszemy w dalszej części rozdziału, prezen-
       tują znacznie bardziej złożone podejście, gdyż pozwalają na generację całych
       warstw interfejsu użytkownika na podstawie modelu zadeklarowanego na ser-
       werze. Choć wewnętrznie frameworki te wykorzystują standardowe możliwo-
       ści technologii Ajax, jednak z punktu widzenia twórców aplikacji udostępniają
       swój własny model programowania. W efekcie korzystanie z nich raczej w nie-
       wielkim stopniu przypomina stosowanie ogólnej technologii Ajax, dlatego też
       w niniejszej książce będziemy w stanie przedstawić je jedynie bardzo krótko
       i pobieżnie.

       Backbase
       Backbase Presentation Server udostępnia bardzo bogaty zbiór kontrolek koja-
       rzących wykonawczą część aplikacji ze znacznikami XML umieszczonymi
       w dokumencie HTML generowanym przez serwer. Zasada działania tego frame-
       worka przypomina nieco działanie komponentów Behavior pakietu Rico, z tym
       że Backbase używa własnego zbioru elementów XHTML reprezentujących
       komponenty interfejsu użytkownika, a nie zwyczajnych znaczników HTML.
           Część frameworka działająca po stronie serwera jest dostępna w wersji na-
       pisanej w języku Java oraz w wersji przeznaczonej dla platformy .NET. Backbase
       jest produktem komercyjnym, jednak dostępna jest także wersja, z której można
       korzystać bezpłatnie.

       Echo2
       Echo2 firmy NextApp’s jest napisanym w Javie frameworkiem wykonywanym
       po stronie serwera, który generuje zaawansowane kontrolki na podstawie modelu
       interfejsu użytkownika zadeklarowanego na serwerze. Po uruchomieniu w prze-
       glądarce kontrolki interfejsu użytkownika są raczej niezależne i będą obsługi-
       wały czynności wykonywane przez użytkownika lokalnie, używając odpowied-
       niego kodu JavaScript, bądź też będą przesyłać żądania na serwer, używając
       przy tym kolejek, podobnie jak ma to miejsce w przypadku pakietu Rico.
           Echo2 jest reklamowany jako rozwiązanie wykorzystujące technologię Ajax,
       które od swych użytkowników nie wymaga żadnej znajomości języka HTML,
       JavaScript ani CSS, oczywiście zakładając, że nie będziemy chcieli rozszerzać lub
       zmieniać domyślnego zbioru dostępnych komponentów. W większości przypad-
       ków tworzenie interfejsu użytkownika odbywa się wyłącznie w języku Java.
       Echo2 jest frameworkiem typu „open source”, rozpowszechnianym na warunkach
       licencji Mozilla, która daje możliwość stosowania go w aplikacjach komercyjnych.

       Ruby on Rails
       Ruby on Rails jest frameworkiem służącym do tworzenia aplikacji interneto-
       wych w języku Ruby. Łączy on w sobie rozwiązania pozwalające na kojarzenie
       obiektów wykonywanych na serwerze z bazą danych, prezentowanie zawarto-
       ści przy użyciu szablonów i działa w sposób bardzo przypominający wzorzec
       MVC opisany w podrozdziale 3.4. Ruby on Rails zapewnia bardzo szybki proces
140     ROZDZIAŁ 3
        Porządkowanie aplikacji wykorzystujących technologię Ajax


       tworzenia niewielkich i średnich aplikacji internetowych, gdyż pozwala on na
       automatyczne generowanie często powtarzającego się kodu. Oprócz tego twórcy
       frameworka starali się zminimalizować ilość czynności konfiguracyjnych koniecz-
       nych do zapewnienia poprawnego działania aplikacji.
           W nowszych wersjach Ruby on Rails zapewnia możliwość wykorzystywania
       technologii Ajax poprzez udostępnienie biblioteki Prototype. Oba te narzędzia
       współpracują ze sobą w bardzo ścisły i naturalny sposób, gdyż kod JavaScript
       korzystający z biblioteki Prototype jest generowany automatycznie. Co więcej,
       zarówno Ruby, jak i Prototype wykorzystują podobny styl programowania. Po-
       dobnie framework Echo2, także Ruby on Rails pozwala na stosowanie techno-
       logii Ajax bez konieczności dobrej znajomości technologii podstawowych, takich
       jak JavaScript. Jednak osoby znające JavaScript mogą rozbudowywać mechani-
       zmy wykorzystania Ajaksa dostępne w tym frameworku.
           Informacjami o frameworku Ruby on Rails zakończyliśmy prezentację narzędzi
       i rozwiązań wspomagających wykorzystywanie technologii Ajax. Jak zaznaczyli-
       śmy na samym początku, narzędzia te rozwijają się obecnie bardzo dynamicznie,
       dotyczy to także znacznej części prezentowanych tu frameworków i bibliotek.
           Warto zauważyć, że większość z prezentowanych tu bibliotek i frameworków
       charakteryzuje się swoimi własnymi rozwiązaniami i unikalnym stylem pro-
       gramowania. Pisząc przykłady do niniejszej książki, staraliśmy się przedstawiać
       charakter poszczególnych technologii i technik oraz unikać zbyt częstego sto-
       sowania jednego konkretnego frameworka. Niemniej jednak Czytelnik na pew-
       no zauważy, że wzmianki o niektórych frameworkach i bibliotekach zawarte w tym
       rozdziale będą się pojawiały także w innych miejscach książki.



3.6.   Podsumowanie
       W tym rozdziale przedstawiliśmy pojęcie refaktoryzacji jako sposobu na po-
       prawę jakości i elastyczności kodu. Nasze pierwsze spotkanie z refaktoryzacją
       polegało na zapewnieniu łatwych możliwości wielokrotnego stosowania obiektu
       XMLHttpRequest w wielu różnych przeglądarkach.
          Przedstawiliśmy także kilka wzorców projektowych przydatnych do rozwią-
       zywania problemów, które najczęściej występują podczas tworzenia kodu aplikacji
       wykorzystujących technologię Ajax. Wzorce projektowe stanowią nieformalny
       sposób pozyskiwania wiedzy zdobytej przez innych programistów, którzy wcze-
       śniej rozwiązywali podobne problemy, i pozwalają nam dążyć do określonego
       celu.
          Wzorce Façade oraz Adapter pozwalają ukrywać różnice występujące po-
       między różnymi używanymi implementacjami. W aplikacjach wykorzystujących
       technologię Ajax oba te wzorce są szczególnie przydatne, gdyż pozwalają nam
       stworzyć warstwę chroniącą nasz kod przed rozbieżnościami występującymi
       w poszczególnych przeglądarkach. A właśnie te rozbieżności są podstawowym
       utrapieniem programistów tworzących aplikacje w języku JavaScript.
3.6.                                                         Podsumowanie          141



           Kolejnym przedstawionym przez nas wzorcem projektowym był Observer.
       Jest to elastyczny wzorzec wykorzystywany w systemach sterowanych zdarze-
       niami. Wrócimy do niego w rozdziale 4., gdzie będziemy zajmowali się warstwami
       interfejsu użytkownika występującymi w aplikacjach internetowych. Observer
       w połączeniu z wzorcem projektowym Command, stanowiącym doskonały sposób
       hermetyzacji czynności wykonywanych przez użytkownika, umożliwia utworzenie
       solidnego rozwiązania służącego do obsługi poczynań użytkownika na witrynie
       i zapewniającego możliwość odtwarzania ich skutków. Wzorzec Command można
       także stosować podczas określania sposobu organizacji interakcji pomiędzy klien-
       tem a serwerem; tymi zagadnieniami zajmiemy się w rozdziale 5.
           Wzorzec Singleton stanowi bardzo prosty i skuteczny sposób kontrolowania
       dostępu do konkretnego zasobu. W aplikacjach wykorzystujących technologię
       Ajax można go z powodzeniem używać do kontroli dostępu do sieci. Stosowne
       przykłady zamieściliśmy w rozdziale 5.
           W końcu przedstawiliśmy także wzorzec projektowy MVC (ang. Model-View-
       Controller). Jest to wzorzec architektoniczny, który od wielu lat (przynajmniej
       w krótkiej historii Sieci) jest stosowany do tworzenia aplikacji internetowych.
       W niniejszym rozdziale opisaliśmy, jak zastosowanie tego wzorca może poprawić
       elastyczność aplikacji. Przedstawiliśmy też przykład, w którym zastosowaliśmy
       model obiektowy stworzony w oparciu o mechanizm ORM oraz system obsługi
       szablonów.
           Na przykładzie aplikacji obsługującej sklep odzieżowy zademonstrowaliśmy,
       w jaki sposób można jednocześnie używać refaktoryzacji oraz wzorców projek-
       towych. Bardzo trudno jest już za pierwszym razem stworzyć idealnie zapro-
       jektowany kod, jednak z powodzeniem można zastosować refaktoryzację oraz
       „nieeleganckie”, lecz użyteczne fragmenty kodu (takie jak kod z listingu 3.4),
       by powoli, etapami, wprowadzać wzorce projektowe do naszej aplikacji.
           Pod koniec rozdziału przedstawiliśmy grupę bibliotek oraz frameworków.
       Stosowanie takich narzędzi nie tylko ułatwia nam pracę, lecz także pozwala na
       wprowadzenie porządku w tworzonym kodzie. Obecnie pojawia się coraz więcej
       bibliotek i frameworków przeznaczonych do użycia w aplikacjach wykorzystują-
       cych technologię Ajax, zaczynając od zbiorów kontrolek działających we wszyst-
       kich przeglądarkach, a kończąc na kompletnych rozwiązaniach wykorzystywa-
       nych zarówno po stronie klienta, jak i serwera. Pokrótce przedstawiliśmy kilka
       najpopularniejszych frameworków, a nieco więcej informacji o niektórych spośród
       nich będzie można znaleźć w dalszej części książki.
           W dwóch kolejnych rozdziałach spróbujemy zastosować zdobytą wiedzę
       o refaktoryzacji oraz wzorcach projektowych podczas tworzenia kodu JavaScript
       działającego po stronie przeglądarki, a następnie do zaimplementowania sys-
       temu komunikacji pomiędzy klientem a serwerem. Przykłady te pomogą nam
       w opracowaniu niezbędnej terminologii oraz zespołu praktycznych umiejętności
       przydatnych podczas tworzenia solidnych, wielofunkcyjnych aplikacji interne-
       towych.
142     ROZDZIAŁ 3
        Porządkowanie aplikacji wykorzystujących technologię Ajax


3.7.   Zasoby
       Martin Fowler (oraz współautorzy: Kent Beck, John Brant, William Opdyke oraz
       Don Roberts) napisał bardzo cenioną i popularną książkę dotyczącą refaktory-
       zacji: Refactoring: Improving the Design of Existing Code (Addison-Wesley Pro-
       fessional 1999).
           Erich Gamma, Richard Helm, Ralph Johnson oraz Johm Vlissides (określani
       jako „The Gang of Four”, czyli „Gang Czworga”), napisali książkę Design Patterns
       (Addison-Wesley Professional, 1995), która wywarła ogromny wpływ na środowi-
       sko projektantów oprogramowania oraz programistów.
           Erich Gamma został później architektem zajmującym się tworzeniem zin-
       tegrowanego środowiska programistycznego Eclipse (patrz dodatek A). Infor-
       macje o Eclipse oraz wzorcach projektowych można znaleźć w wywiadzie opu-
       blikowanym na stronie http:/www.artima.com/lejava/articles/gammadp.html.
           Michael Mahemoff stworzył ostatnio witrynę, http://www.ajaxpatterns.org/,
       poświęconą katalogowaniu wzorców projektowych stosowanych w aplikacjach
       wykorzystujących technologię Ajax.

More Related Content

PDF
Access. Programowanie w VBA
PDF
Visual Basic .NET. Bazy danych. Księga eksperta
PDF
Ajax. Zaawansowane programowanie
PDF
ASP.NET 2.0. Gotowe rozwiązania
PDF
ActionScript 2.0. Od podstaw
PDF
Spring Framework. Profesjonalne tworzenie oprogramowania w Javie
PDF
Spring. Zapiski programisty
PDF
Macromedia Flash 8 ActionScript. Oficjalny podręcznik
Access. Programowanie w VBA
Visual Basic .NET. Bazy danych. Księga eksperta
Ajax. Zaawansowane programowanie
ASP.NET 2.0. Gotowe rozwiązania
ActionScript 2.0. Od podstaw
Spring Framework. Profesjonalne tworzenie oprogramowania w Javie
Spring. Zapiski programisty
Macromedia Flash 8 ActionScript. Oficjalny podręcznik

Viewers also liked (20)

PPTX
Audit Doskonalący Seminarium WSB 18 05 2010
PDF
2007.01 Efektywne i intuicyjne serwisy www - Kurs usability webusability.pl
PDF
kwantologia ... Wielowymiarowa płaszczyzna.
DOC
magnetyzm
PDF
Neokatechumenat szansa czy_zagrozenie_dla_kosciola
PDF
Podatek VAT w jednostkach samorządu terytorialnego
PDF
Korpus oficerski-wojska-polskiego-w-drugiej-polowie-xvii-wieku
PPT
Szkoła mojego dziadka
PDF
Legendy i mity PR
PDF
Proces karny. część ogólna - ebook
PDF
Gut Impact
PPT
Kryształowy Pałac
PDF
Zadania samorządu terytorialnego w zakresie ochrony środowiska - ebook
PPTX
Prezentacja firmy CHEMOSERVIS-DWORY S.A.
PDF
Katalog for home
PPTX
Ppt 1. rozliczanie czasu pracy - czas i miejsce pracy
PPT
Prezentacja wolontariatu
PDF
Dietetyk 321[11] z2.04_u
PDF
2008 Polscy Emigranci W Europie
Audit Doskonalący Seminarium WSB 18 05 2010
2007.01 Efektywne i intuicyjne serwisy www - Kurs usability webusability.pl
kwantologia ... Wielowymiarowa płaszczyzna.
magnetyzm
Neokatechumenat szansa czy_zagrozenie_dla_kosciola
Podatek VAT w jednostkach samorządu terytorialnego
Korpus oficerski-wojska-polskiego-w-drugiej-polowie-xvii-wieku
Szkoła mojego dziadka
Legendy i mity PR
Proces karny. część ogólna - ebook
Gut Impact
Kryształowy Pałac
Zadania samorządu terytorialnego w zakresie ochrony środowiska - ebook
Prezentacja firmy CHEMOSERVIS-DWORY S.A.
Katalog for home
Ppt 1. rozliczanie czasu pracy - czas i miejsce pracy
Prezentacja wolontariatu
Dietetyk 321[11] z2.04_u
2008 Polscy Emigranci W Europie
Ad

Similar to Ajax w akcji (20)

PDF
Ajax. Wzorce i najlepsze rozwiązania
PDF
Ajax. Niezbędnik projektanta dynamicznych aplikacji
PDF
AJAX w mgnieniu oka
PDF
AJAX i PHP. Tworzenie interaktywnych aplikacji internetowych
PDF
Ajax. Biblia
PDF
Tworzenie stron WWW z wykorzystaniem Ajaksa. Projekty
PDF
ASP.NET 2.0. Projektowanie aplikacji internetowych
PPT
User Experience – wpływ internetu na aplikacje enterprise - Netcamp #14
PDF
CSS i Ajax. Strony WWW zgodne ze standardami sieciowymi W3C
PDF
ASP.NET. Vademecum profesjonalisty
PDF
Po prostu JavaScript i Ajax. Wydanie VI
PDF
Head First Servlets & JSP. Edycja polska
PDF
ASP.NET 2.0 i Ajax. Wprowadzenie
PDF
ASP.NET 3.5 dla programistów PHP
PDF
J2EE. Podstawy programowania aplikacji korporacyjnych
PDF
Ajax, JavaScript i PHP. Intensywny trening
PDF
Serwisy internetowe. Programowanie
PDF
Visual C# 2005. Zapiski programisty
PDF
Java. Usługi WWW. Vademecum profesjonalisty
PDF
JavaScript dla webmasterów. Zaawansowane programowanie
Ajax. Wzorce i najlepsze rozwiązania
Ajax. Niezbędnik projektanta dynamicznych aplikacji
AJAX w mgnieniu oka
AJAX i PHP. Tworzenie interaktywnych aplikacji internetowych
Ajax. Biblia
Tworzenie stron WWW z wykorzystaniem Ajaksa. Projekty
ASP.NET 2.0. Projektowanie aplikacji internetowych
User Experience – wpływ internetu na aplikacje enterprise - Netcamp #14
CSS i Ajax. Strony WWW zgodne ze standardami sieciowymi W3C
ASP.NET. Vademecum profesjonalisty
Po prostu JavaScript i Ajax. Wydanie VI
Head First Servlets & JSP. Edycja polska
ASP.NET 2.0 i Ajax. Wprowadzenie
ASP.NET 3.5 dla programistów PHP
J2EE. Podstawy programowania aplikacji korporacyjnych
Ajax, JavaScript i PHP. Intensywny trening
Serwisy internetowe. Programowanie
Visual C# 2005. Zapiski programisty
Java. Usługi WWW. Vademecum profesjonalisty
JavaScript dla webmasterów. Zaawansowane programowanie
Ad

More from Wydawnictwo Helion (20)

PDF
Tworzenie filmów w Windows XP. Projekty
PDF
Blog, więcej niż internetowy pamiętnik
PDF
Access w biurze i nie tylko
PDF
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
PDF
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
PDF
Microsoft Visual C++ 2008. Tworzenie aplikacji dla Windows
PDF
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
PDF
Makrofotografia. Magia szczegółu
PDF
Windows PowerShell. Podstawy
PDF
Java. Efektywne programowanie. Wydanie II
PDF
JavaScript. Pierwsze starcie
PDF
PowerPoint 2007 PL. Seria praktyk
PDF
Excel 2007 PL. Seria praktyk
PDF
Access 2007 PL. Seria praktyk
PDF
Word 2007 PL. Seria praktyk
PDF
Serwisy społecznościowe. Budowa, administracja i moderacja
PDF
AutoCAD 2008 i 2008 PL
PDF
Bazy danych. Pierwsze starcie
PDF
Inventor. Pierwsze kroki
PDF
Serwer SQL 2008. Administracja i programowanie
Tworzenie filmów w Windows XP. Projekty
Blog, więcej niż internetowy pamiętnik
Access w biurze i nie tylko
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
Microsoft Visual C++ 2008. Tworzenie aplikacji dla Windows
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
Makrofotografia. Magia szczegółu
Windows PowerShell. Podstawy
Java. Efektywne programowanie. Wydanie II
JavaScript. Pierwsze starcie
PowerPoint 2007 PL. Seria praktyk
Excel 2007 PL. Seria praktyk
Access 2007 PL. Seria praktyk
Word 2007 PL. Seria praktyk
Serwisy społecznościowe. Budowa, administracja i moderacja
AutoCAD 2008 i 2008 PL
Bazy danych. Pierwsze starcie
Inventor. Pierwsze kroki
Serwer SQL 2008. Administracja i programowanie

Ajax w akcji

  • 1. IDZ DO PRZYK£ADOWY ROZDZIA£ SPIS TREœCI Ajax w akcji Autorzy: Dave Crane, Eric Pascarello, Darren James ISBN: 83-246-0495-2 KATALOG KSI¥¯EK Tytu³ orygina³u: Ajax in Action Format: B5, stron: 712 KATALOG ONLINE ZAMÓW DRUKOWANY KATALOG TWÓJ KOSZYK Praktyczne zastosowania technologii, która zrewolucjonizowa³a aplikacje sieciowe • Podstawy Ajaksa DODAJ DO KOSZYKA • Metodologie tworzenia aplikacji internetowych • Optymalizacja i zabezpieczanie aplikacji Ajax to technologia tworzenia aplikacji i witryn WWW, która zyskuje coraz wiêksze CENNIK I INFORMACJE uznanie wœród programistów. Dziêki po³¹czeniu jêzyków JavaScript i XML z asynchroniczn¹ komunikacj¹ miêdzy przegl¹dark¹ i serwerem twórcom tej technologii ZAMÓW INFORMACJE uda³o siê wyeliminowaæ podstawow¹ wadê aplikacji bazuj¹cych na przegl¹darce O NOWOœCIACH internetowej, czyli koniecznoœæ oczekiwania na „prze³adowanie” strony. Ajax sprawia, ¿e niezbêdne dane s¹ pobierane w tle. Zastosowanie tej technologii powoduje, ¿e aplikacje ZAMÓW CENNIK sieciowe pod wzglêdem obs³ugi coraz bardziej przypominaj¹ tradycyjne programy. Efekty dzia³ania Ajaksa mo¿na zaobserwowaæ na coraz wiêkszej iloœci witryn WWW. „Ajax w akcji” to praktyczny przewodnik po zagadnieniach zwi¹zanych z projektowaniem CZYTELNIA witryn i aplikacji WWW w tej technologii. Czytaj¹c go, poznasz genezê Ajaksa i podstawy korzystania z niego. Nauczysz siê stosowaæ wzorce projektowe, model MVC FRAGMENTY KSI¥¯EK ONLINE oraz frameworki i biblioteki wspomagaj¹ce tworzenie witryn WWW. Przeczytasz o profesjonalnych technikach budowania aplikacji sieciowych i zastosujesz je w praktyce, pisz¹c w³asne systemy. Znajdziesz tu równie¿ informacje o optymalizowaniu wydajnoœci aplikacji oraz tworzeniu mechanizmów zabezpieczeñ. Dziêki przyk³adom zgromadzonym w kolejnych rozdzia³ach ksi¹¿ki, takim jak dynamiczna lista rozwijana, mechanizm podpowiedzi, rozbudowany portal sieciowy oraz interfejsy u¿ytkownika, poznasz praktyczne aspekty stosowania Ajaksa w projektach. • Podstawowe zastosowania Ajaksa • Dostosowywanie istniej¹cych aplikacji do Ajaksa • Wzorzec MVC w aplikacjach • Biblioteki i frameworki wspomagaj¹ce pracê nad aplikacjami Wydawnictwo Helion • Oddzielanie logiki od widoku ul. Koœciuszki 1c • Wymiana danych pomiêdzy przegl¹dark¹ i serwerem 44-100 Gliwice • Zabezpieczanie aplikacji tel. 032 230 98 63 • Optymalizacja wydajnoœci e-mail: helion@helion.pl Twórz aplikacje, które bêd¹ nie tylko funkcjonalne, ale równie¿ wygodne w obs³udze
  • 2. Spis treści Przedmowa 13 Podziękowania 15 O książce 19 Część I Reorganizacja pojęcia aplikacji internetowej 27 1. Nowe projektowanie dla serwisów internetowych 29 1.1. Dlaczego mówi się o wzbogaconych klientach ajaksowych? ............................................ 30 1.1.1. Porównanie odczuć użytkowników .......................................................................... 31 1.1.2. Opóźnienia sieci ........................................................................................................ 35 1.1.3. Interakcje asynchroniczne ........................................................................................ 37 1.1.4. Długotrwałe i chwilowe wzorce użytkowania ......................................................... 40 1.1.5. „Oduczanie się” internetu ......................................................................................... 41 1.2. Cztery zasady definiujące Ajaksa ....................................................................................... 42 1.2.1. Przeglądarka przechowuje aplikację, nie zawartość ................................................ 42 1.2.2. Serwer dostarcza dane, nie zawartość ...................................................................... 44 1.2.3. Interakcja użytkownika z aplikacją może być płynna i ciągła ................................. 46 1.2.4. To jest prawdziwe pisanie programów i wymaga dyscypliny ................................. 47 1.3. Wzbogacone klienty ajaksowe w rzeczywistym świecie ................................................... 48 1.3.1. Badanie terenu .......................................................................................................... 48 1.3.2. Google Maps .............................................................................................................. 49 1.4. Alternatywy dla Ajaksa ........................................................................................................ 52 1.4.1. Rozwiązania oparte na Macromedia Flash .............................................................. 52 1.4.2. Java Web Start i związane z nią technologie ........................................................... 53
  • 3. 6 Spis treści 1.5. Podsumowanie ...................................................................................................................... 53 1.6. Zasoby .................................................................................................................................... 54 2. Pierwsze kroki z Ajaksem 55 2.1. Kluczowe elementy Ajaksa .................................................................................................. 56 2.2. Sprawianie dobrego wrażenia na użytkownikach za pomocą JavaScriptu .................... 58 2.3. Definiowanie wyglądu i zachowania przy użyciu CSS ..................................................... 59 2.3.1. Selektory CSS ............................................................................................................ 60 2.3.2. Właściwości stylów CSS ........................................................................................... 62 2.3.3. Prosty przykład CSS .................................................................................................. 63 2.4. Organizowanie widoku za pomocą DOM .......................................................................... 68 2.4.1. Praca z DOM przy użyciu JavaScriptu .................................................................... 70 2.4.2. Znajdowanie węzła DOM ......................................................................................... 72 2.4.3. Tworzenie węzła DOM ............................................................................................. 73 2.4.4. Dodawanie stylów do dokumentu ............................................................................ 74 2.4.5. Droga na skróty: użycie właściwości innerHTML .................................................. 75 2.5. Asynchroniczne ładowanie danych przy użyciu technologii XML ................................. 76 2.5.1. <iframe> ................................................................................................................... 77 2.5.2. Obiekty XmlDocument i XMLHttpRequest ........................................................... 79 2.5.3. Wysyłanie żądań do serwera ..................................................................................... 81 2.5.4. Używanie funkcji zwrotnych do monitorowania żądania ........................................ 83 2.5.5. Pełny cykl życia ......................................................................................................... 84 2.6. Czym wyróżnia się Ajax? ...................................................................................................... 87 2.7. Podsumowanie ...................................................................................................................... 89 2.8. Zasoby .................................................................................................................................... 90 3. Porządkowanie aplikacji wykorzystujących technologię Ajax 91 3.1. Porządkowanie chaosu ......................................................................................................... 93 3.1.1. Wzorce: tworzenie wspólnego słownictwa .............................................................. 93 3.1.2. Refaktoryzacja a Ajax ................................................................................................. 94 3.1.3. Zachowanie właściwych proporcji ........................................................................... 95 3.1.4. Refaktoryzacja w działaniu ....................................................................................... 96 3.2. Niewielkie przykłady refaktoryzacji ................................................................................... 99 3.2.1. Niezgodności przeglądarek: wzorce Façade oraz Adapter ..................................... 99 3.2.2. Zarządzanie procedurami obsługi zdarzeń: wzorzec Observer ............................ 103 3.2.3. Wielokrotne stosowanie funkcji obsługujących działania użytkownika: wzorzec Command ........................................................... 107 3.2.4. Przechowywanie tylko jednej referencji do zasobu: wzorzec Singleton .............. 110 3.3. Model-View-Controller ...................................................................................................... 114 3.4. Wzorzec MVC stosowany na serwerze ............................................................................. 117 3.4.1. Serwerowa część aplikacji tworzona bez użycia wzorców .................................... 117 3.4.2. Refaktoryzacja modelu dziedziny ........................................................................... 121 3.4.3. Separacja zawartości od prezentacji ....................................................................... 124 3.5. Biblioteki i frameworki ...................................................................................................... 128 3.5.1. Biblioteki zapewniające poprawne działanie skryptów w różnych przeglądarkach ......128 3.5.2. Kontrolki oraz bazujące na nich szkielety .............................................................. 133 3.5.3. Frameworki aplikacji ............................................................................................... 136 3.6. Podsumowanie .................................................................................................................... 140 3.7. Zasoby .................................................................................................................................. 142
  • 4. Spis treści 7 Część II Podstawowe techniki 143 4. Strona WWW jako aplikacja 145 4.1. Inny rodzaj wzorca MVC ................................................................................................... 146 4.1.1. Powielanie wzorca w różnych skalach ................................................................... 146 4.1.2. Stosowanie wzorca MVC w przeglądarce .............................................................. 147 4.2. Widoki w aplikacjach Ajax ................................................................................................ 150 4.2.1. Oddzielanie logiki od widoku ................................................................................. 150 4.2.2. Oddzielanie widoku od logiki ................................................................................. 156 4.3. Kontroler w aplikacjach wykorzystujących technologię Ajax ........................................ 160 4.3.1. Klasyczne procedury obsługi zdarzeń .................................................................... 160 4.3.2. Model obsługi zdarzeń W3C .................................................................................. 164 4.3.3. Implementacja elastycznego modelu obsługi zdarzeń w języku JavaScript ........ 165 4.4. Model w aplikacjach wykorzystujących technologię Ajax ............................................. 170 4.4.1. Zastosowanie JavaScriptu do stworzenia modelu dziedziny biznesowej ............. 171 4.4.2. Wymiana danych z serwerem ................................................................................. 173 4.5. Generacja widoku na podstawie modelu ......................................................................... 175 4.5.1. Refleksja obiektów JavaScript ................................................................................ 175 4.5.2. Obsługa tablic i obiektów ....................................................................................... 179 4.5.3. Dodawanie kontrolera ............................................................................................. 182 4.6. Podsumowanie .................................................................................................................... 185 4.7. Zasoby .................................................................................................................................. 186 5. Rola serwera 187 5.1. Współpraca z aplikacjami działającymi na serwerze ..................................................... 188 5.2. Tworzenie aplikacji działającej po stronie serwera ........................................................ 189 5.2.1. Najczęściej stosowane języki programowania ....................................................... 189 5.2.2. Architektury n-warstwowe ...................................................................................... 190 5.2.3. Utrzymanie modeli dziedziny po stronie klienta i serwera ................................ 191 5.3. Postać ogólna: najczęściej stosowane projekty aplikacji wykonywanych na serwerze ........ 193 5.3.1. Naiwne tworzenie kodu działającego na serwerze bez stosowania żadnego frameworka ................................................. 193 5.3.2. Korzystanie z frameworków Model2 ...................................................................... 195 5.3.3. Frameworki bazujące na komponentach ............................................................... 196 5.3.4. Stosowanie architektur bazujących na usługach ................................................... 199 5.4. Informacje szczegółowe: wymiana danych ...................................................................... 204 5.4.1. Interakcje klienckie ................................................................................................. 205 5.4.2. Prezentacja przykładowej aplikacji przeglądarki planet ........................................... 205 5.4.3. Myśl jak strona WWW — interakcje operujące na zawartości ............................ 208 5.4.4. Myśl jak plugin — interakcje operujące na skryptach .......................................... 212 5.4.5. Myśl jak aplikacja — interakcje operujące na danych .......................................... 218 5.5. Przekazywanie danych na serwer ..................................................................................... 223 5.5.1. Stosowanie formularzy HTML ............................................................................... 224 5.5.2. Stosowanie obiektu XMLHttpRequest .................................................................. 226 5.5.3. Efektywna obsługa aktualizacji danych ................................................................. 227 5.6. Podsumowanie .................................................................................................................... 236 5.7. Zasoby .................................................................................................................................. 237
  • 5. 8 Spis treści Część III Profesjonalny Ajax 239 6. Wrażenia użytkownika 241 6.1. Róbmy to dobrze — pisanie dobrych aplikacji ............................................................... 242 6.1.1. Łatwość i szybkość reakcji ...................................................................................... 243 6.1.2. Solidność .................................................................................................................. 244 6.1.3. Konsekwencja .......................................................................................................... 244 6.1.4. Prostota .................................................................................................................... 245 6.1.5. Zapewnienie działania ............................................................................................. 246 6.2. Informowanie użytkownika ............................................................................................... 246 6.2.1. Obsługa odpowiedzi na nasze żądania ................................................................... 247 6.2.2. Obsługa modyfikacji wprowadzanych przez inne osoby ...................................... 248 6.3. Projektowanie systemu powiadomień dla aplikacji Ajax ............................................... 253 6.3.1. Modelowanie powiadomień .................................................................................... 254 6.3.2. Definiowanie wymagań interfejsu .......................................................................... 256 6.4. Implementacja frameworka do obsługi powiadomień ................................................... 257 6.4.1. Prezentacja ikony na pasku stanu ........................................................................... 257 6.4.2. Prezentacja szczegółów powiadomienia ................................................................ 260 6.4.3. Scalanie poszczególnych elementów systemu ....................................................... 261 6.5. Zastosowanie frameworka podczas obsługi żądań sieciowych ...................................... 267 6.6. Sygnalizacja aktualności danych ....................................................................................... 271 6.6.1. Zdefiniowanie prostego sposobu wyróżniania ....................................................... 272 6.6.2. Wyróżnianie przy użyciu biblioteki efektów Scriptaculous .................................. 274 6.7. Podsumowanie .................................................................................................................... 275 6.8. Zasoby .................................................................................................................................. 275 7. Bezpieczeństwo a Ajax 277 7.1. Bezpieczeństwo języka JavaScript i przeglądarki ........................................................... 278 7.1.1. Wprowadzanie polityki „serwera pochodzenia” .................................................... 279 7.1.2. Problemy do rozważenia w aplikacjach Ajax ......................................................... 280 7.1.3. Problemy z poddomenami ...................................................................................... 280 7.1.4. Bezpieczeństwo w różnych przeglądarkach .......................................................... 281 7.2. Komunikacja ze zdalnymi usługami ................................................................................. 283 7.2.1. Pośredniczenie w korzystaniu ze zdalnych usług .................................................. 283 7.2.2. Korzystanie z usług WWW ..................................................................................... 284 7.3. Ochrona poufnych danych ................................................................................................. 295 7.3.1. Atak „man in the middle” ........................................................................................ 295 7.3.2. Stosowanie bezpiecznego protokołu HTTP .......................................................... 296 7.3.3. Użycie JavaScriptu do szyfrowania danych przesyłanych protokołem HTTP .... 297 7.4. Zabezpieczanie transmisji danych w aplikacjach wykorzystujących technologię Ajax ....... 300 7.4.1. Projektowanie bezpiecznej warstwy internetowej ................................................ 301 7.4.2. Ograniczanie dostępu do danych ........................................................................... 305 7.5. Podsumowanie .................................................................................................................... 310 7.6. Zasoby .................................................................................................................................. 310 8. Wydajność 313 8.1. Czym jest wydajność? ......................................................................................................... 314 8.2. Szybkość wykonywania kodu JavaScript .......................................................................... 315 8.2.1. Profilowanie aplikacji w najprostszy możliwy sposób ........................................... 316 8.2.2. Użycie programu profilującego Venkman ............................................................. 322 8.2.3. Optymalizacja szybkości wykonywania kodu JavaScript ........................................... 324
  • 6. Spis treści 9 8.3. Wymagania pamięciowe kodu JavaScript ....................................................................... 336 8.3.1. Zapobieganie „wyciekom” pamięci ........................................................................ 337 8.3.2. Zagadnienia odnoszące się do technologii Ajax ..................................................... 341 8.4. Projektowanie pod kątem wydajności .............................................................................. 346 8.4.1. Mierzenie zajętości pamięci ................................................................................... 346 8.4.2. Prosty przykład ........................................................................................................ 351 8.4.3. Wyniki: jak 150-krotnie zmniejszyć zużycie pamięci? .......................................... 355 8.5. Podsumowanie .................................................................................................................... 358 8.6. Zasoby .................................................................................................................................. 358 Część IV Ajax w przykładach 361 9. Podwójna dynamiczna lista rozwijalna 363 9.1. Skrypt podwójnej listy rozwijalnej ................................................................................... 364 9.1.1. Ograniczenia rozwiązania działającego po stronie klienta .................................... 364 9.1.2. Ograniczenia rozwiązania działającego po stronie serwera .................................. 365 9.1.3. Rozwiązanie wykorzystujące Ajaksa ....................................................................... 366 9.2. Architektura warstwy klienta ............................................................................................ 367 9.2.1. Projektowanie formularza ....................................................................................... 368 9.2.2. Projektowanie interakcji klient-serwer .................................................................. 369 9.3. Implementacja serwera — VB.NET ................................................................................ 371 9.3.1. Definiowanie formatu odpowiedzi XML ............................................................... 372 9.3.2. Pisanie kodu wykonywanego na serwerze ............................................................. 372 9.4. Przetwarzanie wyników ..................................................................................................... 375 9.4.1. Przeglądanie dokumentu XML .............................................................................. 376 9.4.2. Zastosowanie kaskadowych arkuszy stylów ........................................................... 378 9.5. Zagadnienia zaawansowane .............................................................................................. 379 9.5.1. Zapewnienie możliwości wyboru większej ilości opcji ......................................... 380 9.5.2. Zamiana listy podwójnej na potrójną ..................................................................... 381 9.6. Refaktoring .......................................................................................................................... 382 9.6.1. Nowy i poprawiony komponent net.ContentLoader ............................................ 383 9.6.2. Tworzenie komponentu podwójnej listy rozwijalnej ............................................ 389 9.7. Podsumowanie .................................................................................................................... 396 10. Sugestie prezentowane podczas wpisywania 399 10.1. Zastosowania i cechy mechanizmu prezentacji sugestii ................................................. 400 10.1.1. Cechy mechanizmów prezentacji sugestii ............................................................. 400 10.1.2. Google Suggest ........................................................................................................ 402 10.1.3. Autorski mechanizm prezentowania sugestii ......................................................... 403 10.2. Skrypt wykonywany na serwerze — C# .......................................................................... 404 10.2.1. Serwer oraz baza danych ......................................................................................... 404 10.2.2. Testowanie kodu wykonywanego na serwerze ...................................................... 407 10.3. Framework działający po stronie klienta ........................................................................ 408 10.3.1. HTML ...................................................................................................................... 408 10.3.2. JavaScript ................................................................................................................. 409 10.3.3. Pobieranie danych z serwera .................................................................................. 419 10.4. Rozbudowa możliwości funkcjonalnych: więcej elementów używających różnych zapytań ...................................................... 431
  • 7. 10 Spis treści 10.5. Refaktoring .......................................................................................................................... 432 10.5.1. Dzień 1. Określenie planu prac nad komponentem TextSuggest ........................ 433 10.5.2. Dzień 2. Tworzenie komponentu TextSuggest — „eleganckiego” i konfigurowalnego .................................................................................................. 437 10.5.3. Dzień 3. Zastosowanie Ajaksa ................................................................................. 441 10.5.4. Dzień 4. Obsługa zdarzeń ....................................................................................... 446 10.5.5. Dzień 5. Interfejs użytkownika listy sugestii ......................................................... 453 10.5.6. Podsumowanie prac nad komponentem ................................................................ 462 10.6. Podsumowanie .................................................................................................................... 462 11. Rozbudowany portal wykorzystujący Ajaksa 463 11.1. Ewolucja portali .................................................................................................................. 464 11.1.1. Klasyczne portale ..................................................................................................... 465 11.1.2. Portale o zaawansowanym interfejsie użytkownika ............................................... 465 11.2. Architektura portalu używającego Ajaksa i Javy ............................................................ 467 11.3. Logowanie wykorzystujące Ajaksa ................................................................................... 469 11.3.1. Tabela użytkowników .............................................................................................. 470 11.3.2. Kod obsługujący logowanie na serwerze: Java ...................................................... 471 11.3.3. Mechanizm logowania działający po stronie klienta ............................................. 474 11.4. Implementacja okien DHTML ......................................................................................... 480 11.4.1. Baza danych okien ................................................................................................... 480 11.4.2. Kod obsługi okien działający na serwerze .............................................................. 482 11.4.3. Dodawanie zewnętrznej biblioteki JavaScript ....................................................... 486 11.5. Implementacja automatycznego zapisu właściwości ...................................................... 489 11.5.1. Dostosowanie biblioteki .......................................................................................... 489 11.5.2. Automatyczny zapis informacji w bazie danych .................................................... 491 11.6. Refaktoring .......................................................................................................................... 494 11.6.1. Definiowanie konstruktora ...................................................................................... 497 11.6.2. Dostosowanie biblioteki AjaxWindows.js .............................................................. 497 11.6.3. Określanie poleceń obsługiwanych przez portal ................................................... 500 11.6.4. Wykorzystanie technologii Ajax .............................................................................. 504 11.6.5. Podsumowanie prac nad komponentem ................................................................ 505 11.7. Podsumowanie .................................................................................................................... 506 12. Dynamiczne wyszukiwanie przy użyciu XSLT 507 12.1. Analiza technik wyszukiwania ........................................................................................... 508 12.1.1. Klasyczne rozwiązania wyszukiwania ..................................................................... 509 12.1.2. Wady metody wykorzystującej ramki i wyskakujące okienka ............................... 510 12.1.3. Analiza dynamicznego wyszukiwania przy użyciu technologii Ajax i XSLT ........ 512 12.1.4. Przesyłanie wyników do klienta .............................................................................. 513 12.2. Kod klienta .......................................................................................................................... 514 12.2.1. Konfiguracja klienta ................................................................................................. 515 12.2.2. Rozpoczęcie procesu ............................................................................................... 516 12.3. Kod działający na serwerze — PHP ................................................................................. 518 12.3.1. Generacja dokumentu XML ................................................................................... 518 12.3.2. Tworzenie dokumentu XSLT ................................................................................. 521 12.4. Łączenie dokumentów XML i XSLT ................................................................................ 523 12.4.1. Przekształcenia XSLT w Internet Explorerze ....................................................... 525 12.4.2. Przekształcenia XSLT w przeglądarkach Mozilla i Firefox .................................. 526
  • 8. Spis treści 11 12.5. Dopracowanie wyszukiwania ............................................................................................ 527 12.5.1. Zastosowanie CSS .................................................................................................... 528 12.5.2. Usprawnianie wyszukiwania ................................................................................... 529 12.5.3. Podejmowanie decyzji o zastosowaniu XSLT ........................................................ 531 12.5.4. Rozwiązanie problemu zakładek ............................................................................ 533 12.6. Refaktoring .......................................................................................................................... 534 12.6.1. XSLTHelper ............................................................................................................ 535 12.6.2. Komponent dynamicznego wyszukiwania ............................................................. 539 12.6.3. Podsumowanie prac nad komponentem ................................................................ 544 12.7. Podsumowanie .................................................................................................................... 544 13. Tworzenie niezależnych aplikacji wykorzystujących technologię Ajax 547 13.1. Odczytywanie informacji ze świata .................................................................................. 548 13.1.1. Poszukiwanie kanałów XML ................................................................................... 549 13.1.2. Struktura formatu RSS ............................................................................................ 550 13.2. Tworzenie bogatego interfejsu użytkownika ................................................................... 553 13.2.1. Proces ....................................................................................................................... 554 13.2.2. Strona HTML bez tabel .......................................................................................... 555 13.2.3. Formatowanie przy użyciu CSS ............................................................................. 557 13.3. Pobieranie zawartości kanałów RSS ................................................................................. 563 13.3.1. Zasięg globalny ........................................................................................................ 563 13.3.2. Wczytywanie danych ............................................................................................... 565 13.4. Utworzenie wizualnego efektu przejścia .......................................................................... 569 13.4.1. Nieprzezroczystość w różnych przeglądarkach ..................................................... 569 13.4.2. Implementacja efektu przejścia .............................................................................. 570 13.4.3. Zastosowanie liczników czasu ................................................................................. 572 13.5. Dodatkowe możliwości ....................................................................................................... 574 13.5.1. Dodawanie kolejnych kanałów ............................................................................... 575 13.5.2. Implementacja możliwości przechodzenia do innych artykułów i wstrzymywania czytnika .................................................... 577 13.6. Unikanie ograniczeń projektu ........................................................................................... 579 13.6.1. Problemy z systemem zabezpieczeń przeglądarek fundacji Mozilla .................... 580 13.6.2. Zmiana zakresu aplikacji ......................................................................................... 583 13.7. Refaktoring ......................................................................................................................... 584 13.7.1. Model używany w czytniku RSS ............................................................................ 584 13.7.2. Widok stosowany w czytniku RSS .......................................................................... 587 13.7.3. Kontroler czytnika RSS ........................................................................................... 591 13.7.4. Podsumowanie wprowadzonych zmian .................................................................. 605 13.8. Podsumowanie .................................................................................................................... 605 Część V Dodatki 607 A Przybornik programisty aplikacji Ajax 609 B JavaScript dla programistów obiektowych 639 C Frameworki i biblioteki ułatwiające stosowanie technologii Ajax 671 Skorowidz 691
  • 9. Porządkowanie aplikacji wykorzystujących technologię Ajax Zagadnienia opisywane w tym rozdziale: ♦ Tworzenie i pielęgnacja rozbudowanego kodu wykorzystującego możliwości technologii Ajax. ♦ Refaktoryzacja kodu JavaScript wykorzystującego technologię Ajax. ♦ Wzorce projektowe najczęściej używane w aplikacjach wykorzystujących Ajaksa. ♦ Stosowanie wzorca projektowego MVC w aplikacjach wykonywanych po stronie serwera. ♦ Przegląd dostępnych bibliotek ułatwiających korzystanie z technologii Ajax.
  • 10. 92 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax W rozdziale 2. opisaliśmy wszystkie podstawowe technologie, które ogólnie określamy jako Ajax. Dysponując wiedzą, jaką już zdobyliśmy, możemy napi- sać niesamowitą aplikację wykorzystującą technologię Ajax, o jakiej zawsze ma- rzyliśmy. Równie dobrze możemy jednak wpaść w bardzo poważne kłopoty i skończyć na całej masie złożonego kodu JavaScript, HTML i arkuszy stylów, których utrzymanie i pielęgnacja są praktycznie niemożliwe i który, z jakichś całkowicie niewyjaśnionych przyczyn, pewnego dnia może przestać działać. Albo może być jeszcze gorzej — możemy stworzyć aplikację działającą tak długo, jak długo nie będziemy przy niej nic robić, oddychać ani wydawać żadnych gło- śniejszych dźwięków. Znalezienie się w takiej sytuacji podczas prac nad naszym własnym, prywatnym projektem może być jedynie zniechęcające. Jeśli jednak znajdziemy się w takiej sytuacji podczas modyfikacji witryny pracodawcy lub klienta, który płaci za jej utrzymanie i zażądał właśnie wprowadzenia kilku zmian tu i tam, to bez wątpienia będzie to przerażające. Na szczęście takie problemy występowały powszechnie już od samego po- czątku ery komputerów, a można sądzić, że nawet wcześniej! Wiele osób łamało sobie głowy nad znalezieniem sposobu zarządzania złożonością oraz utrzymaniem coraz większych ilości kodu źródłowego w takim porządku, który umożliwiałby jego dalsze rozwijanie i pielęgnację. W tym rozdziale przedstawimy podstawowe narzędzia, które pozwolą nam zachować orientację w tym, co dzieje się w naszym kodzie, pisać aplikacje wykorzystujące technologię Ajax, jakie spełnią oczeki- wania naszych klientów, a jednocześnie sprawią, że będziemy mogli wracać do domu o normalnej porze. Ajax stanowi przełom w porównaniu z wcześniej stosowanymi technologiami DHTML i to nie tylko pod względem sposobu połączenia poszczególnych tech- nologii, lecz także skali, na jaką są one stosowane. Aplikacje wykorzystujące technologię Ajax w znacznie większym stopniu używają skryptów JavaScript, a ich kod jest przechowywany w przeglądarce znacznie dłużej. W efekcie muszą one rozwiązywać znacznie poważniejsze problemy ze złożonością, które nie wystę- powały we wcześniejszych aplikacjach DHTML. W tym rozdziale przedstawimy narzędzia i techniki, które pozwolą Czytel- nikowi zachować przejrzystość i prostotę tworzonego kodu. Z naszego doświad- czenia wynika, iż są one najbardziej skuteczne w przypadku tworzenia dużych i złożonych aplikacji wykorzystujących technologię Ajax. W przypadku pisania prostych aplikacji sugerujemy pominąć tę część książki i przejść bezpośrednio do rozdziałów prezentujących konkretne rozwiązania, czyli do rozdziału 9. i kolej- nych. Jeśli Czytelnik już doskonale zna zasady refaktoryzacji oraz wszystkie wzorce projektowe, to może pominąć ten rozdział i zająć się lekturą rozdziałów opisujących zastosowanie tych technik w aplikacjach wykorzystujących technolo- gię Ajax (czyli w rozdziałach od 4. do 6.). Niemniej jednak podstawowe informacje, jakie prezentujemy w tym rozdziale, mają duże znaczenie dla zapewniania wysokiej jakości kodu JavaScript, dlatego też przypuszczamy, że wcześniej lub później Czytelnik i tak tu zajrzy. Skorzystamy także z nadarzającej się sposobności i pod koniec rozdziału przedstawimy dostępne biblioteki ułatwiające korzysta- nie z technologii Ajax; a zatem jeśli Czytelnik szuka biblioteki lub frameworka, który mógłby mu ułatwić pracę nad projektem, to może zajrzeć do podroz- działu 3.5.
  • 11. 3.1. Porządkowanie chaosu 93 3.1. Porządkowanie chaosu Podstawowym narzędziem, jakie zastosujemy, będzie tzw. refaktoryzacja — czyli proces polegający na przepisywaniu kodu lub wprowadzaniu w nim modyfikacji, które nie mają na celu rozbudowy jego możliwości funkcjonalnych, lecz popra- wienie jego przejrzystości. Poprawianie przejrzystości samo w sobie może być doskonałym powodem do wprowadzania zmian w kodzie, niemniej jednak ma ono także i inne zalety, które powinny przekonać każdego. Zazwyczaj znacznie łatwiej jest dodawać nowe możliwości do opracowanego wcześniej kodu, modyfikować już zaimplementowane lub usuwać je. Najpro- ściej rzecz ujmując, takie operacje są zrozumiałe. Jeśli jednak kod aplikacji nie zostanie dobrze opracowany i zaprojektowany, to chociaż będzie on poprawnie funkcjonował i spełniał wymagania zleceniodawcy, to jednak osoby pracujące nad nim mogą nie do końca rozumieć, jak on działa oraz dlaczego działa poprawnie. Zmiany wymagań aplikacji, często połączone z krótkim okresem realizacji, są przykrą codziennością dla programistów. Refaktoryzacja pozwala na zapew- nienie przejrzystości kodu, ułatwia jego utrzymanie i pozwala, by perspektywa implementacji zmian w założeniach projektu nie napawała nas przerażeniem. Podstawowe sposoby refaktoryzacji poznaliśmy już w rozdziale 2., przy okazji przenoszenia kodu JavaScript, HTML oraz definicji stylów do osobnych plików. Jednak kod JavaScript staje się już długi, gdy objętość pliku przekroczy około 120 wierszy oraz gdy zaczniemy w nim stosować możliwości funkcjonalne niskiego poziomu (takie jak generowanie żądań i przesyłanie ich na zdalny serwer) z kodem operującym na obiektach. Kiedy zaczniemy tworzyć większe projekty, umiesz- czanie całego kodu JavaScript w jednym pliku przestanie być dobrym rozwią- zaniem (to samo zresztą dotyczy arkuszy stylów). Cel, do którego dążymy — two- rzenie niewielkich, przejrzystych, zrozumiałych i łatwych do modyfikacji fragmen- tów kodu obsługujących konkretne zagadnienia — jest często określany jako separacja obowiązków (ang. separation of responsibility). Jednak refaktoryzacja jest często przeprowadzana także z innego powodu, pozwala ona bowiem na wskazanie często stosowanych rozwiązań, sposobów realizacji konkretnych zadań i tworzenie kodu na podstawie określonych wzorców. Także te cele są dostateczną motywacją do wprowadzania zmian w kodzie, a dąże- nie do nich ma bardzo praktyczne konsekwencje. Przedstawimy je w kolejnym punkcie rozdziału. 3.1.1. Wzorce: tworzenie wspólnego słownictwa Prawdopodobieństwo, że kod spełniający założenia jakiegoś dobrze znanego i powszechnie stosowanego wzorca będzie działać poprawnie, jest bardzo duże. Wynika to z prostego faktu, iż taki kod był już wielokrotnie tworzony i stosowany. Wiele problemów, jakie występują podczas tworzenia konkretnego rozwiązania zostało już dokładnie przemyślanych i, miejmy nadzieję, skutecznie rozwiąza- nych. A jeśli mamy szczęście, to może się okazać, że ktoś już napisał nadający się do wielokrotnego stosowania kod, realizujący dokładnie te możliwości funkcjo- nalne, o jakie nam chodzi.
  • 12. 94 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax Taki sposób wykonywania konkretnego zadania nazywamy wzorcem projek- towym. Pojęcie wzorca pojawiło się w latach 70. ubiegłego wieku w odniesie- niu do sposobów rozwiązywania problemów występujących w architekturze i pla- nowaniu. Później zapożyczono je także do świata programowania komputerów, gdzie z powodzeniem jest stosowane od blisko dziesięciu lat. Wzorce projek- towe są bardzo popularne w środowisku programistów wykorzystujących język Java, a ostatnio firma Microsoft usilnie stara się wprowadzić je także w platformie .NET. Termin „wzorzec projektowy” bardzo często jest kojarzony z czymś aka- demickim, teoretycznym i odpychającym; równie często nadużywa się go lub stosuje błędnie, by wywrzeć większe wrażenie na rozmówcach. Jednak wzo- rzec projektowy jest jedynie opisem sposobu rozwiązania pewnego konkretne- go problemu, który bardzo często występuje podczas projektowania i tworzenia oprogramowania. Warto zauważyć, iż wzorce projektowe określają i nadają na- zwy abstrakcyjnym, technicznym rozwiązaniom, dzięki czemu nie tylko łatwiej można o nich rozmawiać, lecz także łatwiej jest je zrozumieć. Wzorce projektowe mogą być ważne w kontekście refaktoryzacji kodu, gdyż pozwalają nam na zwięzłe i precyzyjne opisanie celów, do jakich dążymy. Stwier- dzenie, iż chodzi nam o „zaimplementowanie, w formie obiektów tych fragmen- tów kodu, które realizują proces obsługi konkretnej czynności wykonywanej przez użytkownika” jest długie, złożone i raczej trudno się na nim skoncentro- wać podczas wprowadzania zmian w kodzie. Jeśli jednak stwierdzimy, że chcemy zastosować wzorzec projektowy Command, to nasz cel nie tylko będzie bardziej precyzyjnie określony, lecz także łatwiej będzie nam o nim rozmawiać. Jeśli Czytelnik zna się na pisaniu w Javie programów wykonywanych po stronie serwera bądź jest architektem oprogramowania, który zna się na rzeczy, to zapewne zacznie się zastanawiać, cóż takiego odkrywczego jest w tym, co przed chwilą napisaliśmy. Z kolei, jeśli Czytelnik wywodzi się z kręgów projektantów stron WWW lub osób zajmujących się nowymi rodzajami mediów, to pewnie pomyśli, że jesteśmy tymi natchnionymi dziwakami, którzy wolą rysowanie śmiesz- nych schematów i diagramów od tworzenia kodu. Jednak niezależnie od tego, kim jest Czytelnik i jakie ma doświadczenia, to mógł pomyśleć, co to wszystko ma wspólnego z aplikacjami wykorzystującymi technologię Ajax. Otóż ma, i to całkiem sporo. Przekonajmy się zatem, co programista tworzący aplikacje wyko- rzystujące Ajaksa może zyskać dzięki refaktoryzacji. 3.1.2. Refaktoryzacja a Ajax Wspominaliśmy już, że aplikacje wykorzystujące technologię Ajax będą zazwy- czaj zawierać więcej kodu JavaScript oraz że ten kod będzie dłużej działać w śro- dowisku przeglądarki niż w tradycyjnych aplikacjach internetowych. W tradycyjnych aplikacjach internetowych najbardziej złożony kod jest wy- konywany na serwerze, a wzorce projektowe są używane przy pisaniu skryptów i aplikacji w językach PHP, Java oraz językach używanych na platformie .NET. Jednak w aplikacjach wykorzystujących technologię Ajax te same techniki możemy zastosować podczas tworzenia kodu wykonywanego po stronie przeglądarki.
  • 13. 3.1. Porządkowanie chaosu 95 Można nawet podać argument sugerujący, iż zastosowanie takiego sposobu organizacji kodu jest znacznie bardziej potrzebne w języku JavaScript niż w języ- kach o rygorystycznej kontroli typów, takich jak Java lub C#. Pomimo tego iż składnia języka JavaScript bardzo przypomina składnię C, to jednak sam język jest znacznie bardziej podobny do języków skryptowych, takich jak Ruby, Python, bądź nawet Common Lisp niż do Javy lub C#. JavaScript zapewnia ogromną elastyczność i przestrzeń do opracowywania własnych rozwiązań i metodologii. W rękach doświadczonego programisty jest to wspaniałe narzędzie, jednak przed przeciętnymi programistami stawia trudne i niebezpieczne wyzwania. Języki najczęściej stosowane do tworzenia aplikacji korporacyjnych, takie jak Java lub C# zostały zaprojektowane w taki sposób, by ułatwiały pracę zespołów przeciętnych programistów i dawały możliwość szybkiego zdobywania nowych użytkowników. Jednak JavaScript jest zupełnie innym językiem. W przypadku stosowania JavaScriptu prawdopodobieństwo tworzenia za- wikłanego, niezrozumiałego kodu jest bardzo wysokie; a kiedy zaczniemy używać go nie tylko do tworzenia prostych sztuczek, lecz także do pisania złożonych aplikacji wykorzystujących Ajaksa, to wszystkie te zagrożenia staną się znacznie bardziej realne i zauważalne. Właśnie dlatego gorąco zachęcamy, by znacznie częściej stosować refaktoryzację w aplikacjach wykorzystujących technologię Ajax niż w aplikacjach pisanych w językach Java lub C#, czyli językach bezpiecz- nych, w przypadku których społeczność programistów już dawno opracowała i zaczęła stosować wzorce projektowe. 3.1.3. Zachowanie właściwych proporcji Zanim zajmiemy się kolejnymi zagadnieniami, koniecznie należy podkreślić, że zarówno refaktoryzacja, jak i wzorce projektowe są jedynie narzędziami i należy je stosować wyłącznie w przypadku, gdy może to zapewnić wymierne korzyści. Jeśli popadniemy w przesadę, to możemy doprowadzić do sytuacji określanej jako paraliż przez analizę (ang. paralysis by analisys), w której implementacja aplikacji jest wstrzymywana przez ciągłe zmiany projektu, mające na celu popra- wienie elastyczności struktury kodu i uwzględnienie potencjalnych zmian wy- magań, jakie mogą, lecz nie muszą, się pojawić. Errich Gamma, ekspert zajmujący się wzorcami projektowymi, bardzo traf- nie podsumował tę sytuację w jednym z ostatnich wywiadów (patrz podrozdział „Zasoby” zamieszczony na samym końcu tego rozdziału) — opowiadał w nim czytelniku, który zwrócił się do niego z prośbą o pomoc, gdyż w tworzonej aplika- cji udało mu się zaimplementować jedynie 21 z 23 wzorców projektowych opi- sanych w książce Design Patterns. Należy pamiętać, że wzorce projektowe nadają się do zastosowania wyłącznie w określonych sytuacjach, tak samo jak różne typy danych — przecież nikt przy zdrowych zmysłach nie starałby się używać wszystkich dostępnych typów danych, liczb całkowitych, zmiennoprzecinkowych, łańcuchów znaków, tablic, itd. — w każdym fragmencie kodu. Gamma stwierdza, że właśnie refaktoryzacja jest najlepszą okazją do wpro- wadzania wzorców projektowych. Najpierw należy napisać działający kod w możliwie jak najprostszy sposób, a dopiero potem starać się stosować wzorce
  • 14. 96 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax do rozwiązywania określonych problemów, jakie udało się nam wyróżnić. Jeśli Czytelnik sam jest autorem kodu lub jeśli musi „opiekować się” kodem, w którym „nabałaganił” ktoś inny, to zapewne aż do tej chwili odczuwa nieprzyjemny skurcz żołądka. Na szczęście wzorce projektowe można także stosować z po- wodzeniem w już istniejącym kodzie i to bez względu na to, jak został on napisany. W następnym punkcie rozdziału przypomnimy jakiś fragment kodu zapisany w rozdziale 2. i przekonamy się, co będziemy z niego mogli zrobić dzięki zasto- sowaniu refaktoryzacji. 3.1.4. Refaktoryzacja w działaniu Być może niektórym z Czytelników refaktoryzacja już się spodobała, jednak bardziej praktyczne osoby, zanim się nią zachwycą, wolałyby zapewne ujrzeć ją w działaniu. Poświęćmy zatem nieco czasu na wprowadzenie zmian w kodzie, który napisaliśmy w poprzednim rozdziale i przedstawiliśmy na listingu 2.11. Kod ten składa się z funkcji sendRequest(), która odpowiada za przesyłanie żądań na serwer. Funkcja sendRequest() wywołuje z kolei funkcję initHttpRequest(), która tworzy odpowiedni obiekt XMLHttpRequest i definiuje funkcję obsługującą odpowiedzi — onReadyState(). Obiekt XMLHttpRequest został zapisany w zmiennej globalnej, dzięki czemu wszystkie używane przez nas funkcje mogły bez trudu pobrać referencję do niego. Funkcja zwrotna obsługująca odpowiedzi spraw- dzała stan obiektu żądania i generowała stosowne informacje testowe. Kod przedstawiony na listingu 2.11 wykonuje dokładnie to, o co nam chodziło, jednak jego ponowne wykorzystanie przysporzyłoby nam sporych problemów. Zazwyczaj jeśli decydujemy się na przesyłanie żądania na serwer, chcemy prze- twarzać odpowiedzi i wykonywać pewne operacje charakterystyczne jedynie dla tej konkretnej aplikacji. W celu zastosowania nowej logiki biznesowej w ist- niejącym już kodzie, będziemy musieli zmodyfikować pewne fragmenty funkcji onReadyState(). Sporym problemem może być także zastosowanie zmiennych globalnych. Jeśli jednocześnie będziemy chcieli przesłać na serwer kilka żądań, musimy mieć możliwość określenia odpowiedniej funkcji zwrotnej w każdym z nich. Jeżeli np. będziemy pobierali listę zasobów do aktualizacji oraz drugą listę zasobów przeznaczonych do aktualizacji, to przecież należałoby je w jakiś sposób roz- różnić! W przypadku zastosowania technik programowania obiektowego rozwiąza- niem takiego problemu byłoby zaimplementowanie niezbędnych możliwości funkcjonalnych w formie obiektu. Język JavaScript zapewnia niezbędne moż- liwości, które z powodzeniem będziemy mogli zastosować. Obiekt, jaki utwo- rzymy w tym rozdziale, nazwiemy ContentLoader, gdyż będziemy go używać do pobierania zawartości z serwera. Ale jak ten obiekt powinien wyglądać? Opty- malnie byłoby, gdybyśmy mogli utworzyć go, przekazując w wywołaniu kon- struktora adres URL, na jaki należy przesłać żądanie. Oprócz tego powinniśmy mieć możliwość przekazania referencji do zdefiniowanej przez nas funkcji zwrotnej, która zostanie wywołana w razie pomyślnego zakończenia obsługi
  • 15. 3.1. Porządkowanie chaosu 97 żądania, oraz drugiej funkcji — wywoływanej w razie wystąpienia jakichś błędów. Instrukcja tworząca ten obiekt mogłaby mieć zatem następującą postać: new loader = new ContentLoader('mydata.xml', parseMyData); Przy czym parseMyData jest funkcją, którą należy wywołać w momencie ode- brania odpowiedzi na żądanie. Kod obiektu ContentLoader przedstawiliśmy na listingu 3.1. Zastosowaliśmy w nim kilka nowych rozwiązań, które wymagają dokładniejszego opisu. Listing 3.1. Obiekt ContentLoader var net=new Object(); Obiekt pełniący funkcję przestrzeni nazw net.READY_STATE_UNINITIALIZED=0; net.READY_STATE_LOADING=1; net.READY_STATE_LOADED=2; net.READY_STATE_INTERACTIVE=3; net.READY_STATE_COMPLETE=4; net.ContentLoader=function(url,onload,onerror){ Funkcja konstruktora this.url=url; this.req=null; this.onload=onload; this.onerror=(onerror) ? onerror : this.defaultError; this.loadXMLDoc(url); } net.ContentLoader.prototype = { loadXMLDoc:function(url){ Funkcja initXMLHttpRequest ze zmienioną nazwą if (window.XMLHttpRequest){ Zmodyfikowana this.req=new XMLHttpRequest(); funkcja loadXML } else if (window.ActiveXObject){ this.req=new ActiveXObject("Microsoft.XMLHTTP"); } if (this.req){ try{ var loader=this; this.req.onreadystatechange=function(){ net.ContentLoader.onReadyState.call(loader); } this.req.open('GET',url,true); Zmodyfikowana funkcja sendRequest this.req.send(null); }catch (err){ this.onerror.call(this); } } }, onReadyState:function(){ Zmodyfikowana funkcja zwrotna var req=this.req; var ready=req.readyState; var httpStatus=req.status; if (ready==net.READY_STATE_COMPLETE){ if (httpStatus==200 || httpStatus==0){ this.onload.call(this); }else{ this.onerror.call(this); } } },
  • 16. 98 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax defaultError:function(){ alert("Błąd podczas pobierania danych!" +"nnreadyState:"+this.req.readyState +"nstatus: "+this.req.status +"nnagłówki: "+this.req.getAllResponseHeaders()); } } Pierwszą rzeczą, na jaką należy zwrócić uwagę w powyższym kodzie, jest zde- finiowanie zmiennej globalnej net i skojarzenie z nią wszystkich pozostałych referencji. Dzięki temu minimalizujemy ryzyko wystąpienia konfliktów nazw z innymi zmiennymi i grupujemy cały kod związany z generacją i obsługą żądań w jednym miejscu. Następnie zdefiniowaliśmy funkcję konstruktora naszego obiektu . Wymaga ona przekazania trzech argumentów, jednak tylko dwa pierwsze są obowiązkowe. Trzeci argument, określający funkcję wywoływaną w przypadku wystąpienia błędów, jest opcjonalny — jeśli jego wartość będzie równa null, to wywołamy funkcję domyślną. Dla osób, które mają doświadczenie w programowaniu obiek- towym, możliwość przekazywania zmiennej ilości argumentów w wywołaniach funkcji może się wydawać dziwna, podobnie jak przekazywanie funkcji jako referencji. Jednak są to często stosowane możliwości języka JavaScript. Szcze- gółowe informacje na ich temat oraz dokładny opis samego języka można znaleźć w Dodatku B. Znaczne fragmenty kodu funkcji initXMLHttpRequest() oraz sendRequest() z listingu 2.11 przenieśliśmy do metod tworzonego obiektu ContentLoader. Przy okazji zmieniliśmy trochę nazwy metod, by lepiej odpowiadały nieco więk- szemu zakresowi ich działania. Teraz kod generujący żądania znajduje się w meto- dzie o nazwie loadXMLDoc(). Obiekt XMLHttpRequest odnajdujemy i tworzymy dokładnie w taki sam sposób jak poprzednio, jednak użytkownik naszego obiektu ContentLoader nie musi nic na ten temat wiedzieć. Funkcja zwrotna onReadyState() także powinna wyglądać bardzo podobnie do swego pierwowzoru z listingu 2.11. Wywołania generujące komunikaty na konsoli do testów zastąpiliśmy w tym przypadku wywołaniami funkcji onload oraz onerror. Składnia używana do wykonania tych wywołań może się wydawać nieco dziwna, zatem przyjrzyjmy się jej dokładniej. We właściwościach onload oraz onerror są zapisane obiekty Function języka JavaScript, a obiekty tego typu udostępniają metodę call(). Pierwszym argumentem wywołania metody Function.call() jest obiekt, który stanie się kontekstem dla wywoływanej funkcji, czyli wewnątrz niej będzie się można odwoływać do niego przy wykorzystaniu słowa kluczowego this. Napisanie funkcji zwrotnej, którą przekażemy do obiektu ContentLoader, także nie przysporzy nam większych problemów. Jeśli w kodzie funkcji zwrotnej będziemy musieli odwołać się do jakiejkolwiek właściwości obiektu ContentLoader, to wystarczy, że jej nazwę poprzedzimy słowem kluczowym this. Np.: function myCallBack(){ alert( "Plik " +this.url +" został wczytany! Oto jego zawartość:nn"
  • 17. 3.2. Niewielkie przykłady refaktoryzacji 99 +this.req.responseText ); } Utworzenie takiego kontekstu wymaga napisania dodatkowego kodu i doskonałej znajomości tajników JavaScriptu, jednak kiedy go już napiszemy i zastosujemy w obiekcie, to użytkownik końcowy nie będzie musiał o niczym wiedzieć. Taka sytuacja, w której użytkownik obiektu nie musi się interesować szcze- gółami jego implementacji, bardzo często jest sygnałem dobrze przeprowadzonej refaktoryzacji. Oznacza bowiem, że wszystkie złożone czynności zostały umiesz- czone wewnątrz obiektu, a użytkownik może się posługiwać łatwym interfejsem. W ten sposób jest on chroniony przed całą złożonością działania obiektu, a eks- pert, który go stworzył i odpowiada za jego pielęgnację, umieścił cały złożony kod w jednym miejscu. Wszelkie ewentualne zmiany będą musiały być wyko- nane tylko jeden raz i w jednym, precyzyjnie określonym miejscu, a ich efekt zostanie zauważony i uwzględniony w całym kodzie aplikacji. W ten sposób przedstawiliśmy podstawowe założenia refaktoryzacji i pokazali- śmy, jakie korzyści nam ona zapewnia. W następnym podrozdziale przedstawimy problemy, które najczęściej występują podczas pisania aplikacji wykorzystujących technologię Ajax, oraz jak można je rozwiązywać poprzez zastosowanie refakto- ryzacji. Jednocześnie zaprezentujemy kilka przydatnych sztuczek, które my z powodzeniem będziemy mogli wykorzystać w następnych rozdziałach książki, a Czytelnik — we własnych projektach. 3.2. Niewielkie przykłady refaktoryzacji W tym podrozdziale zaprezentujemy kilka problemów występujących podczas wykorzystywania technologii Ajax oraz najczęściej stosowane sposoby ich roz- wiązywania. W każdym z przykładów pokażemy, jak zastosowanie refaktoryza- cji może nam pomóc w rozwiązaniu problemu, a następnie wskażemy elementy rozwiązania, które będzie można wykorzystać przy innych okazjach. Nawiązując do zaszczytnych tradycji literatury poświęconej wzorcom pro- jektowym, w pierwszej kolejności opiszemy problem, następnie przedstawimy jego techniczne rozwiązanie, a na końcu opiszemy je w szerszym kontekście. 3.2.1. Niezgodności przeglądarek: wzorce Façade oraz Adapter Gdyby zapytać osoby zajmujące się tworzeniem witryn i aplikacji internetowych — niezależnie od tego, czy są to programiści, projektanci, graficy, czy osoby zajmujące się każdym z tych aspektów tworzenia stron WWW — co je najbardziej denerwuje w ich pracy, to istnieje bardzo duże prawdopodobieństwo, iż na liście odpowiedzi znajdzie się konieczność zapewniania poprawnego wyglądu stron w różnych przeglądarkach. Na WWW istnieje wiele standardów przeróżnych technologii, a większość nowoczesnych przeglądarek implementuje zazwyczaj
  • 18. 100 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax w całości lub prawie w całości znaczną większość tych standardów. Czasami jednak standardy te nie są precyzyjne i zostawiają miejsce dla dowolnej inter- pretacji, czasami twórcy przeglądarek rozszerzają je w sposób przydatny, choć niespójny, a zdarza się również, że w przeglądarkach pojawiają się stare, wszech- obecne błędy programistyczne. Programiści używający języka JavaScript już od momentu jego pojawienia się nawykli do sprawdzania tego, jaka przeglądarka jest aktualnie używana bądź to testowania oraz czy konkretny obiekt jest dostępny, czy nie. Przeanalizujmy teraz bardzo prosty przykład. Operowanie na elementach DOM Zgodnie z informacjami podanymi w rozdziale 2. zawartość strony WWW jest udostępniana w kodzie JavaScript w formie obiektowego modelu dokumentu (ang. Document Object Model, w skrócie DOM) — struktury drzewiastej, której węzły odpowiadają elementom HTML umieszczonym w kodzie strony. W przy- padku programowego manipulowania drzewem DOM bardzo często pojawia się konieczność określenia położenia elementu na stronie. Niestety, twórcy przeglą- darek udostępnili różne, niestandardowe sposoby określania tego położenia, przez co napisanie jednego, ogólnego kodu wykonującego to zadanie prawidłowo w różnych przeglądarkach jest dosyć trudne. Listing 3.2 przedstawia uprosz- czoną wersję funkcji pochodzącej z biblioteki x Mike’a Fostera (patrz podroz- dział 3.5), która rozwiązuje problem określenia położenia wskazanego elementu od lewej krawędzi strony. W tym przypadku element jest przekazywany jako argument wywołania funkcji. Listing 3.2. Funkcja getLeft() function getLeft(e){ if(!(e=xGetElementById(e))){ return 0; } var css=xDef(e.style); if (css && xStr(e.style.left)) { iX=parseInt(e.style.left); if(isNaN(iX)) iX=0; }else if(css && xDef(e.style.pixelLeft)) { iX=e.style.pixelLeft; } return iX; } Różne przeglądarki udostępniają wiele sposobów określania położenia węzła przy wykorzystaniu tablicy stylów, którą przedstawiliśmy w rozdziale 2. Standard W3C CSS2 udostępnia właściwość style.left, zawierającą łańcuch znaków składający się z wartości liczbowej oraz określenia jednostki, np. 100px. Należy pamiętać, że mogą być stosowane inne jednostki niż piksele. Z kolei właściwość style.pixelLeft zawiera wartość numeryczną i to określającą przesunięcie elementu liczone w pikselach. Jednak właściwość ta jest dostępna wyłącznie
  • 19. 3.2. Niewielkie przykłady refaktoryzacji 101 w przeglądarce Internet Explorer. Przedstawiona na powyższym listingu funkcja getLeft() w pierwszej kolejności sprawdza, czy jest dostępny obiekt stylów CSS — jeśli jest, to najpierw stara się pobrać wartość właściwości style.left, zaś jeśli nie jest ona dostępna, to właściwości style.pixelLeft. Jeżeli nie uda się pobrać żadnej z tych wartości, funkcja zwraca domyślną wartość 0. Warto zauważyć, że metoda ta nie sprawdza nazw ani wersji używanej przeglądarki, a zamiast tego korzysta ze znacznie lepszego sposobu polegającego na testowaniu dostępności obiektów (opisaliśmy go w rozdziale 2.). Pisanie takich funkcji, ukrywających różnice pomiędzy poszczególnymi prze- glądarkami, jest zadaniem żmudnym i czasochłonnym; jednak kiedy się już z nim uporamy, będziemy mogli zająć się pisaniem aplikacji bez zwracania uwagi na te drobne, lecz niezwykle denerwujące szczegóły. A dzięki zastosowaniu dobrze napisanych i przetestowanych bibliotek, takich jak biblioteka x, możemy się uchronić przed koniecznością wykonywania większości najtrudniejszej pracy. Możliwość zastosowania godnych zaufania, ogólnych funkcji pozwalających na określanie położenia elementów DOM na stronie może w znaczny sposób przy- spieszyć tworzenie interfejsu użytkownika aplikacji wykorzystujących techno- logię Ajax. Przesyłanie żądania na serwer Już w poprzednim rozdziale zetknęliśmy się z podobnym problemem niezgod- ności pomiędzy różnymi przeglądarkami. Wynikał on z faktu, iż różne przeglą- darki udostępniają różne sposoby tworzenia obiektu XMLHttpRequest, używanego do asynchronicznego przesyłania żądań na serwer. Chcąc pobrać z serwera dokument XML, musieliśmy sprawdzić, którą z możliwości należy zastosować. Internet Explorer pozwoli nam na asynchroniczne pobieranie plików wyłącz- nie w przypadku gdy poprosimy o odpowiedni komponent ActiveX, natomiast przeglądarki fundacji Mozilla i Safari doskonale poradzą sobie, używając wbu- dowanego obiektu XMLHttpRequest. Jednak o tych rozbieżnościach „wiedział” jedynie kod bezpośrednio odpowiedzialny za wczytywanie dokumentów XML. Kiedy obiekt XMLHttpRequest został już pobrany, to dalszy sposób korzystania z niego był identyczny we wszystkich przeglądarkach. Jednak kod skryptu, który chce pobrać jakiś dokument XML z serwera, nie musi „wiedzieć” ani o obiek- tach ActiveX, ani o podsystemie obiektów udostępnianych przez przeglądarkę: wystarczy, że będzie „wiedział”, jak wywołać konstruktor net.ContentLoader(). Wzorzec Façade W obu przedstawionych wcześniej funkcjach, zarówno getLeft(), jak i net.Con- tentLoader(), kod służący do wykrywania aktualnie używanej przeglądarki jest paskudny i nużący. Dzięki stworzeniu funkcji, która ukryje go przed resztą nasze- go kodu, nie tylko poprawimy jego czytelność, lecz także umieścimy wszystkie możliwości funkcjonalne związane z wykrywaniem przeglądarki w jednym, precyzyjnie określonym miejscu. To jedna z podstawowych zasad refaktoryzacji — nie należy się powtarzać. Jeśli odkryjemy jakąś szczególną sytuację, w której
  • 20. 102 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax nasz obiektowy kod nie działa zbyt dobrze, wystarczy wprowadzić zmiany w jed- nym miejscu, a zostaną one uwzględnione we wszystkich wywołaniach określają- cych położenie lewej krawędzi elementu DOM, tworzenia obiektu XMLHttpRequest bądź też jakiejkolwiek innej metody. W języku wzorców projektowych mówimy, iż używamy wzorca o nazwie Façade (fasada). Wzorzec ten służy do tworzenia wspólnego punktu dostępu do różnych implementacji pewnej usługi lub możliwości funkcjonalnej. Taką użyteczną usługę udostępnia np. obiekt XMLHttpRequest, a naszej aplikacji tak naprawdę nie „obchodzi”, w jaki sposób jest on tworzony — ważne, by działał prawidłowo (patrz rysunek 3.1). Rysunek 3.1. Schemat wzorca Façade oraz jego związków z obiektem XMLHttpRequest w przeglądarkach. Funkcja loadXML() wymaga obiektu XMLHttpRequest, jednak nie „interesuje” jej faktyczna implementacja tego obiektu. Obie faktyczne implementacje mogą udostępniać znacznie bogatsze możliwości obsługi obiektów żądań HTTP, jednak w tym przypadku zostają one ograniczone od minimalnych możliwości wymaganych przez funkcję wywołującą W wielu przypadkach zdarza się, że będziemy chcieli uprościć dostęp do pew- nego podsystemu. Chcąc np. określić położenie lewej krawędzi elementu DOM, możemy to zrobić na wiele różnych sposobów, a zwrócona wartość może być wyrażona w pikselach, punktach lub wielu innych jednostkach. Ta dowolność może być nam zupełnie nieprzydatna. Przedstawiona wcześniej funkcja getLeft() będzie działać, jeśli określając układ strony, konsekwentnie będziemy to robić przy wykorzystaniu pikseli. Taki sposób uproszczenia podsystemu jest kolejną cechą wzorca Façade.
  • 21. 3.2. Niewielkie przykłady refaktoryzacji 103 Wzorzec Adapter Wzorzec Adapter jest powiązany ze wzorcem Façade. Także w jego przypadku operujemy na dwóch podsystemach, które wykonują te same zadania, takich jak dwa różne sposoby tworzenia obiektu XMLHttpRequest używane w przeglądarkach Internet Explorer i Mozilla. Jednak zamiast tworzyć fasadę dla każdego z tych dwóch podsystemów, to dla jednego z nich tworzymy dodatkową warstwę abstrak- cji udostępniającą taki sam interfejs, jakim dysponuje drugi. To właśnie ta war- stwa stanowi wzorzec Adapter. Biblioteka Sarissa, którą przedstawimy w punk- cie 3.5.1, używa tego wzorca, by upodobnić komponent ActiveX stosowany w Internet Explorerze do wbudowanego obiektu XMLHttpRequest stosowanego w przeglądarkach fundacji Mozilla. Oba te wzorce projektowe są dobre i mogą znacząco pomóc w wykorzystaniu istniejącego już kodu w tworzonej aplikacji (jak widać, dotyczy to także rozwiązań udostępnianych przez przeglądarki). Zajmijmy się kolejnym studium, w którym zastanowimy się nad problemami związanymi z modelem obsługi zdarzeń stosowanym w języku JavaScript. 3.2.2. Zarządzanie procedurami obsługi zdarzeń: wzorzec Observer Trudno sobie wyobrazić pisanie aplikacji wykorzystujących technologię Ajax bez zastosowania technik programowania bazującego na zdarzeniach. Interfejsy użytkownika tworzone i obsługiwane w języku JavaScript powszechnie korzystają ze zdarzeń, a zastosowanie w aplikacji asynchronicznych żądań wysyłanych i odbieranych przy użyciu technologii Ajax powoduje, że w aplikacji pojawia się kolejna grupa zdarzeń oraz obsługujących je funkcji. W stosunkowo prostych aplikacjach zdarzenia takie, jak kliknięcie przycisku lub odebranie odpowiedzi przesłanej z serwera można obsługiwać przy wykorzystaniu jednej funkcji. Jednak wraz z powiększaniem się aplikacji zarówno pod względem ilości kodu, jak i jego złożoności może się pojawić konieczność, aby informacja o odebraniu jednego zdarzenia trafiła do kilku niezależnych podsystemów. W skrajnym przypadku może się nawet okazać, że konieczne jest udostępnienie mechanizmu pozwalają- cego zainteresowanym obiektom na rejestrowane się i otrzymywanie informacji o konkretnych zdarzeniach. Przyjrzyjmy się przykładowi, który zilustruje taką sytuację. Stosowanie wielu procedur obsługi zdarzeń Podczas programowego operowania na węzłach DOM często stosowana jest technika polegająca na umieszczaniu skryptu w procedurze obsługi zdarzeń onload (wykonywanej po załadowaniu całej strony, a co za tym idzie, po zakoń- czeniu tworzenia drzewa DOM). Załóżmy, że na naszej stronie jest umieszczony element, w którym chcemy dynamicznie wyświetlać dane, jakie po załadowaniu strony będą cyklicznie pobierane z serwera. Kod JavaScript koordynujący pobie- ranie danych oraz ich wyświetlanie musi odwoływać się do węzła DOM, a zatem wszystkie operacje zaczynają się w procedurze obsługi zdarzeń onload:
  • 22. 104 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax window.onload=function() { displayDiv=document.getElementById('display'); } Jak na razie wszystko idzie doskonale. A teraz załóżmy, że chcemy umieścić na stronie drugi obszar, prezentujący komunikaty pochodzące z kanałów RSS (jeśli Czytelnik jest zainteresowany mechanizmem implementującym takie możliwości, to może go znaleźć w rozdziale 13.). Kod aktualizujący prezentowane powia- domienia, także podczas uruchamiania strony, musi pobrać referencję do węzła DOM. Zatem także dla niego definiujemy procedurę obsługi zdarzeń onload: window.onload=function() { feedDiv=document.getElementById('feeds'); } Przetestowaliśmy oba te rozwiązania na osobnych stronach i okazało się, że dzia- łają doskonale. A zatem w ramach kolejnego etapu prac połączyliśmy je i wtedy okazało się, że druga procedura obsługi zdarzeń onload przesłoniła pierwszą, dane nie wyświetlają się na stronie, a co gorsza zamiast nich na stronie pojawia się cała masa komunikatów o błędach JavaScriptu. W tym przypadku cały pro- blem polegał na tym, iż obiekt window pozwala na określenie tylko jednej pro- cedury obsługi zdarzeń onload. Ograniczenia złożonej procedury obsługi zdarzeń Jak już napisaliśmy, w powyższym przykładzie druga procedura obsługi zdarzeń onload przesłania pierwszą. Problem ten możemy w prosty sposób rozwiązać poprzez napisanie jednej, złożonej funkcji: window.onload=function() { displayDiv=document.getElementById('display'); feedDiv=document.getElementById('feeds'); } W naszym przykładzie takie rozwiązanie zda egzamin, choć ma tę wadę, iż łączy w sobie kod związany z prezentacją danych i pobieraniem zawartości kanałów RSS, które nie są ze sobą w żaden sposób powiązane. Jeśli w tej aplikacji będą stosowane nie 2, lecz 20 takich niezależnych podsystemów, i każdy będzie mu- siał pobrać referencję do jakiegoś węzła DOM, to kod takiej złożonej procedury obsługi zdarzeń stanie się trudny do pielęgnacji. Poza tym takie rozwiązanie znacznie utrudniłoby dodawanie do aplikacji i usuwanie z niej komponentów oraz sprawiłoby, że takie modyfikacje zostaną obciążone dodatkowym ryzykiem wystąpienia błędów. Innymi słowy, w większej aplikacji zastosowanie takiej złożonej procedury obsługi zdarzeń mogłoby doprowadzić do opisanej wcześniej sytuacji, w której nikt nie chciałby zmieniać kodu, obawiając się, iż przestanie on działać. Spróbujmy zatem nieco zmodyfikować nasz kod i zdefiniować osobne funkcje inicjujące dla każdego z podsystemów: window.onload=function(){ getDisplayElements(); getFeedElements(); }
  • 23. 3.2. Niewielkie przykłady refaktoryzacji 105 function getDisplayElements(){ displayDiv=document.getElementById('display'); } function getFeedElements(){ feedDiv=document.getElementById('feeds'); } Jak widać, udało nam się nieco poprawić przejrzystość i czytelność kodu oraz zredukować objętość funkcji window.onload do jednego wiersza kodu dla każdego podsystemu. Niemniej jednak ta złożona funkcja wciąż jest słabym punktem całego projektu i potencjalną przyczyną problemów. W następnym podpunkcie przedstawimy nieco bardziej złożone, lecz jednocześnie bardziej skalowalne rozwiązanie. Wzorzec Observer Czasami warto się zastanowić, na kim spoczywa odpowiedzialność za wykonanie pewnej akcji. W przypadku przedstawionej wcześniej funkcji złożonej odpowie- dzialność za pobranie referencji do węzłów DOM spoczywała na obiekcie window, co oznacza, że musi on „wiedzieć”, które z nich będą używane na danej stronie. W idealnym przypadku każdy z podsystemów sam powinien być odpowiedzialny za pobranie potrzebnej referencji. Dzięki temu jeśli podsystem zostanie użyty na stronie, to pobierze referencję, w przeciwnym razie żadne operacje inicjaliza- cyjne tego modułu nie zostaną wykonane. Aby jasno i precyzyjnie określić obowiązki poszczególnych podsystemów, możemy zapewnić im możliwość rejestrowania się i uzyskiwania powiadomień o zgłoszeniu zdarzenia load. W tym celu podsystemy będą przekazywały funk- cje zwrotne, które należy wywołać w procedurze obsługi zdarzeń onload. Poniżej przedstawiliśmy prostą implementację takiego rozwiązania: window.onloadListeners=new Array(); window.addOnLoadListener(listener){ window.onloadListeners[window.onloadListeners.length]=listener; } Po wczytaniu całej zawartości strony, podczas obsługi zdarzenia load, musimy jedynie przejrzeć całą zawartość tablicy window.onloadListeners i wywołać każdą z zapisanych w niej funkcji: window.onload=function(){ for(var i=0;i<window.onloadListeners.length;i++){ var func=window.onlloadListeners[i]; func.call(); } } Jeśli założymy, że każdy podsystem wykorzysta to rozwiązanie, będziemy mogli zaoferować znacznie bardziej przejrzysty sposób ich inicjalizacji i to bez koniecz- ności niepotrzebnego kojarzenia wszystkich podsystemów ze sobą w jednym fragmencie kodu. Oczywiście, wystarczy tylko jeden nieostrożny wiersz kodu, by jawnie przesłonić naszą funkcję window.onload i sprawić, że całe rozwiązanie
  • 24. 106 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax przestanie działać. Jednak w jakimś momencie będziemy musieli zatroszczyć się o nasz kod i zadbać o to, by takie błędy się w nim nie pojawiały. Przy tej okazji warto zwrócić uwagę Czytelnika na fakt, iż nowy model obsługi zdarzeń W3C także implementuje podobne rozwiązanie, w którym w jednym obiekcie można rejestrować wiele funkcji obsługujących to samo zdarzenie. Zdecydowaliśmy się jednak na zaimplementowanie własnego rozwiązania ba- zującego na starym modelu obsługi zdarzeń języka JavaScript, gdyż nowy model W3C nie jest spójnie obsługiwany we wszystkich przeglądarkach. Zagadnieniem tym zajmiemy się bardziej szczegółowo w rozdziale 4. Wzorzec projektowy, który zastosowaliśmy, tworząc powyższe rozwiązanie, nosi nazwę Observer (obserwator). Definiuje on obiekt „obserwowany” (w naszym przypadku jest to wbudowany obiekt window przeglądarki) oraz grupę obiektów „obserwatorów” lub „słuchaczy”, które mogą się w nim rejestrować (patrz rysu- nek 3.2). Rysunek 3.2. Rozdzielenie obowiązków we wzorcu projektowym Observer. Obiekty, które „chcą” być informowane o zdarzeniach, czyli obserwatory, mogą się rejestrować i usuwać ze źródła zdarzeń, które z kolei będzie przekazywać im stosowne informacje w momencie zajścia zdarzenia W tym wzorcu obowiązki są prawidłowo rozdzielone pomiędzy źródło zdarzeń oraz procedurę obsługi tego zdarzenia. Funkcje obsługujące zdarzenie same powinny się zarejestrować oraz, ewentualnie, poinformować obiekt obserwo- wany, że nie są już zainteresowane generowanymi przez niego zdarzeniami. Obiekt stanowiący źródło zdarzeń odpowiada za przechowywanie listy wszyst- kich „zainteresowanych” funkcji oraz generowanie powiadomień w przypadku zgłoszenia zdarzenia. Wzorzec Observer od dawna jest stosowany podczas pro- gramowej obsługi interfejsów użytkownika sterowanych zdarzeniami i wrócimy do niego w rozdziale 4., przy okazji szczegółowej analizy obsługi zdarzeń w ję- zyku JavaScript. Jak się okaże, wzorzec ten z powodzeniem można zastosować we własnych obiektach, które nie mają nic wspólnego z obsługą zdarzeń gene- rowanych przez klawiaturę lub mysz. Teraz zajmiemy się kolejnym często występującym problemem, który mo- żemy rozwiązać przy użyciu refaktoryzacji.
  • 25. 3.2. Niewielkie przykłady refaktoryzacji 107 3.2.3. Wielokrotne stosowanie funkcji obsługujących działania użytkownika: wzorzec Command Być może takie stwierdzenie będzie dla Czytelnika oczywiste, jednak w więk- szości przypadków, to użytkownik „informuje” aplikację (przy wykorzystaniu klawiatury lub myszy) o tym, co należy zrobić, a ta wykonuje odpowiednie czynno- ści. W prostych programach możemy udostępnić użytkownikowi tylko jedną możliwość wykonania pewnej czynności, jednak w przypadku tworzenia bardziej złożonych interfejsów możemy chcieć, aby mógł on doprowadzić do wykonania tej samej operacji na kilka różnych sposobów. Implementacja kontrolki przycisku Załóżmy, że na naszej stronie znajduje się element DOM, któremu, przy użyciu stylów CSS, nadaliśmy wygląd przypominający przycisk. Kliknięcie tego przyci- sku powoduje wykonanie pewnych obliczeń i wyświetlenie wyników w tabeli HTML. Procedurę obsługującą zdarzenia kliknięcia takiego przycisku mogli- byśmy zdefiniować w następujący sposób: function buttonOnclickHandler(event){ var data=new Array(); data[0]=6; data[1]=data[0]/3; data[2]=data[0]*data[1]+7; var newRow=createTableRow(dataTable); for (var i=0;i<data.length;i++){ createTableCell(newRow,data[i]); } } Zakładamy przy tym, że zmienna dataTable zawiera referencję do faktycznie istniejącej tabeli, a funkcje createTabelRow() oraz createTableCell() potrafią prawidłowo operować na węzłach DOM. Najciekawszym fragmentem powyż- szego kodu jest fragment odpowiadający za wykonanie obliczeń; w rzeczywistej aplikacji może on mieć długość kilkuset wierszy kodu. Powyższą funkcję koja- rzymy z przyciskiem w następujący sposób: buttonDiv.onclick=buttonOnclickHandler; Obsługa zdarzeń wielu różnych typów Załóżmy teraz, że rozbudowaliśmy naszą aplikację o możliwości wykorzysty- wania technologii Ajax. Pobieramy z serwera aktualizacje danych i chcemy wy- konywać powyższe obliczenia jedynie w przypadku gdy pewna wartość została zmodyfikowana, a oprócz tego chcemy aktualizować inną tabelę danych. Tym razem nie będziemy się zajmowali konfiguracją cyklicznego przesyłania żądań na serwer. Załóżmy, że dysponujemy obiektem, który zapewnia odpowiednie możliwości. Używa on obiektu XMLHttpRequest i konfiguruje go w taki sposób, by po odebraniu odpowiedzi z serwera wywoływana była funkcja onload(). Możemy wydzielić fragmenty kodu związane z dokonywaniem odpowiednich
  • 26. 108 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax obliczeń oraz z aktualizacją strony i umieścić je w dwóch niezależnych funkcjach przedstawionych poniżej: function buttonOnclickHandler(event){ var data=calculate(); showData(dataTable,data); } function ajaxOnloadHandler(){ var data=calculate(); showData(otherDataTable,data); } function calculate(){ var data=new Array(); data[0]=6; data[1]=data[0]/3; data[2]=data[0]*data[1]+7; return data; } function showData(table,data){ var newRow=createTableRow(table); for (var i=0;i<data.length;i++){ createTableCell(newRow,data[i]); } } buttonDiv.onclick=buttonOnclickHandler; poller.onload=ajaxOnloadHandler; Cały wielokrotnie używany kod został wydzielony i umieszczony w funkcjach calculate() oraz showData(); dzięki temu znacznie ograniczyliśmy powielanie się fragmentów kodu w całej aplikacji (aktualnie powtarzają się w niej jedynie dwa wiersze, używane w funkcjach buttonOnclickHandler() oraz ajaxOnloadHan- dler()). Dzięki zastosowanemu rozwiązaniu znacznie lepiej rozdzieliliśmy kod odpo- wiedzialny za realizację logiki biznesowej oraz za aktualizację interfejsu użyt- kownika. Także w tym przypadku udało nam się określić przydatne rozwiązanie nadające się do wielokrotnego wykorzystania. Wzorzec projektowy Command definiuje pewną czynność o dowolnym stopniu złożoności, którą bez trudu będzie można przekazywać i używać w różnych miejscach kodu oraz w różnych ele- mentach interfejsu użytkownika. W klasycznym wzorcu Command stosowanym w obiektowych językach programowania wszystkie czynności wykonywane przez użytkownika zostają umieszczone w obiektach Command, które zazwyczaj dzie- dziczą do pewnej wspólnej klasy bazowej (może to także być interfejs bazowy). W przedstawionym przykładzie ten sam problem rozwiązaliśmy w nieco inny sposób. Ponieważ w języku JavaScript funkcje są obiektami, same funkcje mo- żemy potraktować jako obiekty Command. Jednak nawet takie nieco uprosz- czone rozwiązanie gwarantuje nam ten sam poziom abstrakcji. Choć obsługa wszystkich czynności wykonywanych przez użytkownika w obiektach Command może się wydawać rozwiązaniem nieco dziwnym i niepo- ręcznym, to jednak posiada ono konkretne zalety. Kiedy np. wszystkie czynności wykonywane przez użytkownika zastaną zaimplementowane w formie obiektów Command, bardzo łatwo będziemy mogli z nimi skojarzyć inne, standardowe
  • 27. 3.2. Niewielkie przykłady refaktoryzacji 109 możliwości funkcjonalne. Jednym z najczęściej opisywanych rozszerzeń tego typu jest dodanie metody undo(). W ten sposób można stworzyć zalążek ogólnego mechanizmu o zasięgu całej aplikacji, pozwalającego na odtwarzanie skutków wcześniej wykonanych czynności. Można też sobie wyobrazić bardziej złożone rozwiązanie, w którym każdy wykonywany obiekt Command będzie także zapi- sywany na stosie, a użytkownik, klikając odpowiedni przycisk, będzie mógł zdej- mować te obiekty ze stosu, przywracając tym samym aplikację do wcześniejszego stanu (patrz rysunek 3.3). Rysunek 3.3. Zastosowanie wzorca Command do utworzenia ogólnego stosu poleceń w aplikacji edytora tekstów. Wszystkie czynności wykonywane przez użytkownika są reprezentowane jako obiekty Command, dzięki temu czynności te można nie tylko wykonywać, lecz także odtwarzać Każde nowe polecenie jest umieszczane na wierzchołku stosu, a następnie można je kolejno z niego zdejmować. Wyobraźmy sobie sytuację, w której użyt- kownik tworzy dokument, wykonując sekwencję akcji zapisu tekstu, a następnie, przypadkowo, zaznacza całą zawartość dokumentu i usuwa ją. W takim przypadku, jeśli kliknie on przycisk Cofnij, zdejmie ze stosu ostatni umieszczony na nim obiekt Command i wywoła jego metodę undo(), co sprawi, że usunięty tekst znów pojawi się w dokumencie. Dalsze kliknięcia przycisku będą powodowały usuwanie z dokumentu kolejnych fragmentów tekstu.
  • 28. 110 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax Oczywiście, z punktu widzenia programisty, zastosowanie obiektów Com- mand do zaimplementowania stosu operacji, które można odtworzyć, oznacza konieczność wykonania dodatkowej pracy. Jest ona związana z zapewnieniem, by kombinacja wykonania pewnej czynności oraz jej odtworzenia spowodowała przywrócenie systemu do początkowego stanu. Niemniej jednak udostępnienie takiego mechanizmu może być ważnym czynnikiem świadczącym o jakości i moż- liwościach produktu. W szczególności dotyczy to aplikacji, które są używane długo i intensywnie, a zgodnie z informacjami podanymi w rozdziale 1. są to dwie podstawowe cechy aplikacji wykorzystujących technologię Ajax. Obiekty Command można także zastosować w sytuacjach, gdy konieczne jest przenoszenie danych przez granice rozdzielające różne podsystemy aplikacji. Oczywiście jedną z takich granic jest sam internet, zatem wrócimy do wzorca projektowego Command w rozdziale 5., poświęconym interakcjom pomiędzy klientem i serwerem. 3.2.4. Przechowywanie tylko jednej referencji do zasobu: wzorzec Singleton W niektórych sytuacjach bardzo duże znaczenie ma zapewnienie, by w aplikacji istniał tylko jeden punkt kontaktu z konkretnym zasobem. Także to zagadnienie najlepiej będzie przedstawić na konkretnym przykładzie. Prosty przykład handlowy Załóżmy, że nasza aplikacja używająca technologii Ajax operuje na danych giełdowych, pozwalając nam na przeprowadzanie faktycznych operacji na pa- pierach wartościowych, wykonywanie różnych obliczeń typu „co się stanie, jeśli” oraz uczestniczenie w internetowych grach symulacyjnych z innymi użytkow- nikami. Nasza aplikacja będzie mogła działać w trzech trybach, których nazwy odpowiadają kolorom świateł sygnalizacji ulicznej. I tak, tryb czasu rzeczywi- stego (tryb zielony) pozwala na wykonywanie faktycznych operacji giełdowych, czyli sprzedawanie i kupowanie papierów wartościowych w czasie gdy giełdy są otwarte oraz przeprowadzenie różnego typu obliczeń na zgromadzonych danych. Kiedy rynki zostaną zamknięte, aplikacja zaczyna działać w trybie analiz (trybie czerwonym), w którym wciąż pozwala na przeprowadzanie obliczeń, lecz nie daje możliwości handlowania papierami wartościowymi. Trzecim dostępnym trybem jest tryb symulacji (czyli żółty); możemy w nim realizować wszystkie czynności dostępne w trybie zielonym, lecz w tym przypadku nie są one wykony- wane na faktycznych danych giełdowych, lecz na zestawie danych testowych. Kod wykonywany w przeglądarce rozróżnia te wszystkie tryby przy wyko- rzystaniu obiektu JavaScript przedstawionego na poniższym przykładzie: var MODE_RED=1; var MODE_AMBER=2; var MODE_GREEN=2; function TradingMode(){ this.mode=MODE_RED; }
  • 29. 3.2. Niewielkie przykłady refaktoryzacji 111 Możemy zarówno sprawdzać, jak i ustawiać tryb stosowany w tym obiekcie, a kod naszej aplikacji robi to w wielu miejscach. Moglibyśmy zdefiniować takie metody, jak getMode() oraz setMode(), które sprawdzałyby wiele różnych wa- runków, takich jak np. czy giełdy są w danej chwili otwarte itd., jak na razie jednak nie będziemy sobie komplikować życia. Załóżmy, że użytkownicy mogą korzystać z dwóch możliwości aplikacji: sprzedawania i kupowania papierów wartościowych oraz wyliczania, przed wyko- naniem operacji, potencjalnych zysków i strat, jakie ona wygeneruje. W zależności od trybu działania aplikacji akcje związane z kupowaniem i sprzedawaniem papierów wartościowych będą odwoływać się do różnych usług sieciowych — wewnętrznych — w trybie żółtym, usług dostępnych na serwerze naszego brokera — w trybie zielonym; natomiast w trybie czerwonym operacje te będą niedostępne. Podobnie analizy będę wykonywane w oparciu o pobierane z ser- wera dane o wcześniejszych i aktualnych cenach akcji, przy czym w trybie zielonym będą to faktyczne dane giełdowe, a w trybie żółtym — generowane przez nasz system dane testowe. Aby wiedzieć, z jakich źródeł danych skorzystać, aplikacja będzie korzystać z obiektu TradingMode (patrz rysunek 3.4). Rysunek 3.4. W naszej przykładowej aplikacji do handlu akcjami, wykorzystującej technologię Ajax, zarówno funkcje obsługujące operacje kupna i sprzedaży, jak i funkcje analityczne określają, czy należy używać rzeczywistej bazy danych, czy też zestawu danych testowych. Wybór zestawu danych odbywa się na podstawie wartości statusu obiektu TradingMode. Dane testowe używane są w trybie żółtym oraz odnoszą się do serwera z rzeczywistymi danymi w trybie zielonym. Jeśli w systemie pojawiłby się więcej niż jeden obiekt TradingMode, to mógłby on stracić stabilność
  • 30. 112 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax Kluczowe znaczenie dla poprawnego działania aplikacji ma zapewnienie, by obie akcje korzystały z tego samego obiektu TradingMode. Jeśli użytkownik będzie handlować akacjami w grze symulacyjnej, podejmując decyzje na podstawie faktycznych kursów papierów wartościowych, to można przypuszczać, że przegra. Jeśli natomiast użytkownik będzie handlować prawdziwymi akcjami na podstawie analiz i symulacji wykonywanych na danych testowych, to zapewne szybko straci pracę! Obiekt, którego w całej aplikacji można utworzyć tylko jeden egzemplarz, nazywamy często singletonem. W dalszej części rozdziału w pierwszej kolejno- ści zobaczymy, jak singletony są tworzone w językach obiektowych, a następnie opracujemy sposób ich tworzenia w języku JavaScript. Singletony w Javie W językach obiektowych takich jak Java w implementacjach singletonów zazwy- czaj ukrywamy konstruktor, a sam obiekt można pobierać przy użyciu odpowied- niej metody pobierającej. Implementację takiego singletonu przedstawiliśmy na listingu 3.3. Listing 3.3. Obiekt TradingMode jako singleton zaimplementowany w Javie public class TradingMode{ private static TradingMode instance=null; public int mode; private TradingMode(){ mode=MODE_RED; } public static TradingMode getInstance(){ if (instance==null){ instance=new TradingMode(); } return instance; } public void setMode(int mode){ ... } } Jak widać, powyższe rozwiązanie napisane w języku Java wymusza odpowiednie działanie singletonu poprzez zastosowanie modyfikatorów dostępu: public oraz private. Jeśli spróbujemy użyć poniższego fragmentu kodu: new TradingMode().setMode(MODE_AMBER); to okaże się, iż nie można go skompilować, gdyż konstruktor obiektu nie jest publicznie dostępny. Nasz obiekt da się natomiast pobrać przy użyciu poniższe- go wywołania: TradingMode.getInstance().setMode(MODE_AMBER);
  • 31. 3.2. Niewielkie przykłady refaktoryzacji 113 Powyższy kod zapewnia, że wszystkie odwołania trafią do tego samego egzem- plarza obiektu TradingMode. Jednak w powyższym rozwiązaniu zastosowaliśmy kilka możliwości Javy, które nie są dostępne w języku JavaScript. Przekonajmy się zatem, w jaki sposób możemy rozwiązać te problemy. Singletony w języku JavaScript Język JavaScript nie udostępnia modyfikatorów dostępu, niemniej jednak mo- żemy „ukryć” konstruktor obiektu — wystarczy w ogóle go nie implementować. JavaScript jest językiem wykorzystującym prototypy, w którym konstruktory są zwyczajnymi obiektami Function (jeśli Czytelnik nie wie, co to oznacza, to wszystkie niezbędne informacje można znaleźć w Dodatku B.). Moglibyśmy zaimplementować obiekt TradingMode w konwencjonalny sposób: function TradingMode(){ this.mode=MODE_RED; } TradingMode.prototype.setMode=function(){ } i udostępnić zmienną globalną „udającą” singleton: TradingMode.instance = new TradingMode(); Jednak takie rozwiązanie nie uchroniłoby nas przed możliwością wywołania konstruktora. Z drugiej strony, cały obiekt możemy także stworzyć ręcznie, bez stosowania prototypu: var TradingMode=new Object(); TradingMode.mode=MODE_RED; TradingMode.setMode=function(){ ... } Ten sam efekt możemy uzyskać, stosując nieco bardziej zwartą formę zapisu: var TradingMode={ mode:MODE_RED, setMode:function(){ ... } }; W obu powyższych przypadkach wygenerowany obiekt będzie identyczny. Pierwsze rozwiązanie będzie zapewne bardziej zrozumiałe dla osób znających języki Java lub C#, natomiast drugi sposób tworzenia obiektów przedstawiliśmy we wcześniejszej części książki, gdyż jest on często stosowany w bibliotece Pro- totype oraz we wszystkich frameworkach, które z niej korzystają. Powyższe rozwiązanie działa dobrze w zakresie jednego kontekstu skryp- towego. Jeśli jednak skrypt zostanie wyczytany do osobnego elementu <iframe>, to spowoduje to utworzenie drugiego egzemplarza singletonu. Problem ten można jednak rozwiązać, jawnie żądając, by obiekt singletonu był pobierany z dokumentu głównego (w języku JavaScript odwołanie do tego dokumentu można pobrać przy wykorzystaniu właściwości top). Takie rozwiązanie przed- stawiliśmy na listingu 3.4.
  • 32. 114 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax Listing 3.4. Obiekt TradingMode jako singleton zaimplementowany w języku JavaScript Function getTradingMode(){ if (!top.TradingMode){ top.TradingMode=new Object(); top.TradingMode.mode=MODE_RED; top.TradingMode.setMode=function(){ ... } } return top.TradingMode; } Takie rozwiązanie sprawia, że skrypt będzie działał poprawnie nawet w przy- padku dołączania go w różnych elementach <iframe>, a jednocześnie zapewnia unikalność obiektu singletonu. (Jeśli Czytelnik planuje obsługę singletonów w aplikacji, w której może być kilka okien głównych, to konieczne będzie spraw- dzanie właściwości top.opener. Jednak ze względu na ograniczoną objętość niniejszej książki nie możemy szczegółowo opisać tych zagadnień i zostawiamy je jako ćwiczenie do samodzielnego wykonania przez Czytelnika). Zazwyczaj obsługa interfejsu użytkownika nie będzie wymagała od nas sto- sowania singletonów, niemniej jednak mogą one być niezwykle przydatne pod- czas modelowania procesów biznesowych w języku JavaScript. W tradycyjnych aplikacjach internetowych logika biznesowa jest modelowana i wykonywana wyłącznie na serwerze; jednak w aplikacjach wykorzystujących technologię Ajax wszystko się zmienia, więc warto pamiętać o czymś takim jak singletony. Prezentacją wzorca projektowego Singleton kończymy część rozdziału, której zadaniem było bardzo pobieżne przedstawienie praktycznych możliwości, jakie zapewnia nam refaktoryzacja. Oczywiście należy pamiętać, że wszystkie zapre- zentowane przykłady były stosunkowo proste; jednak pomimo to zastosowanie refaktoryzacji do poprawienia przejrzystości kodu pomogło nam wyeliminować kilka jego słabych punktów, które w przyszłości, po powiększeniu się aplikacji, mogłyby jeszcze bardzo dać się nam we znaki. Jednocześnie przedstawiliśmy kilka wzorców projektowych. W następnym podrozdziale zaprezentujemy duży i popularny wzorzec projektowy używany w aplikacjach wykonywanych po stronie serwera i przyjrzymy się, jak za jego pomocą możemy poprawić przejrzystość i elastyczność początkowo bardzo skomplikowanego kodu. 3.3. Model-View-Controller Niewielkie wzorce projektowe, które przedstawiliśmy w poprzednim podrozdziale, mogą być przydatne podczas realizacji konkretnych zadań programistycznych. Jednak istnieją także wzorce opracowywane z myślą o określaniu sposobu organi- zacji całych aplikacji. Czasami określa się je jako wzorce architektoniczne. W tym podrozdziale przeanalizujemy jeden z takich wzorców architektonicznych, który
  • 33. 3.3. Model-View-Controller 115 pomoże nam uporządkować kod aplikacji wykorzystującej technologię Ajax i sprawi, że zarówno jej tworzenie, jak i późniejsza pielęgnacja, będą łatwiejsze. Wzorzec Model-View-Controller (w skrócie: MVC, Model-Widok-Kontroler) jest doskonałym sposobem opisu wzajemnej separacji fragmentów programu odpowiedzialnych za obsługę interakcji z użytkownikiem oraz za wykonywanie różnych poważnych, „biznesowych” operacji. Wzorze MVC jest zazwyczaj stosowany w dużej skali — obejmuje całe war- stwy aplikacji, a czasami nawet kilka warstw. W tym podrozdziale opiszemy go i pokażemy, w jaki sposób można go zastosować do zaimplementowania serwe- rowej części aplikacji, która będzie zwracać do niej dane w odpowiedzi na żądania przesyłane asynchronicznie przy użyciu technologii Ajax. W rozdziale 4. zajmiemy się nieco trudniejszym zagadnieniem, jakim jest zastosowanie wzorca MVC w kodzie pisanym w języku JavaScript i wykonywanym po stronie przeglądarki. Wzorzec MVC określa trzy role, jakie mogą pełnić komponenty programu. Model jest reprezentacją dziedziny problemu, czyli tym, czym ma się zajmować aplikacja. Np. w edytorze tekstów modelem będzie dokument, a w elektro- nicznej mapie — punkty na siatce, linie itd. Drugim elementem wzorca MVC jest widok. Jest to ta część programu, która odpowiada za prezentację — to ona wyświetla na ekranie elementy formularzy, obrazki, teksty oraz wszelkie inne kontrolki. Nie należy jednak sądzić, że widoki mogą mieć jedynie charakter graficzny. Np. w aplikacjach sterowanych głosem widokiem będą komunikaty dźwiękowe generowane przez aplikację. Jedna z podstawowych zasad wzorca MVC zaleca, iż model i widok nie po- winny się „komunikować” ze sobą w sposób bezpośredni. Łatwo zauważyć, iż programy składające się wyłącznie z modelu i widoku byłby raczej mało przy- datne, dlatego też całość rozwiązania dopełnia trzeci element wzorca MVC — kontroler. Kiedy użytkownik kliknie przycisk lub wypełni pola formularza, widok poinformuje o tym fakcie kontroler. Kontroler wykona stosowne operacje na modelu i określi, czy zmiany, jakie zostały wprowadzone w modelu, wyma- gają aktualizacji widoku. Jeśli taka aktualizacja okaże się konieczna, to kontroler wydaje odpowiednie polecenie widokowi (patrz rysunek 3.5). Rysunek 3.5. Podstawowe komponenty wzorca Model-View-Controller. Model oraz widok nie wymieniają informacji pomiędzy sobą w sposób bezpośredni, lecz za pośrednictwem kontrolera. Kontroler można sobie wyobrazić jako cienką warstwę pozwalającą na wymianę danych pomiędzy modelem i widokiem, która jednocześnie wymusza precyzyjne rozdzielenie obowiązków pomiędzy poszczególnymi fragmentami kodu, poprawiając w ten sposób jego elastyczność i ułatwiając utrzymanie
  • 34. 116 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax Zaleta tego rozwiązania polega na tym, iż widok i model są ze sobą powiązane w bardziej luźny sposób, czyli żaden z tych elementów aplikacji nie musi „wie- dzieć” zbyt wiele na temat drugiego. Oczywiście, powinny one „wiedzieć” na tyle dużo, by mogły realizować swoje zadania, niemniej jednak muszą dyspo- nować jedynie bardzo ogólną znajomością modelu. Przeanalizujmy przykład programu do zarządzania stanem magazynu. W takim programie kontroler może udostępniać widokowi funkcję zwracającą listę wszyst- kich linii produktów należących do kategorii o podanym identyfikatorze; jednak sam widok nie „wie”, w jaki sposób ta lista jest tworzona. Może się okazać, że w pierwszej wersji programu dane niezbędne do wygenerowania takiej listy są gromadzone w pamięci lub odczytywane z pliku. Z kolei w drugiej wersji pro- gramu pojawił się wymóg obsługi bardzo dużych zbiorów danych, w związku z tym do architektury systemu został dodany serwer relacyjnej bazy danych. Z punktu widzenia modelu zmiany, jakich trzeba dokonać w celu obsługi nowego źródła danych, są bardzo duże. Niemniej jednak, jeśli założymy, że kontroler wciąż będzie przekazywał widokowi listę linii produktów spełniającą zadane kryte- ria, to okaże się, że w kodzie widoku nie trzeba wprowadzać żadnych zmian. Podobnie projektanci pracujący nad widokiem powinni być w stanie popra- wiać i rozszerzać jego możliwości funkcjonalne bez obawy, że ich zmiany spo- wodują wystąpienia jakichś problemów w modelu, oczywiście o ile tylko będą stosowali prosty interfejs, jaki udostępnia im kontroler. A zatem, dzieląc cały system na trzy elementy, model MVC jest swoistą „polisą ubezpieczeniową”, która zabezpiecza nas przed sytuacją, gdy jakaś drobna zmiana zmusi nas do wprowadzania wielu innych zmian w całym kodzie aplikacji. Oprócz tego wzorzec ten zapewnia możliwość szybkiego i sprawnego działania zespołów pracujących nad poszczególnymi elementami aplikacji i minimalizuje niebezpieczeństwo, że będą one sobie wzajemnie przeszkadzać. Pewien szczególny sposób wykorzystania wzorca MVC jest bardzo często stosowany we frameworkach obsługujących klasyczne aplikacje internetowe. Otóż frameworki te generują statyczne strony WWW stanowiące interfejs użyt- kownika. W aplikacjach wykorzystujących technologię Ajax, działających i przesy- łających żądania na serwer, proces zwracania danych przypomina nieco działanie klasycznych aplikacji internetowych. Oznacza to, że także aplikacje wykorzy- stujące technologię Ajax mogą skorzystać na zastosowaniu tej specyficznej wersji wzorca MVC. Ponieważ wzorzec ten jest powszechnie znany, nasze rozważania zaczniemy właśnie do niego, by później przejść do innych jego zastosowań, bardziej charakterystycznych dla aplikacji wykorzystujących technologię Ajax. Jeśli Czytelnik nie zetknął się jeszcze z frameworkami używanymi do two- rzenia aplikacji internetowych, to w tej części rozdziału może znaleźć informacje, które pomogą mu zrozumieć, dlaczego narzędzia te poprawiają solidność i ska- lowalność aplikacji. Jeśli jednak Czytelnik zna narzędzia i technologie używane do tworzenia wielowarstwowych aplikacji internetowych, takie jak mechanizmy obsługi szablonów, mechanizmy odwzorowań obiektowo-relacyjnych (określane skrótowo jako mechanizm ORM), czy też frameworki, takie jak Struts, Spring lub Tapestry, to zapewne będzie doskonale wiedział, o czym będziemy tu mówić.
  • 35. 3.4. Wzorzec MVC stosowany na serwerze 117 W takim przypadku Czytelnik może pominąć dalszą część rozdziału i kontynu- ować lekturę od rozdziału 4., w którym zajmiemy się całkowicie innymi sposo- bami wykorzystania wzorca MVC. 3.4. Wzorzec MVC stosowany na serwerze Wzorzec MVC jest od dawna stosowany w aplikacjach internetowych i to nawet w tych starych, bazujących na „normalnych” stronach, które tak mocno krytyku- jemy w niniejszej książce. Sam charakter tych aplikacji wymusza istnienie pewnej separacji pomiędzy widokiem i modelem, gdyż są one obsługiwane na dwóch różnych komputerach. A zatem czy aplikacje internetowe same w sobie są zgodne ze wzorcem MVC? Albo, ujmując to w inny sposób: czy można napisać aplikację internetową, w której role widoku i modelu zostaną połączone? Niestety, taka możliwość istnieje. Co więcej, można to zrobić bardzo łatwo i większość programistów tworzących takie aplikacje, włącznie z autorami niniej- szej książki, kiedyś tak postąpiła. Przeważająca większość zwolenników stosowania wzorca MVC w aplikacjach internetowych traktuje wygenerowaną stronę HTML, jak również kod, który ją generuje, jako widok. Nie uważają oni, że widokiem jest jedynie to, co zobaczą na ekranie po wygenerowaniu strony. W przypadku aplikacji wykorzystujących technologię Ajax, przekazujących dane do kodu JavaScript wykonywanego w prze- glądarce, widokiem jest dokument XML generowany przez serwer i zwracany w odpowiedzi na żądanie HTTP. Oznacza to, że rozdzielenie wygenerowanego kodu od logiki biznesowej będzie wymagać pewnej dyscypliny. 3.4.1. Serwerowa część aplikacji tworzona bez użycia wzorców Aby zilustrować analizowane zagadnienia na przykładzie, napiszemy fragment aplikacji wykorzystującej technologię Ajax działający po stronie serwera. Już wcześniej, rozdziale 2. oraz w podpunkcie 3.1.4, przedstawiliśmy proste przy- kłady kodu JavaScript wykorzystującego możliwości Ajaksa; kolejne przykłady przedstawimy w rozdziale 4. Na razie jednak skoncentrujemy się na tym, co się dzieje po stronie serwera WWW. Kod serwerowej części aplikacji napiszemy najpierw w najprostszy możliwy sposób, a potem będziemy go stopniowo prze- kształcać w postać zgodną ze wzorcem MVC. W ten sposób pokażemy, jakie zalety — pod względem łatwości wprowadzania różnego typu zmian — posia- da ten wzorzec. Zacznijmy zatem od przedstawienia aplikacji. Otóż dysponujemy listą ubrań oferowanych w sklepie odzieżowym; lista ta jest przechowywana w bazie danych, a my chcemy przeszukiwać tę bazę i wy- świetlać użytkownikom listę dostępnych produktów, prezentując dla każdego z nich obrazek, nazwę, krótki opis oraz cenę. Jeśli konkretny produkt będzie dostępny w kilku różnych kolorach, to chcemy, aby użytkownik mógł także określić kolor zamawianego produktu. Podstawowe komponenty tego systemu
  • 36. 118 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax zostały przedstawione na rysunku 3.6; są to: baza danych, struktura danych re- prezentujących konkretny produkt, dokument XML jaki ma być przesłany do przeglądarki oraz lista wszystkich produktów spełniających podane kryteria wyszukiwania. Rysunek 3.6. Podstawowe komponenty systemu używane w naszej przykładowej aplikacji sklepu internetowego do generacji danych XML. W ramach procesu generacji widoku pobieramy zbiór wyników z bazy danych, następnie na jego podstawie wypełniamy struktury danych reprezentujące poszczególne produkty, a w końcu przesyłamy wszystkie dane do klienta w postaci dokumentu XML Załóżmy, że użytkownik właśnie wszedł na witrynę naszego sklepu i może wejść do jednego z trzech głównych działów: odzieży męskiej, kobiecej oraz dziecięcej. Każdy produkt w bazie jest przypisany do jednej z tych trzech kategorii; odpo- wiada za to kolumna Category umieszczona w tabeli Garments. Poniżej przed- stawiliśmy proste polecenie SQL pobierające wszystkie produkty dostępne w kategorii odzieży męskiej: SELECT * FROM garments WHERE CATEGORY = 'Menswear'; Naszym zadaniem jest pobranie wyników tego zapytania i przesłanie ich w postaci kodu XML do przeglądarki. Zobaczmy zatem, jak można to zrobić. Generacja danych XML w celu ich przesłania do przeglądarki Listing 3.5 ilustruje szybkie i niezbyt „eleganckie” rozwiązanie przedstawione- go wcześniej wymagania. Skrypt został napisany w języku PHP i korzysta z ba- zy danych MySQL. Jednak akurat ten jego aspekt nie ma większego znaczenia — w przypadku tego skryptu ważna jest jego ogólna struktura. Skrypty pisane w innych językach: ASP, JSP czy też Ruby, mogłyby zostać napisane w bardzo podobny sposób. Listing 3.5. Szybkie rozwiązanie problemu generacji danych XML na podstawie wyników zapytania <?php header("Content-type: application/xml"); ← Informujemy, że zwracany będzie kod XML echo "<?xml version="1.0" encoding="UTF-8" ?>n";
  • 37. 3.4. Wzorzec MVC stosowany na serwerze 119 $db=mysql_connect("my_db_server","mysql_user"); Pobranie mysql_select_db("mydb",$db); danych z bazy $sql="SELECT id,title,description,price,colors,sizes" ."FROM garments WHERE category="{$cat}""; $result=mysql_query($sql,$db); echo "<garments>n"; while ($myrow = mysql_fetch_row($result)) { ← Przejrzenie zwróconych wyników printf("<garment id="%s" title="%s">n" ."<description>%s</description>n<price>%s</price>n", $myrow["id"], $myrow["title"], $myrow["description"], $myrow["price"]); if (!is_null($myrow["colors"])){ echo "<colors>{$myrow['colors']}</colors>n"; } if (!is_null($myrow["sizes"])){ echo "<sizes>{$myrow['sizes']}</sizes>n"; } echo "</garment>n"; } echo "</garments>n"; ?> Powyższa strona PHP będzie generować kod XML przypominający ten przed- stawiony na listingu 3.6, przy czym w tym przypadku widać, że z bazy zostały pobrane dwa produkty. Dla poprawienia czytelności listingu dodaliśmy do niego odpowiednie wcięcia. Zdecydowaliśmy się przekazywać dane do klienta w formie dokumentu XML, ponieważ jest to bardzo często stosowane rozwiązanie, oraz dlatego, że w poprzednim rozdziale pokazaliśmy, jak można pobierać takie dane po stronie klienta, używając do tego celu obiektu XMLHttpRequest. W rozdziale 5. przedstawimy inne możliwości wymiany danych pomiędzy serwerem i klientem. Listing 3.6. Przykładowe wyniki wygenerowane przez skrypt z listingu 3.5 <garments> <garment id="SCK001" title="Golfers’ Socks"> <description>Garish diamond patterned socks. Real wool. Real itchy.</description> <price>$5.99</price> <colors>heather combo,hawaiian medley,wild turkey</colors> </garment> <garment id="HAT056" title="Deerstalker Cap"> <description>Complete with big flappy bits. As worn by the great detective Sherlock Holmes. Pipe is model’s own.</description> <price>$79.99</price> <sizes>S, M, L, XL, egghead</sizes> </garment> </garments>
  • 38. 120 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax A zatem udało nam się stworzyć serwerową część aplikacji internetowej, przy czym założyliśmy, że istnieje już „śliczna” aplikacja działająca po stronie prze- glądarki, która, wykorzystując technologię Ajax, pobiera dane XML. Załóżmy, że wraz z powiększaniem się asortymentu naszego sklepu chcemy dodać do niego kilka nowych kategorii (takich jak: Na szczególne okazje, Codzienna, Dla aktyw- nych), opcję wyszukiwania odzieży „na wybraną porę roku” oraz ewentualnie opcję wyszukiwania po słowie kluczowym i łącze pozwalające na usunięcie wybranych wcześniej produktów. Wszystkie te opcje można by z powodzeniem obsłużyć przy użyciu podobnych dokumentów XML. Zobaczmy zatem, w jaki sposób moglibyśmy użyć istniejącego kodu do obsługi nowych możliwości oraz jakie problemy możemy przy tym napotkać. Problemy wielokrotnego stosowania kodu Istnieje kilka problemów utrudniających ponowne zastosowanie naszego kodu PHP w jego aktualnej postaci. Przede wszystkim zauważmy, że wykonywane przez skrypt zapytanie SQL zostało podane na stałe. Oznacza to, że chcąc po- nownie przeszukać bazę na podstawie kategorii lub słowa kluczowego, będziemy musieli zmienić sposób generacji zapytania SQL. W efekcie możemy uzyskać koszmarną sekwencję instrukcji warunkowych, która stopniowo, wraz z pojawia- niem się coraz to nowych opcji przeszukiwania, będzie stawać się coraz dłuższa. Można sobie jednak wyobrazić jeszcze gorsze rozwiązanie: umieszczanie w klauzuli WHERE zapytania parametru przekazywanego w żądaniu: $sql="SELECT id,title,description,price,colors,sizes" ."FROM garments WHERE ".$sqlWhere; W takim przypadku adres URL używany do wywołania skryptu mógłby mieć następującą postać: garments.php?sqlWhere=CATEGORY="Menswear" Takie rozwiązanie może wprowadzić jeszcze większe zamieszanie w naszym modelu i widoku, gdyż kod prezentacji uzyskuje bezpośrednio dostęp do za- pytania SQL. Oprócz tego stanowi ono jawne zaproszenie do przeprowadzania ataków typu „SQL injection”1 i choć nowe wersje języka PHP posiadają wbu- dowane zabezpieczenia przeciwko takim atakom, to jednak bezkrytyczne pole- ganie na nich byłoby co najmniej nierozważne. Co więcej, także struktura generowanego dokumentu XML została określona na stałe w kodzie PHP — gdzie jest generowana przy użyciu instrukcji printf oraz echo. Istnieje jednak wiele potencjalnych powodów, które mogą nas skło- nić do zmiany formatu danych. Może się np. zdarzyć, że oprócz ceny sprzedaży będziemy chcieli wyświetlić także oryginalną cenę produktu, by przekonać wszystkich niedowiarków, że zakup oferowanych przez nas golfów jest naprawdę świetnym interesem! 1 Ataki tego typu polegają na przekazywaniu tak spreparowanych danych, że po zastosowaniu ich w poleceniu SQL atakujący uzyska dostęp do tych, których autor skryptu nie planował ujawniać. — przyp. tłum.
  • 39. 3.4. Wzorzec MVC stosowany na serwerze 121 W końcu ostatni problem wynika z faktu, iż dane XML są generowane bez- pośrednio na podstawie informacji pobranych z bazy. Można by sądzić, iż jest to wydajne rozwiązanie, jednak w rzeczywistości jest ono potencjalnym źródłem dwóch problemów. Przede wszystkim podczas generowania kodu XML połącze- nie z bazą danych jest cały czas otwarte. W tym konkretnym przypadku wewnątrz pętli while nie wykonujemy żadnych bardzo skomplikowanych operacji, zatem połączenie z bazą nie będzie otwarte zbyt długo. Niemniej jednak w przyszłości takie przetwarzanie wyników przy otwartym połączeniu z bazą może się stać wąskim gardłem całego systemu. Poza tym rozwiązanie to będzie działać wyłącznie w przypadku gdy będziemy traktować naszą bazę jako płaską strukturę danych. 3.4.2. Refaktoryzacja modelu dziedziny Używany przez nas obecnie sposób przechowywania listy kolorów i rozmiarów odzieży nie jest szczególnie wydajny — są one przechowywane w polach tabeli Garments jako łańcuchy znaków zawierające wartości oddzielone od siebie prze- cinkami. Jeśli znormalizujemy naszą bazę danych i zastosujemy się do zaleceń związanych z tworzeniem dobrych, relacyjnych modeli danych, to okaże się, że konieczne będzie utworzenie dwóch dodatkowych tabel — pierwszej, zawie- rającej listę wszystkich dostępnych kolorów oraz drugiej, kojarzącej konkretne produkty z kolorami (definiującej coś, co specjaliści od baz danych nazywają relacją „wiele-do-wielu”). Zastosowanie takiej relacji zostało przedstawione na rysunku 3.7. Rysunek 3.7. Relacje wiele-do-wielu w tabelach bazy danych. W tabeli Colors przechowywane są wszystkie kolory, jakie mogą mieć produkty, dzięki temu w tabeli Garments nie muszą być już przechowywane żadne informacje o kolorach Aby określić listę kolorów, w jakich jest dostępna oferowana w sklepie czapeczka z dwoma daszkami, przeglądamy tabelę Garments_to_Colors, używając przy tym klucza zewnętrznego garment_id. Kolejne porównanie, tym razem kolumny color_id z kluczem głównym tabeli Colors, pozwoli nam dowiedzieć się, że czapeczka jest dostępna w kolorach różowym i ciemnofiletowym, lecz nie w ko- lorze ciemnoszarym. Wykonując oba polecenia w odwrotnej kolejności, może- my użyć tabeli Garments_to_Colors do sporządzenia listy wszystkich produktów dostępnych w konkretnym kolorze.
  • 40. 122 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax Obecnie używamy już bazy danych w nieco lepszy sposób, jednak kod SQL konieczny do obsłużenia wszystkich zapytań i ich opcji powoli staje się dosyć długi. Dlatego też zamiast samodzielnego tworzenia długich, łączonych zapytań, dobrze by było, gdybyśmy mogli potraktować nasze produkty jako obiekty, które zawierałyby tablice dostępnych kolorów i rozmiarów. Narzędzia obsługi odwzorowań obiektowo-relacyjnych Na szczęście istnieją biblioteki i narzędzia, które są w stanie przekształcić zbiór danych w obiekty; są to tzw. mechanizmy odwzorowań obiektowo-relacyjnych (ang. Object-Relational Mappig, w skrócie ORM). Narzędzia ORM automatycznie przekształcają obiekty bazy danych w obiekty dostępne dla skryptu PHP i są w stanie „stworzyć” za programistę cały niezbędny kod SQL. Programiści używają- cy języka PHP mogą się przyjrzeć takim rozwiązaniom jak obiekt DB_DataObject, pakiet Easy PHP Data Objects (EZPDO) czy też Metastorage. Programiści używający języka Java mają raczej prosty wybór — świadczy to tym fakt, iż ich przeważająca większość wybiera pakiet Hibernate (aktualnie trwają prace nad udostępnieniem go w wersji dla środowiska .NET). Narzędzia ORM stanowią bardzo obszerne zagadnienie, którym, niestety, nie możemy się w niniejszej książce zająć. Analizując naszą aplikacje pod kątem zastosowania w niej wzorca MVC, można zauważyć, iż użycie mechanizmu ORM daje tę dodatkową zaletę, że do- stajemy do dyspozycji doskonały obiektowy model. Teraz możemy zmienić kod generujący dokument XML w taki sposób, by korzystał z obiektu Garment i w ogóle nie „przejmował” się operacjami na bazie danych, które w całości będą realizo- wane przez mechanizm ORM. Co więcej, dzięki takiemu rozwiązaniu nie jesteśmy już uzależnienie od funkcji służących do obsługi konkretnej bazy danych. Listing 3.7 przedstawia zmodyfikowaną wersję poprzedniego kodu, wykorzystującą mechanizm ORM. Listing 3.7. Model obiektów stosowany w sklepie odzieżowym require_once "DB/DataObject.php"; class GarmentColor extends DB_DataObject { var $id; var $garment_id; var $color_id; } class Color extends DB_DataObject { var $id; var $name; } class Garment extends DB_DataObject { var $id; var $title; var $description; var $price; var $colors; var $category; function getColors(){
  • 41. 3.4. Wzorzec MVC stosowany na serwerze 123 if (!isset($this->colors)){ $linkObject=new GarmentColor(); $linkObject->garment_id = $this->id; $linkObject->find(); $colors=array(); while ($linkObject->fetch()){ $colorObject=new Color(); $colorObject->id=$linkObject->color_id; $colorObject->find(); while ($colorObject->fetch()){ $colors[] = clone($colorObject); } } } return $colors; } } W tym przypadku obiekty biznesowe (czyli model), które będą używane w na- szym przykładowym sklepie, definiujemy w języku PHP, używając do tego klasy PEAR::DB_DataObject. Wymaga ona, żeby klasy definiowane przez programistę dziedziczyły po klasie DB_DataObject. W innych mechanizmach ORM mogą być stosowane zupełnie inne rozwiązania, jednak podstawowa zasada ich działania zawsze jest taka sama i polega na tym, że tworzymy grupę obiektów, które są używane tak samo jak wszystkie inne obiekty i ukrywają przed nami wszystkie problemy i uciążliwości związane ze stosowaniem języka SQL. Oprócz głównej klasy Grament zdefiniowaliśmy także klasę Color oraz metodę pozwalającą na pobieranie kolorów (obiektów Color), w jakich jest dostępny dany produkt (obiekt Garment). W bardzo podobny sposób można by zaimplementować obsługę rozmiarów, jednak dla uproszczenia przykładu pominęliśmy ją tutaj. Ponieważ zastosowana biblioteka nie obsługuje bezpośrednio relacji „wiele-do- wielu”, musimy zdefiniować klasę dla tabeli pośredniej i korzystać z niej w me- todzie getColors(). Pomimo to utworzony tak model obiektów jest stosunkowo kompletny i zrozumiały. A teraz przekonajmy się, w jaki sposób możemy wyko- rzystać ten model obiektów w naszym skrypcie. Zastosowanie poprawionego modelu Wygenerowaliśmy nowy model obiektów, bazujący na nowej, bardziej przej- rzystej strukturze bazy danych. Teraz musimy go użyć w kodzie skryptu PHP. Listing 3.8 przedstawia poprawiony kod strony, w której zastosowaliśmy nasze nowe klasy. Listing 3.8. Zmodyfikowana strona obsługująca bazę danych przy wykorzystaniu mechanizmu ORM <?php header("Content-type: application/xml"); echo "<?xml version="1.0" encoding="UTF-8" ?>n"; include "garment_business_objects.inc"; $garment=new Garment;
  • 42. 124 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax $garment->category = $_GET["cat"]; $number_of_rows = $garment->find(); echo "<garments>n"; while ($garment->fetch()) { printf("<garment id="%s" title="%s">n" ."<description>%s</description>n<price>%s</price>n", $garment->id, $garment->title, $garment->description, $garment->price); $colors=$garment->getColors(); if (count($colors)>0){ echo "<colors>n"; for($i=0;$i<count($colors);$i++){ echo "<color>{$colors[$i]}</color>n"; } echo "</colors>n"; } echo "</garment>n"; } echo "</garments>n"; ?> Na początku kodu skryptu dołączamy definicje klas, a w kolejnych wykonywanych operacjach posługujemy się już wyłącznie obiektami naszego modelu. Zamiast tworzenia jakiegoś doraźnego kodu SQL tworzymy pusty obiekt Garment i zapi- sujemy w nim kryteria wyszukiwania, jakimi dysponujemy. Ponieważ model obiektów jest dołączany w formie pliku zewnętrznego, będziemy go mogli używać także na innych stronach, obsługujących inne zapytania. Także kod XML sta- nowiący widok naszej aplikacji jest generowany na podstawie obiektów. Kolejną czynnością związaną z refaktoryzacją naszej aplikacji będzie oddzielenie formatu kodu XML od procesu, który ten kod generuje. 3.4.3. Separacja zawartości od prezentacji Widok naszej aplikacji wciąż jest powiązany z obiektem, gdyż z nim powiązany jest generowany kod XML. Jeśli aplikacja ta będzie się składać z kilku stron, to nie będziemy mogli zmienić formatu kodu XML w jednym, centralnym miejscu i liczyć na to, że wszystkie strony „zauważą” i uwzględnią te zmiany. W bardziej złożonych rozwiązaniach wykorzystujących kilka różnych formatów dokumentów XML, np. osobny format dla listy informacji skróconych, listy informacji pełnych oraz listy stanu magazynu, zapewne chcielibyśmy zdefiniować każdy z nich tylko jeden raz i dla każdego udostępnić jakieś scentralizowane rozwiązanie pozwala- jące na generację dokumentu XML w konkretnym formacie. Systemy wykorzystujące szablony Jednym z bardzo popularnych rozwiązań problemów tego typu jest użycie sys- temu pozwalającego na tworzenie dokumentów tekstowych zawierających pewne znaczniki, które podczas przetwarzania są zastępowane wartościami odpowiednich
  • 43. 3.4. Wzorzec MVC stosowany na serwerze 125 zmiennych. Skrypty PHP, ASP oraz JSP same są językami przetwarzającymi szablony, gdyż można je zapisać w formie strony HTML zawierającej znaczniki, które w odpowiednich miejscach wstawiają podane wartości. Ten sposób działa- nia odróżnia szablony od serwletów Javy oraz tradycyjnych skryptów CGI, które zawierają sekwencje instrukcji generujących kod HTML. Języki te pozwalają na stosowanie w szablonach wszystkich swoich możliwości, dzięki czemu tak łatwo można połączyć logikę biznesową z prezentacją. Z kolei w specjalistycznych językach przetwarzania szablonów, takich jak Smarty używany języku PHP lub Apache Velocity (system napisany oryginalnie w języku Java, a później przeniesiony także na platformę .NET pod nazwą NVelo- city), możliwości tworzonego kodu są zazwyczaj znacząco mniejsze i ograniczają się do kontroli przepływu sterowania (np. instrukcji warunkowych if) oraz pętli (takich jak for i while). Listing 3.9 przedstawia szablon Smarty generujący do- kument XML używany w naszej przykładowej aplikacji. Listing 3.9. Szablon Smarty służący do generacji kodu XML <?xml version="1.0" encoding="UTF-8" ?> <garments> {section name=garment loop=$garments} <garment id="{$garment.id}" title="{$garment.title}"> <description>{$garment.description}</description> <price>{$garment.price}</price> {if count($garment.getColors())>0} <colors> {section name=color loop=$garment.getColors()} <color>$color->name</color> {/section} </colors> {/if} </garment> {/section} </garments> Powyższy szablon „oczekuje”, że jako dane wejściowe zostanie do niego prze- kazana tablica o nazwie garments, zawierająca obiekty Garment. Przeważająca część szablonu jest generowana w oryginalnej postaci, jednak jego fragmenty umieszczone w nawiasach klamrowych są interpretowane jako instrukcje i za- stępowane odpowiednimi zmiennymi bądź traktowane jako pętle lub instrukcje warunkowe. Struktura generowanego kodu XML jest znacznie wyraźniejsza w szablonie niż w kodzie skryptowym, o czym łatwo można się przekonać, po- równując powyższy kod z listingiem 3.7. A teraz zobaczmy, w jaki sposób moż- na wykorzystać taki szablon w naszym skrypcie. Poprawiony widok Udało nam się przenieść definicje formatu XML z głównego skryptu do nieza- leżnego pliku szablonu. W efekcie, musimy teraz w skrypcie jedynie zainicjować mechanizm obsługi szablonów i przekazać do niego odpowiednie dane. Zmiany, jakie należy w tym celu wprowadzić, przedstawiliśmy na listingu 3.10.
  • 44. 126 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax Listing 3.10. Zastosowanie mechanizmu Smarty do generacji kodu XML <?php header("Content-type: application/xml"); include "garment_business_objects.inc"; include "smarty.class.php"; $garment=new DataObjects_Garment; $garment->category = $_GET["cat"]; $number_of_rows = $garment->find(); $smarty=new Smarty; $smarty->assign('garments',$garments); $smarty->display('garments_xml.tpl'); ?> Sposób stosowania mechanizm Smarty jest bardzo zwięzły i składa się z trzech etapów. Pierwszy z nich polega na utworzeniu samego mechanizmu. W ramach drugiego etapu należy określić wartości zmiennych, które będą potrzebne do wygenerowania szablonu. W naszym przypadku będzie to tylko jedna zmienna, jednak możemy ich określić dowolnie wiele — gdyby w sesji były przechowy- wane jakieś dane dotyczące użytkownika, to moglibyśmy je także przekazywać, by wyświetlić np. spersonalizowane powitanie. W końcu wywołujemy metodę display(), przekazując do niej nazwę pliku szablonu. Jak widać, udało się nam szczęśliwie odseparować widok od samej strony pobierającej dane z bazy. Postać dokumentu XML jest definiowana tylko jeden raz, a wykonanie jego generacji zajmuje nam kilka wierszy kodu. Strona z wy- nikami wyszukiwania jest ścisła i precyzyjna — zawiera tylko te informacje, które są jej potrzebne, konkretnie rzecz biorąc, są to wartości parametrów wyszuki- wania oraz dane określające format wyników. Być może Czytelnik pamięta, że wcześniej wspominaliśmy, iż byłoby cudownie, gdybyśmy mogli w dowolnej chwili zmieniać format XML używany do generacji danych wynikowych. Dzięki zastosowaniu mechanizmu Smarty nie przysparza nam to najmniejszych proble- mów — wystarczy go zdefiniować. Mechanizm Smarty zapewnia nawet możliwość wstawiania jednych szablonów wewnątrz innych, z której możemy skorzystać, jeśli zechcemy generować dane wynikowe w sposób bardzo strukturalny. Jeśli przyjrzymy się informacjom podanym na samym początku analizy wzorca MVC, przekonamy się, iż implementujemy go we właściwy sposób. Na rysunku 3.8 przedstawiliśmy graficznie to, co do tej pory udało się nam osiągnąć. Nasz model jest kolekcją obiektów dziedziny, które są automatycznie zapi- sywane w bazie danych przy wykorzystaniu mechanizmu ORM. Rolę widoku pełni szablon określający postać generowanego kodu XML. I w końcu kontrole- rem jest strona PHP służąca do wyszukiwania produktów według kategorii, jak również wszystkie inne strony, które zdecydujemy się zdefiniować i które będą łączyć model oraz widok. W taki sposób wygląda klasyczne zastosowanie wzorca MVC w aplikacjach internetowych. W prezentowanym przykładzie zastosowaliśmy ten wzorzec w serwerowej części aplikacji wykorzystującej technologię Ajax, która generuje
  • 45. 3.4. Wzorzec MVC stosowany na serwerze 127 Rysunek 3.8. Wzorzec MVC w postaci często stosowanej w aplikacjach internetowych. Strona WWW lub serwlet działają jako kontroler i w pierwszej kolejności pobierają odpowiednie dane z obiektów modelu. Następnie przekazują te dane do pliku szablonu (widoku), który z kolei generuje zawartość przesyłaną do przeglądarki użytkownika. Warto zauważyć, że jest to rozwiązanie obsługujące jedynie operacje odczytu. W przypadku modyfikacji modelu sekwencja wykonywanych czynności i zdarzeń byłaby nieco inna, niemniej jednak role poszczególnych elementów pozostałyby takie same dokumenty XML. Niemniej jednak bez trudu można sobie wyobrazić, w jaki sposób można by zastosować ten sam wzorzec w klasycznej aplikacji interne- towej, generującej dokumenty HTML. W zależności od używanych technologii Czytelnik będzie mógł się spotkać z różnymi wersjami przedstawionego wzorca MVC; jednak zawsze podstawo- we zasady jego działania i konstrukcji będą identyczne. Np. komponenty EJB (ang. Enterprise JavaBean) używane w technologii J2EE definiują model oraz kontroler w taki sposób, by można je było przechowywać na różnych serwerach. Z kolei na platformie .NET rolę kontrolerów pełnią specjalne „ukryte” obiekty definiowane osobno dla poszczególnych stron. Jeszcze inaczej sytuacja wygląda we frameworkach takich jak Struts, które definiują tak zwany „kontroler frontonu” (ang. front controller) przechwytujący wszystkie żądania kierowane do aplikacji i przekazujący je w odpowiednie miejsca. W takich frameworkach działanie kon- trolera zostało zmodyfikowane tak, by nie tylko obsługiwał konkretne strony, lecz także zarządzał przemieszczaniem się użytkownika pomiędzy poszczegól- nymi stronami aplikacji. (W aplikacji wykorzystującej technologię Ajax podobne możliwości możemy zaimplementować przy użyciu języka JavaScript). Jednak we wszystkich tych przypadkach postać wzorca MVC jest w zasadzie taka sama i zakłada się, że właśnie tak powinno się ją rozmieć w kontekście aplikacji inter- netowych. Opisanie architektury naszej aplikacji przy użyciu wzorca MVC jest przy- datnym rozwiązaniem, a co więcej, będziemy mogli z niego korzystać, nawet jeśli zdecydujemy się wzbogacić klasyczną aplikację internetową o wykorzystanie technologii Ajax. Warto jednak zwrócić uwagę, iż nie jest to jedyna możliwość zastosowania wzorca MVC w aplikacjach używających Ajaksa. W rozdziale 4. przedstawimy inne wersje tego wzorca, dzięki którym jego zalety będziemy mogli wykorzystać także w innych miejscach aplikacji. Zanim to jednak zrobimy, przyj- rzyjmy się innym sposobom zapewniania porządku i organizacji w aplikacjach wykorzystujących Ajaksa.
  • 46. 128 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax Oprócz modyfikowania i poprawiania własnego kodu całość kodu aplikacji możemy często uprościć i skrócić poprzez zastosowanie różnego typu frame- worków i bibliotek. Wraz z ciągłym wzrostem zainteresowania i popularności technologii Ajax rośnie także ilość wykorzystujących ją frameworków i biblio- tek. Kilka najpopularniejszych rozwiązań tego typu przedstawiliśmy na końcu niniejszego rozdziału. 3.5. Biblioteki i frameworki Jednym z podstawowych celów refaktoryzacji jest usuwanie powielającego się kodu poprzez umieszczanie go w funkcjach i obiektach, których będzie można używać w wielu miejscach aplikacji. Rozwijając tę logikę działania, takie wielo- krotnie używane możliwości funkcjonalne można umieścić w bibliotece (lub frameworku), której następnie będzie można używać także w innych projektach. W ten sposób można zmniejszyć ilość unikalnego kodu, jaki należy napisać pod- czas prac nad projektem, a tym samym poprawić wydajność pracy. Co więcej, ponieważ taka biblioteka została przetestowana podczas tworzenia wcześniej- szych projektów, można oczekiwać, że jej jakość będzie wysoka. W dalszej części niniejszej książki stworzymy kilka niewielkich frameworków, które Czytelnik będzie mógł zastosować we własnych projektach: w rozdzia- łach 4. i 5. stworzymy obiekt ObjectBrowser, w rozdziale 5. zaprezentujemy także obiekt CommandQueue, w rozdziale 6. — framework służący do prezentacji powia- domień, w rozdziale 8. — mechanizm profilujący StopWatch, a w Dodatku A — konsolę testującą. Będziemy także modyfikować aplikacje przykładowe, two- rzone w rozdziałach od 9. do 13., przekształcając je w postać komponentów nadających się do wielokrotnego stosowania. Oczywiście nie tylko my zajmujemy się tworzeniem takich rozwiązań — obec- nie na WWW dostępnych jest bardzo wiele frameworków ułatwiających pisanie aplikacji obsługiwanych przy użyciu skryptów JavaScript i wykorzystujących technologię Ajax. Najpopularniejsze z nich mają tę ogromną zaletę, iż zostały bardzo dokładnie przetestowane przez wielu programistów. W tym podrozdziale przedstawimy kilka bibliotek i frameworków, po które mogą sięgnąć osoby tworzące aplikacje wykorzystujące technologię Ajax. Obecnie frameworki do takich aplikacji rozwijają się bardzo dynamicznie, dlatego też nie jesteśmy w stanie szczegółowo opisać każdego z rozwiązań dostępnych na rynku; spróbujemy jednak pokazać, jakie rodzaje frameworków są dostępne, oraz w jaki sposób zastosowanie ich w tworzonych aplikacjach może ułatwić wprowadzanie porządku w pisanym przez nas kodzie. 3.5.1. Biblioteki zapewniające poprawne działanie skryptów w różnych przeglądarkach W punkcie 3.2.1 wspominaliśmy o tym, iż aplikacje wykorzystujące technologię Ajax muszą być pisane w nieco inny sposób — w zależności od przeglądarki, w jakiej mają działać. Wiele bibliotek ułatwia programistom zdanie, udostępniając
  • 47. 3.5. Biblioteki i frameworki 129 wspólną fasadę, która niweluje rozbieżności pomiędzy poszczególnymi prze- glądarkami i zapewnia możliwość korzystania z technologii Ajax w jeden spójny sposób. Niektóre z nich koncentrują się na ściśle określonych możliwościach funkcjonalnych, natomiast inne starają się stworzyć środowiska programistyczne o znacznie większym zakresie. W dalszej części rozdziału opisaliśmy te spośród dostępnych bibliotek, które uznaliśmy na za najbardziej przydatne i użyteczne podczas pisania aplikacji wykorzystujących technologię Ajax. Biblioteka x Biblioteka x jest dojrzałą biblioteką ogólnego przeznaczenia, służącą do tworzenia aplikacji DHTML. Opublikowana po raz pierwszy w 2001 roku, zastąpiła wcze- śniejszą bibliotekę autora o nazwie CBE (ang. Cross-Browser Extensions) i wyko- rzystuje znacznie prostszy styl programowania. Biblioteka ta udostępnia działające w wielu przeglądarkach funkcje pozwalające na manipulację oraz określanie stylów elementów DOM, obsługę modelu zdarzeń przeglądarki oraz gotowe funkcje do tworzenia animacji oraz obsługi operacji typu „przeciągnij-i-upuść”. Można ją z powodzeniem stosować w przeglądarce Internet Explorer 4 (i now- szych), jak również w nowych wersjach Opery i przeglądarek fundacji Mozilla. Biblioteka ta używa prostego stylu programowania opierającego się na wy- korzystaniu funkcji i korzysta z dostępnej w języku JavaScript możliwości prze- kazywania do funkcji różnej ilości argumentów oraz braku rygorystycznej kontroli typów. Przesłania ona np. standardową metodę document.getElementById() wyma- gającą przekazywania łańcucha znaków bardziej elastyczną funkcją, która po- zwala na przekazywanie zarówno łańcuchów znaków, jak i elementów DOM. W razie przekazania łańcucha znaków funkcja działa normalnie, czyli odnajduje element o podanym identyfikatorze, jednak w przypadku przekazania elementu DOM funkcja nie wykonuje żadnych operacji i zwraca element przekazany w jej wywołaniu. Możliwość użycia elementu DOM zamiast jego identyfikatora jest szczególnie przydatna i użyteczna podczas stosowania dynamicznie genero- wanego kodu, np. kodu przekazywanego w wywołaniu metody setTimeout() lub podczas określania funkcji zwrotnych. Podobny, zwięzły styl jest używany także w metodach służących do okre- ślania stylów elementów DOM. W tym przypadku dokładnie ta sama funkcja służy zarówno do odczytu wartości stylu, jak i jej zapisu. Np. wywołanie o postaci: xWidth(myElement); zwróci szerokość elementu DOM określonego przez zmienną myElement; przy czym zmienna ta może zawierać zarówno sam element DOM, jak i jego identy- fikator. Jednak umieszczając w powyższym wywołaniu dodatkowy argument, np.: xWidth(myElement, 420); jesteśmy w stanie określić szerokość wskazanego elementu. A zatem, aby nadać jednemu elementowi taką samą szerokość, jaką ma inny, możemy użyć wywołania: xWidth(element2, xWidth(element1));
  • 48. 130 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax Biblioteka x nie zawiera żadnego kodu służącego do tworzenia i obsługi połą- czeń sieciowych, niemniej jednak z powodzeniem można jej użyć do tworzenia interfejsu użytkownika aplikacji wykorzystujących technologię Ajax. Warto pod- kreślić, iż została ona napisana w bardzo przejrzysty i zrozumiały sposób. Sarissa W porównaniu z biblioteką x Sarissa jest biblioteką o znacznie węższym obszarze zastosowań — służy ona głównie do obsługi danych XML w skryptach JavaScript. Biblioteka ta obsługuje komponenty ActiveX MSXML (w wersji 3. i wyższych) oraz zapewnia swoje podstawowe możliwości funkcjonalne także w przeglądar- kach fundacji Mozilla, Opera, Konqueror oraz Safari; choć niektóre, bardziej zaawansowane możliwości, takie jak obsługa wyrażeń XPath oraz przekształceń XSLT, nie są dostępne we wszystkich przeglądarkach. Z punktu widzenia aplikacji wykorzystujących technologię Ajax najważniej- szą cechą biblioteki Sarissa jest działająca we wszystkich przeglądarkach moż- liwość tworzenia obiektu XMLHttpRequest. Jednak zamiast stosowania swojej własnej implementacji wzorca Façade, Sarissa używa wzorca Adapter do utwo- rzenia obiektu, który symuluje obiekt XMLHttpRequest w przeglądarkach, jakie go nie udostępniają (głównie chodzi tu o Internet Explorer). Tworzony obiekt będzie oczywiście używał odpowiedniego obiektu ActiveX (który przedstawiliśmy w rozdziale 2.), jednak, z punktu widzenia programisty, po zaimportowaniu biblioteki Sarissa poniższy kod będzie działać na wszystkich przeglądarkach: var xhr = new XMLHttpRequest(); xhr.open("GET", "myData.xml"); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ alert(xhr.responseXML); } } xhr.send(null); Jeśli porównamy powyższy kod z listingiem 2.11, to zauważymy, że używane wywołania są identyczne do tych, używanych w przypadku stosowania wbudo- wanego obiektu XMLHttpRequest dostępnego w przeglądarkach fundacji Mozilla oraz w przeglądarce Opera. Jak już wcześniej wspominaliśmy, Sarissa udostępnia także grupę ogólnych mechanizmów pozwalających na wykonywanie różnego typu operacji na do- kumentach XML. Pozwala ona np. na zapisanie dowolnego obiektu JavaScript w postaci kodu XML. Mechanizmy te mogą się przydać podczas przetwarzania danych XML zwracanych przez serwer, zakładając oczywiście, że zdecydujemy się na przesyłanie kodu XML w odpowiedziach na nasze asynchroniczne żądania. (Szczegółowe informacje dotyczące możliwych sposobów prowadzenia wymiany danych pomiędzy klientem i serwerem zostały opisane w rozdziale 5.).
  • 49. 3.5. Biblioteki i frameworki 131 Prototype Prototype jest biblioteką ogólnego przeznaczenia wspomagającą pisanie skryptów w języku JavaScript. Jej podstawowym celem jest rozszerzanie samego języka w celu umożliwienia stosowania bardziej obiektowego stylu programowania. Biblioteka ta wykorzystuje unikalny styl tworzenia kodu JavaScript, bazujący na wprowadzanych przez nią rozszerzeniach. Sam kod tworzony dzięki możli- wościom tej biblioteki może być trudny do analizy, gdyż znacznie odbiega on od kodu tworzonego w językach Java i C#, jednak stosowanie zarówno samej biblioteki Prototype, jak i innych bibliotek stworzonych w oparciu o nią jest dosyć proste. Prototype można traktować jako bibliotekę przeznaczoną dla twórców innych bibliotek. Programiści tworzący aplikacje wykorzystujące technologię Ajax nie będą zapewne korzystali bezpośrednio z biblioteki Prototype, lecz z innych bibliotek wykorzystujących jej możliwości. Biblioteki te przedstawimy w dalszej części rozdziału. Na razie jednak zaprezentujemy podstawowe możliwości, jakie daje Prototype, by na ich przykładzie pokazać stosowany w niej styl programowa- nia. Informacje te przydadzą nam się później, podczas opisywania takich roz- wiązań, jak Scriptaculous, Rico oraz Ruby on Rails. Prototype pozwala, aby jeden obiekt rozszerzał drugi — w tym celu wszystkie właściwości i metody obiektu bazowego są kopiowane do obiektu potomnego. Możliwość tę najlepiej jest przedstawić na przykładzie. Załóżmy, że zdefinio- waliśmy klasę bazową o nazwie Vehicle: function Vehicle(numWheels,maxSpeed){ this.numWheels=numWheels; this.maxSpeed=maxSpeed; } Teraz chcemy utworzyć konkretny obiekt tej klasy, który będzie reprezentować pociąg pasażerski. W naszym nowym obiekcie chcemy określić ilość wagonów oraz udostępnić mechanizm ich dodawania i usuwania. W kodzie JavaScript możliwości te można by zaimplementować w następujący sposób: var passTrain=new Vehicle(24,100); passTrain.carriageCount=12; passTrain.addCarriage=function(){ this.carriageCount++; } passTrain.removeCarriage=function(){ this.carriageCount--; } Jak widać, udało nam się zaimplementować w nowym obiekcie wszystkie po- trzebne możliwości funkcjonalne. Jednak analizując ten kod pod względem archi- tektury, można łatwo zauważyć, iż te dodatkowe możliwości funkcjonalne nie zostały umieszczone w jakiejś ogólnie pojętej „jednostce”. Biblioteka Prototype może nam pomóc w rozwiązaniu tego problemu, pozwala ona bowiem na zdefi- niowanie rozszerzonych możliwości w postaci obiektu, a następnie na skopio- wanie ich do obiektu bazowego. W pierwszej kolejności musimy zdefiniować nowy obiekt, posiadający rozszerzone możliwości:
  • 50. 132 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax function CarriagePuller(carriageCount){ this.carriageCount=carriageCount; this.addCarriage=function(){ this.carriageCount++; } this.removeCarriage=function(){ this.carriageCount--; } } Teraz możemy połączyć oba obiekty w jeden, dysponujący wszystkimi oczeki- wanymi możliwościami: var parent=new Vehicle(24,100); var extension=new CarriagePuller(12); var passTrain=Object.extend(parent,extension); Warto zauważyć, że w pierwszej kolejności definiujemy obiekt bazowy oraz obiekt rozszerzający, a dopiero później „scalamy” je ze sobą. W tym przypadku relacja występuje pomiędzy konkretnymi obiektami, a nie pomiędzy klasami Vehicle oraz CarrigePuller. Choć nie jest to klasyczne rozwiązanie obiektowe, niemniej jednak pozwala ono na umieszczenie kodu związanego z pewną funk- cją (taką jak ciągnięcie wagonów) w jednym, precyzyjnie określonym miejscu i bardzo upraszcza jego wielokrotnie stosowanie. Choć wykorzystywanie tych możliwości funkcjonalnych w niewielkim i prostym przykładzie, takim jak ten, może się wydawać raczej niepotrzebne, to jednak w dużych projektach taka hermetyzacja możliwości funkcjonalnych jest niezwykle przydatna. Biblioteka Prototype wspomaga także stosowanie technologii Ajax — udo- stępnia ona klasę Ajax oraz grupę klas potomnych „ukrywających” przed pro- gramistą wszelkie rozbieżności związane z tworzeniem obiektu XMLHttpRequest w różnych przeglądarkach i ułatwiających obsługę żądań. Jedną z takich klas jest klasa Ajax.Request, która pozwala na generowanie żądań przy użyciu obiektu XMLHttpRequest. Poniżej przedstawiliśmy przykład jej użycia: var req=new Ajax.Request('myData.xml'); Powyższy konstruktor używa stylu, który często można zobaczyć także w innych bibliotekach korzystających z Prototype. Otóż pozwala on na przekazanie dodat- kowego argumentu, będącego tablicą asocjacyjną. W ten sposób, w razie koniecz- ności, można określić bardzo wiele aspektów działania tworzonego obiektu. Dla każdej z takich opcji konfiguracyjnych została określona sensowna wartość do- myślna, dzięki czemu przekazywanie takiego dodatkowego obiektu jest konieczne wyłącznie gdy chcemy przesłonić jakieś domyślne ustawienia. W przypadku konstruktora klasy Ajax.Request w tej dodatkowej tablicy można podać: infor- macje, jakie zostaną przesłane na serwer metodą POST, parametry żądania, metodę, jaka zostanie użyta do przesłania żądania na serwer, oraz funkcje zwrotne służące do obsługi odpowiedzi oraz ewentualnych błędów. Poniżej przedstawili- śmy znacznie bardziej rozbudowane wywołanie konstruktora Ajax.Request(): var req=new Ajax.Request( 'myData.xml',
  • 51. 3.5. Biblioteki i frameworki 133 { method: 'get', parameters: { name:'dawid',likes:'czekoladę,rabarbar' }, onLoaded: function(){ alert('plik wczytany!'); }, onComplete: function(){ alert('gotowe!nn'+req.transport.responseText); } } ); W powyższym przykładzie w tablicy zostały określone cztery parametry. Najpierw określiliśmy, że żądanie ma zostać przesłane metodą get; to ważne, gdyż Proto- type domyślnie używa metody post. Ponieważ użyjemy metody get, parametry określone w kolejnym elemencie tablic (parameters) zostaną umieszczone w łańcu- chu zapytania. W przypadku przesłania żądania metodą post parametry te zostały- by zapisane w treści żądania. I w końcu określamy funkcje zwrotne onLoad oraz onComplete, które zostaną wywołane po zmianie stanu właściwości readyState obiektu XMLHttpRequest. Zmienna req.transport zastosowana w kodzie funkcji onComplete zawiera referencję do obiektu XMLHttpRequest. Kolejną klasą związaną z obsługą żądań, udostępnianą przez bibliotekę Pro- totype, jest klasa Ajax.Updater. Obiekty tej klasy pobierają fragmenty kodu Java- Script przesyłane z serwera i przetwarzają je. Rozwiązanie to stanowi przykład „interakcji operujących na skryptach”, które opiszemy w rozdziale 5.; dokład- niejsza prezentacja tego zagadnienia wykracza poza ramy tematyczne niniej- szego rozdziału. Na tym zakończymy prezentację bibliotek ułatwiających tworzenie skryptów działających w wielu różnych przeglądarkach. Przedstawiliśmy jedynie kilka wybranych bibliotek, a zamieszczone tu informacje na pewno nie są wyczerpujące. Należy także pamiętać, iż biblioteki, frameworki oraz wszelkie innego typu na- rzędzia do tworzenia aplikacji wykorzystujących technologię Ajax rozwijają się obecnie bardzo dynamicznie, dlatego też musieliśmy ograniczyć zamieszczone tu informacje jedynie do rozwiązań, które cieszą się największą popularnością i są najlepsze. W następnym punkcie rozdziału przedstawimy szkielety bazujące na kontrolkach, utworzone w oparciu o inne biblioteki (w tym także biblioteki, o których wspominaliśmy wcześniej). 3.5.2. Kontrolki oraz bazujące na nich szkielety Biblioteki, które przedstawiliśmy w poprzedniej części rozdziału, zapewniały bezpieczne i pewne możliwości wykonywania operacji stosunkowo niskiego poziomu, takich jak operowanie na elementach DOM bądź pobieranie doku- mentów z serwera. Dzięki zastosowaniu takich narzędzi na pewno można sobie znacznie ułatwić tworzenie funkcjonalnych interfejsów użytkownika, jednak i tak zmuszają nas one do wykonania znacznej pracy — w porównaniu z takimi bibliotekami, jak Swing, MFC bądź Qt. Powoli zaczynają się jednak pojawiać gotowe kontrolki lub nawet całe pakiety kontrolek, których mogą używać programiści tworzący aplikacje w języku Java- Script. W typu punkcie rozdziału przedstawimy kilka wybranych rozwiązań tego
  • 52. 134 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax typu; chodzi nam raczej o ogólne zaprezentowanie możliwości, jakie stwarzają takie narzędzia, niż o przedstawienie ich wyczerpującego opisu. Scriptaculous Biblioteki Scriptaculous zawierają komponenty graficznego interfejsu użytkow- nika napisane w oparciu o bibliotekę Prototype (patrz poprzedni punkt rozdziału). Koncentrują się one na dwóch podstawowych grupach możliwości funkcjonal- nych, choć są cały czas aktywnie rozwijane. Biblioteka Effects definiuje wiele efektów wizualnych, których można używać do modyfikowania postaci i wyglądu elementów DOM, np. można zmieniać ich położenie, wielkość oraz przezroczystość. Poszczególne efekty może ze sobą łatwo łączyć, a dodatkowo przygotowano grupę gotowych efektów złożonych, takich jak Puff(), który powoduje, że wskazany element staje się coraz większy i bardziej przezroczysty, aż w końcu całkowicie znika. Kolejnym bardzo ważnym i przydatnym efektem jest efekt o nazwie Parallel() — pozwala on na równo- czesne wykonywanie innych, wskazanych efektów. Mogą one stanowić dosko- nały sposób na szybkie dodanie do interfejsu użytkownika aplikacji wizualnych sygnałów zwrotnych; zagadnieniem tym zajmiemy się bardziej szczegółowo w rozdziale 6. Wywołanie jednego z predefiniowanych efektów wizualnych jest niezwykle proste — sprowadza się do wywołania odpowiedniego konstruktora i przeka- zania do niego identyfikatora elementu DOM. Oto przykład: new Effect.SlideDown(myDOMElement); Podstawą tych wszystkich efektów jest obiekt przejścia, którego działanie można parametryzować, np. określając czas trwania przejścia oraz funkcje zwrotne, jakie mają być wykonana na jego początku i końcu. Dostępnych jest kilka podstawo- wych typów przejść, takich jak: linearne, sinusoidalne, pulsujące oraz drgające. Tworzenie własnych efektów sprowadza się do wybrania odpowiedniego efektu podstawowego (lub ich kombinacji) i określenia odpowiednich parametrów. Szczegółowy opis sposobu tworzenia takich efektów wykracza jednak poza ramy tematyczne tej krótkiej prezentacji. Przykład praktycznego zastosowania biblioteki Scriptaculous można znaleźć w rozdziale 6., w którym użyliśmy jej do tworzenia systemu prezentacji powiadomień. Kolejną możliwością, jaką zapewnia biblioteka Scriptaculous jest obsługa operacji typu „przeciągnij-i-upuść”. Do tego celu służy klasa Sortable. Wymaga ona określenia elementu rodzica i umożliwia przeciąganie i upuszczanie wszyst- kich jego elementów potomnych. Przy użyciu opcji podawanych w wywołaniu konstruktora można określić funkcje zwrotne wywoływane w momencie rozpo- czynania i kończenia przeciągania, typy elementów, jakie można przeciągać, oraz listę „celów” przeciągania (czyli elementów, w których można upuszczać przeciągane elementy). Istnieje także możliwość przekazywania jako opcji obiek- tów efektów wizualnych określających, jak element będzie się zachowywać w momencie rozpoczynania przeciągania, podczas samego przeciągania oraz w chwili upuszczania.
  • 53. 3.5. Biblioteki i frameworki 135 Rico Pakiet Rico, podobnie jak biblioteki Scriptaculous, korzysta z możliwości, jakie daje biblioteka Prototype. Rico pozwala na stosowanie operacji „przeciągnij-i- upuść” oraz różnego rodzaju efektów wizualnych, stwarzając przy tym bardzo duże możliwości ich konfigurowania. Oprócz tego Rico udostępnia pojęcie obiektu zachowania — Behavior — czyli fragmentu kodu, który można dodać do fragmentu drzewa DOM, aby później rozszerzać jego działanie. Pakiet zawiera kilka przykładów zastosowania tych obiektów; jednym z nich jest komponent Accordion, który umieszcza kilka elementów DOM w jednym obszarze, a następ- nie pozwala na wyświetlanie zawartości wybranego z nich. (Komponenty o takim działaniu często określa się jako pasek programu Outlook, gdyż zostały one spopularyzowane w programie Microsoft Outlook). Spróbujmy zatem teraz utworzyć prosty przykład komponentu Accordion. W pierwszej kolejności potrzebujemy nadrzędnego elementu DOM — każdy z elementów umieszczonych wewnątrz niego stanie się jednym panelem tworzo- nego komponentu. Każdy panel definiujemy jako element <div>, wewnątrz którego zostają umieszczone dwa kolejne elementy <div> reprezentujące odpo- wiednio: nagłówek oraz treść panelu: <div id='myAccordion'> <div> <div>Definicja</div> <div> <ul> <li><b>n.</b>instrument muzyczny z grupy idiofonów - instrymentów perkusyjnych. Rodzaj harmonii, oparty na stroikach przelotowych. Prąd powietrza, zagęszczonego przez ręcznie poruszany miech, wywołuje dźwięk, wprawiając w drganie stroik (źródłem dźwięku jest zatem drgająca blaszka, a nie słup powietrza - dlatego instrument perkusyjny, nie aerofon).</li> </ul> </div> </div> <div> <div>Rysunek</div> <div> <img src='monkey-accordion.jpg'></img> </div> </div> </div> Pierwszy panel zawiera fragment definicji opisującej, czym jest akordeon, nato- miast drugi przedstawia jego zdjęcie (patrz rysunek 3.9). Powyższy kod HTML wyświetlony w swojej oryginalnej postaci będzie przedstawiał oba panele — jeden nad drugim. Jednak w głównym elemencie naszego kodu określiliśmy identyfika- tor (jako wartość atrybutu id), co pozwala nam przekazać referencję do tego elementu w wywołaniu konstruktora obiektu Accordion. Obiekt ten tworzymy w następujący sposób:
  • 54. 136 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax var outer=$('myAccordion'); outer.style.width='320px'; new Rico.Accordion( outer, { panelHeight:400, expandedBg:'#909090', collapsedBg:'#404040', } ); Pierwszy wiersz powyższego fragmentu kodu wygląda dosyć ciekawie. Okazuje się jednak, że znak $ jest normalną, dopuszczalną nazwą zmiennej w języku JavaScript, a biblioteka Prototype zastosowała go jako nazwę funkcji. A zatem funkcja $() przypomina nieco funkcję xGetElementById(), którą poznaliśmy przy okazji prezentacji biblioteki x. Pobraną referencję do elementu DOM przekazu- jemy w wywołaniu konstruktora obiektu Accordion wraz z dodatkową tablicą opcji. W naszym przypadku opcje określają jedynie style, jakie należy zastosować podczas prezentowania komponentu na stronie; jednak w tablicy opcji można także określić funkcje zwrotne, wywoływane w momencie otwierania i zamykania paneli. Efekty zastosowania obiektu Accordion do określenia postaci zwyczaj- nych elementów <div> zostały przedstawione na rysunku 3.9. Klasa Behavior pakietu Rico pozwala na bardzo łatwe tworzenie komponentów na podstawie kodu HTML, a oprócz tego oddziela ich zawartość od kodu określającego sposób ich działania. Zagadnienia związane ze stosowaniem dobrych i sprawdzonych rozwiązań podczas tworzenia interfejsów użytkownika w języku JavaScript przed- stawimy w rozdziale 4. Ostatnią cechą pakietu Rico, o jakiej chcieliśmy tu wspomnieć, są doskonałe narzędzia do generowania i obsługi asynchronicznych żądań. Do tego celu używany jest globalny obiekt AjaxEngine. Jest on czymś znacznie więcej niż je- dynie „pojemnikiem” ukrywającym w sobie zwyczajny obiekt XMLHttpRequest i pozwalającym na jego stosowanie w różnych przeglądarkach. Obiekt ten defi- niuje format odpowiedzi XML, który powinien się składać z sekwencji elementów <response>. Mechanizm udostępniany przez pakiet Rico potrafi automatycznie dekodować takie odpowiedzi, przy czym rozróżnia dwa ich rodzaje: odpowiedzi modyfikujące bezpośrednio zawartość elementów DOM oraz modyfikujące wartości przechowywane w obiektach JavaScript. Podobne rozwiązania przed- stawimy szczegółowo w punkcie 5.5.3 poświęconym zagadnieniom interakcji pomiędzy klientem a serwerem. Na razie zajmiemy się jednak frameworkami, które spajają części aplikacji działającej na serwerze oraz w kliencie. 3.5.3. Frameworki aplikacji Wszystkie frameworki, jakie przedstawiliśmy wcześniej, działają jedynie w prze- glądarce i można je stosować jako statyczne pliki JavaScript pobierane z serwera. Ostatnią grupą rozwiązań, jakie chcielibyśmy tu przedstawić, są frameworki, które są przechowywane na serwerze, lecz dynamicznie generują fragmenty kodu JavaScript i HTML.
  • 55. 3.5. Biblioteki i frameworki 137 Rysunek 3.9. Obiekty Behavior dostępne we frameworku Rico pozwalają na przekształcanie zwyczajnych węzłów DOM w interaktywne kontrolki. W tym celu wystarczy przekazać do konstruktora odpowiedniego obiektu Behavior identyfikator głównego węzła. W tym przypadku grupa elementów <div> (widoczna po lewej stronie) została przekształcona w interaktywną kontrolkę menu (pokazaną z prawej strony), w której kliknięcia otwierają i zamykają poszczególne panele Są to zdecydowanie najbardziej złożone ze wszystkich frameworków, o jakich wspominamy w tej książce, i niestety, nie będziemy w stanie przedstawić ich wyczerpująco i szczegółowo. Pomimo to chcielibyśmy zamieścić krótki opis cech i możliwości takich rozwiązań. Do zagadnień związanych z takimi frame- workami wrócimy jednak w rozdziale 5. DWR, JSON-RPC oraz SAJAX Zaczniemy od przedstawienia trzech niewielkich frameworków działających po stronie serwera. Opiszemy je wspólnie, gdyż posiadają wiele tych samych cech, choć zostały napisane w różnych językach. Framework może współpracować z wieloma różnymi językami, takimi jak: PHP, Python, Perl oraz Ruby. DWR (ang. Direct Web Remoting) został napisany w języku Java, przy czym wykorzy- stuje to samo podejście, polegające na udostępnianiu metod obiektów, a nie samych funkcji. Trzeci z wymienionych frameworków, JSON-RPC (ang. Java- Script Object Notation-base Remote Procedure Calls), został zaprojektowany dokładnie w taki sam sposób. Także i on umożliwia korzystanie z języków Java- Script, Python, Ruby, Perl oraz Java. Wszystkie te frameworki pozwalają, by metody obiektów działających na serwerze były udostępniane jako żądania Ajaksa. Często zdarza się, że dyspo- nujemy wykonywaną na serwerze funkcją generującą przydatne informacje,
  • 56. 138 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax które następnie muszą być określone na serwerze, gdyż np. bazują na informacjach pobieranych z bazy danych. Opisywane tu frameworki zapewniają wygodny sposób dostępu do tych funkcji lub metod z poziomu przeglądarki i z powodze- niem można je zastosować do udostępnienia obiektów modelu dziedziny uży- wanego na serwerze w kodzie wykonywanym w przeglądarce. Przyjrzyjmy się przykładowi zastosowania frameworka SAJAX, w którym udostępnimy funkcję używaną na serwerze i napisaną w języku PHP. Nasza funkcja jest wyjątkowo prosta, gdyż jej działanie ogranicza się do zwrócenia łańcucha znaków: <?php function sayHello(name){ return("Hello! {$name} Ajax in Action!!!!"); } ?> Aby udostępnić tę funkcję dla kodu JavaScript wykonywanego w przeglądarce, należy zaimportować mechanizm SAJAX i wywołać funkcję sajax_export(): <?php require('Sajax.php'); sajax_init(); sajax_export("sayHello"); ?> Następnie, pisząc kod dynamicznie generowanej strony WWW, używany ko- lejnych funkcji frameworka do wygenerowania kodu pozwalającego na odwo- ływanie się do wyeksportowanej wcześniej funkcji. Ten wygenerowany kod definiuje w przeglądarce lokalną funkcję, której sygnatura odpowiada sygnaturze funkcji działającej na serwerze: <script type='text/javascript'> <? sajax_show_javascript(); ?> ... alert(sayHello("Dawid")); ... </script> Wywołanie funkcji sayHello("Dawid") w przeglądarce spowoduje, że wygene- rowany kod JavaScript prześle na serwer żądanie, które spowoduje wykonanie odpowiedniej funkcji i przekazanie zwróconych przez nią wyników w odpowiedzi HTTP. Następnie, po stronie przeglądarki, odebrane wyniki zostaną przetwo- rzone i udostępnione dla kodu JavaScript. Programista nie musi zajmować się jakimikolwiek aspektami korzystania z technologii Ajax — wszystkie niezbędne operacje są wykonywane w niewidoczny sposób przez biblioteki SAJAX. Prezentowane tu frameworki kojarzą funkcje i obiekty dostępne na serwerze z kodem JavaScript wykonywanym w przeglądarce — na stosunkowo niskim poziomie abstrakcji. Automatyzują one czynności, których wykonanie byłoby żmudne i pracochłonne, jednak stosując je, ryzykujemy, że udostępnimy zbyt wiele możliwości funkcjonalnych zaimplementowanych na serwerze. Zagadnie- nia te opiszemy bardziej szczegółowo w rozdziale 5.
  • 57. 3.5. Biblioteki i frameworki 139 Pozostałe frameworki, które opiszemy w dalszej części rozdziału, prezen- tują znacznie bardziej złożone podejście, gdyż pozwalają na generację całych warstw interfejsu użytkownika na podstawie modelu zadeklarowanego na ser- werze. Choć wewnętrznie frameworki te wykorzystują standardowe możliwo- ści technologii Ajax, jednak z punktu widzenia twórców aplikacji udostępniają swój własny model programowania. W efekcie korzystanie z nich raczej w nie- wielkim stopniu przypomina stosowanie ogólnej technologii Ajax, dlatego też w niniejszej książce będziemy w stanie przedstawić je jedynie bardzo krótko i pobieżnie. Backbase Backbase Presentation Server udostępnia bardzo bogaty zbiór kontrolek koja- rzących wykonawczą część aplikacji ze znacznikami XML umieszczonymi w dokumencie HTML generowanym przez serwer. Zasada działania tego frame- worka przypomina nieco działanie komponentów Behavior pakietu Rico, z tym że Backbase używa własnego zbioru elementów XHTML reprezentujących komponenty interfejsu użytkownika, a nie zwyczajnych znaczników HTML. Część frameworka działająca po stronie serwera jest dostępna w wersji na- pisanej w języku Java oraz w wersji przeznaczonej dla platformy .NET. Backbase jest produktem komercyjnym, jednak dostępna jest także wersja, z której można korzystać bezpłatnie. Echo2 Echo2 firmy NextApp’s jest napisanym w Javie frameworkiem wykonywanym po stronie serwera, który generuje zaawansowane kontrolki na podstawie modelu interfejsu użytkownika zadeklarowanego na serwerze. Po uruchomieniu w prze- glądarce kontrolki interfejsu użytkownika są raczej niezależne i będą obsługi- wały czynności wykonywane przez użytkownika lokalnie, używając odpowied- niego kodu JavaScript, bądź też będą przesyłać żądania na serwer, używając przy tym kolejek, podobnie jak ma to miejsce w przypadku pakietu Rico. Echo2 jest reklamowany jako rozwiązanie wykorzystujące technologię Ajax, które od swych użytkowników nie wymaga żadnej znajomości języka HTML, JavaScript ani CSS, oczywiście zakładając, że nie będziemy chcieli rozszerzać lub zmieniać domyślnego zbioru dostępnych komponentów. W większości przypad- ków tworzenie interfejsu użytkownika odbywa się wyłącznie w języku Java. Echo2 jest frameworkiem typu „open source”, rozpowszechnianym na warunkach licencji Mozilla, która daje możliwość stosowania go w aplikacjach komercyjnych. Ruby on Rails Ruby on Rails jest frameworkiem służącym do tworzenia aplikacji interneto- wych w języku Ruby. Łączy on w sobie rozwiązania pozwalające na kojarzenie obiektów wykonywanych na serwerze z bazą danych, prezentowanie zawarto- ści przy użyciu szablonów i działa w sposób bardzo przypominający wzorzec MVC opisany w podrozdziale 3.4. Ruby on Rails zapewnia bardzo szybki proces
  • 58. 140 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax tworzenia niewielkich i średnich aplikacji internetowych, gdyż pozwala on na automatyczne generowanie często powtarzającego się kodu. Oprócz tego twórcy frameworka starali się zminimalizować ilość czynności konfiguracyjnych koniecz- nych do zapewnienia poprawnego działania aplikacji. W nowszych wersjach Ruby on Rails zapewnia możliwość wykorzystywania technologii Ajax poprzez udostępnienie biblioteki Prototype. Oba te narzędzia współpracują ze sobą w bardzo ścisły i naturalny sposób, gdyż kod JavaScript korzystający z biblioteki Prototype jest generowany automatycznie. Co więcej, zarówno Ruby, jak i Prototype wykorzystują podobny styl programowania. Po- dobnie framework Echo2, także Ruby on Rails pozwala na stosowanie techno- logii Ajax bez konieczności dobrej znajomości technologii podstawowych, takich jak JavaScript. Jednak osoby znające JavaScript mogą rozbudowywać mechani- zmy wykorzystania Ajaksa dostępne w tym frameworku. Informacjami o frameworku Ruby on Rails zakończyliśmy prezentację narzędzi i rozwiązań wspomagających wykorzystywanie technologii Ajax. Jak zaznaczyli- śmy na samym początku, narzędzia te rozwijają się obecnie bardzo dynamicznie, dotyczy to także znacznej części prezentowanych tu frameworków i bibliotek. Warto zauważyć, że większość z prezentowanych tu bibliotek i frameworków charakteryzuje się swoimi własnymi rozwiązaniami i unikalnym stylem pro- gramowania. Pisząc przykłady do niniejszej książki, staraliśmy się przedstawiać charakter poszczególnych technologii i technik oraz unikać zbyt częstego sto- sowania jednego konkretnego frameworka. Niemniej jednak Czytelnik na pew- no zauważy, że wzmianki o niektórych frameworkach i bibliotekach zawarte w tym rozdziale będą się pojawiały także w innych miejscach książki. 3.6. Podsumowanie W tym rozdziale przedstawiliśmy pojęcie refaktoryzacji jako sposobu na po- prawę jakości i elastyczności kodu. Nasze pierwsze spotkanie z refaktoryzacją polegało na zapewnieniu łatwych możliwości wielokrotnego stosowania obiektu XMLHttpRequest w wielu różnych przeglądarkach. Przedstawiliśmy także kilka wzorców projektowych przydatnych do rozwią- zywania problemów, które najczęściej występują podczas tworzenia kodu aplikacji wykorzystujących technologię Ajax. Wzorce projektowe stanowią nieformalny sposób pozyskiwania wiedzy zdobytej przez innych programistów, którzy wcze- śniej rozwiązywali podobne problemy, i pozwalają nam dążyć do określonego celu. Wzorce Façade oraz Adapter pozwalają ukrywać różnice występujące po- między różnymi używanymi implementacjami. W aplikacjach wykorzystujących technologię Ajax oba te wzorce są szczególnie przydatne, gdyż pozwalają nam stworzyć warstwę chroniącą nasz kod przed rozbieżnościami występującymi w poszczególnych przeglądarkach. A właśnie te rozbieżności są podstawowym utrapieniem programistów tworzących aplikacje w języku JavaScript.
  • 59. 3.6. Podsumowanie 141 Kolejnym przedstawionym przez nas wzorcem projektowym był Observer. Jest to elastyczny wzorzec wykorzystywany w systemach sterowanych zdarze- niami. Wrócimy do niego w rozdziale 4., gdzie będziemy zajmowali się warstwami interfejsu użytkownika występującymi w aplikacjach internetowych. Observer w połączeniu z wzorcem projektowym Command, stanowiącym doskonały sposób hermetyzacji czynności wykonywanych przez użytkownika, umożliwia utworzenie solidnego rozwiązania służącego do obsługi poczynań użytkownika na witrynie i zapewniającego możliwość odtwarzania ich skutków. Wzorzec Command można także stosować podczas określania sposobu organizacji interakcji pomiędzy klien- tem a serwerem; tymi zagadnieniami zajmiemy się w rozdziale 5. Wzorzec Singleton stanowi bardzo prosty i skuteczny sposób kontrolowania dostępu do konkretnego zasobu. W aplikacjach wykorzystujących technologię Ajax można go z powodzeniem używać do kontroli dostępu do sieci. Stosowne przykłady zamieściliśmy w rozdziale 5. W końcu przedstawiliśmy także wzorzec projektowy MVC (ang. Model-View- Controller). Jest to wzorzec architektoniczny, który od wielu lat (przynajmniej w krótkiej historii Sieci) jest stosowany do tworzenia aplikacji internetowych. W niniejszym rozdziale opisaliśmy, jak zastosowanie tego wzorca może poprawić elastyczność aplikacji. Przedstawiliśmy też przykład, w którym zastosowaliśmy model obiektowy stworzony w oparciu o mechanizm ORM oraz system obsługi szablonów. Na przykładzie aplikacji obsługującej sklep odzieżowy zademonstrowaliśmy, w jaki sposób można jednocześnie używać refaktoryzacji oraz wzorców projek- towych. Bardzo trudno jest już za pierwszym razem stworzyć idealnie zapro- jektowany kod, jednak z powodzeniem można zastosować refaktoryzację oraz „nieeleganckie”, lecz użyteczne fragmenty kodu (takie jak kod z listingu 3.4), by powoli, etapami, wprowadzać wzorce projektowe do naszej aplikacji. Pod koniec rozdziału przedstawiliśmy grupę bibliotek oraz frameworków. Stosowanie takich narzędzi nie tylko ułatwia nam pracę, lecz także pozwala na wprowadzenie porządku w tworzonym kodzie. Obecnie pojawia się coraz więcej bibliotek i frameworków przeznaczonych do użycia w aplikacjach wykorzystują- cych technologię Ajax, zaczynając od zbiorów kontrolek działających we wszyst- kich przeglądarkach, a kończąc na kompletnych rozwiązaniach wykorzystywa- nych zarówno po stronie klienta, jak i serwera. Pokrótce przedstawiliśmy kilka najpopularniejszych frameworków, a nieco więcej informacji o niektórych spośród nich będzie można znaleźć w dalszej części książki. W dwóch kolejnych rozdziałach spróbujemy zastosować zdobytą wiedzę o refaktoryzacji oraz wzorcach projektowych podczas tworzenia kodu JavaScript działającego po stronie przeglądarki, a następnie do zaimplementowania sys- temu komunikacji pomiędzy klientem a serwerem. Przykłady te pomogą nam w opracowaniu niezbędnej terminologii oraz zespołu praktycznych umiejętności przydatnych podczas tworzenia solidnych, wielofunkcyjnych aplikacji interne- towych.
  • 60. 142 ROZDZIAŁ 3 Porządkowanie aplikacji wykorzystujących technologię Ajax 3.7. Zasoby Martin Fowler (oraz współautorzy: Kent Beck, John Brant, William Opdyke oraz Don Roberts) napisał bardzo cenioną i popularną książkę dotyczącą refaktory- zacji: Refactoring: Improving the Design of Existing Code (Addison-Wesley Pro- fessional 1999). Erich Gamma, Richard Helm, Ralph Johnson oraz Johm Vlissides (określani jako „The Gang of Four”, czyli „Gang Czworga”), napisali książkę Design Patterns (Addison-Wesley Professional, 1995), która wywarła ogromny wpływ na środowi- sko projektantów oprogramowania oraz programistów. Erich Gamma został później architektem zajmującym się tworzeniem zin- tegrowanego środowiska programistycznego Eclipse (patrz dodatek A). Infor- macje o Eclipse oraz wzorcach projektowych można znaleźć w wywiadzie opu- blikowanym na stronie http:/www.artima.com/lejava/articles/gammadp.html. Michael Mahemoff stworzył ostatnio witrynę, http://www.ajaxpatterns.org/, poświęconą katalogowaniu wzorców projektowych stosowanych w aplikacjach wykorzystujących technologię Ajax.