Head First Object-Oriented
                           Analysis and Design.
                           Edycja polska
                           Autor: Brett D. McLaughlin,
                           Gary Pollice, David West
                           T³umaczenie: Piotr Rajca
                           ISBN: 978-83-246-0965-9
                           Tytu³ orygina³u: Head First Object-Oriented
                           Analysis and Design
                           Format: 200x230, stron: 616
                           Przyk³ady na ftp: 165 kB


                                              Poznaj techniki analizy i projektowania obiektowego
                               • Naucz siê zbieraæ wymagania od u¿ytkowników systemu
                               • Zarz¹dzaj zmianami w specyfikacji
                               • PrzeprowadŸ analizê i wykonaj projekt
                           Systemy informatyczne staj¹ siê coraz bardziej rozbudowane. Programowanie
                           obiektowe znacznie u³atwia ich tworzenie i póŸniejsze modyfikacje, aby jednak system
                           by³ sprawny i funkcjonalny, musi zostaæ zaprojektowany w oparciu o prawid³owo
                           zebrane wymagania. Tu równie¿ z pomoc¹ przychodzi metodologia obiektowa – wzorce
                           projektowe, jêzyk UML i odpowiednie narzêdzia niezwykle u³atwiaj¹ przygotowanie
                           dobrego projektu.
                           Jeœli rozbudowane przyk³ady, skomplikowane diagramy i niezrozumia³e wywody
                           teoretyczne wywo³uj¹ w Tobie niechêæ, koniecznie siêgnij po tê ksi¹¿kê! Dziêki niej
                           poznasz metody analizy i projektowania obiektowego w nietypowy i ciekawy sposób,
                           wykorzystuj¹cy najnowsze teorie skutecznego przekazywania wiedzy. Przeczytasz
                           o tym, w jaki sposób warto gromadziæ wymagania i oczekiwania u¿ytkowników wobec
                           projektowanego systemu, jak uwzglêdniaæ w projekcie postulowane zmiany
                           i przeprowadzaæ proces analizy obiektowej. Nauczysz siê stosowaæ notacjê UML
                           do przedstawiania struktury systemu i przetwarzanych przez niego danych. Dowiesz siê
                           tak¿e, jak testowaæ projektowany system.
                               • Zasady i cele projektowania obiektowego
                               • Gromadzenie wymagañ
                               • Przypadki u¿ycia
Wydawnictwo Helion             • Analiza obiektowa
ul. Koœciuszki 1c
                               • Diagramy UML przedstawiaj¹ce strukturê systemu
44-100 Gliwice
                               • Korzystanie ze wzorców projektowych
tel. 032 230 98 63
e-mail: helion@helion.pl       • Projektowanie architektury systemu
                               • Testowanie
Spis treści


Spis treści (skrócony)
                   1    Dobrze zaprojektowane aplikacje są super. Tu zaczyna się wspaniałe                    31
                        oprogramowanie
                   2    Gromadzenie wymagań. Daj im to, czego chcą                                            83
                   3    Wymagania ulegają zmianom. Kocham cię, jesteś doskonały…                             137
                        A teraz — zmień się
                   4    Analiza. Zaczynamy używać naszych aplikacji w rzeczywistym świecie                   169
                   5    Część 1. Dobry projekt = elastyczne oprogramowanie. Nic nie pozostaje                221
                        wiecznie takie samo
                        Przerywnik. Obiektowa katastrofa                                                     245
                        Część 2. Dobry projekt = elastyczne oprogramowanie. Zabierz swoje                    257
                        oprogramowanie na 30-minutowy trening
                   6    Rozwiązywanie naprawdę dużych problemów. „Nazywam się Art Vandelay...                301
                        jestem Architektem”
                   7    Architektura. Porządkowanie chaosu                                                   343
                   8    Zasady projektowania. Oryginalność jest przereklamowana                              395
                   9    Iteracja i testowanie. Oprogramowanie jest wciąż przeznaczone dla klienta            441
                   10 Proces projektowania i analizy obiektowej. Scalając to wszystko w jedno                499
                        Dodatek A Pozostałości                                                               571
                        Dodatek B Witamy w Obiektowie                                                        589
                        Skorowidz                                                                            603




Spis treści (szczegółowy)
    Wprowadzenie

    Twój mózg koncentruje się na analizie i projektowaniu obiektowym. Podczas gdy Ty
    starasz się czegoś nauczyć, Twój mózg robi Ci przysługę i dba o to, abyś przez przypadek nie zapamiętał
    zdobywanych informacji. Myśli sobie: „Lepiej zostawić trochę miejsca na bardziej istotne sprawy, na przykład
    jakich zwierząt unikać albo czy jazda na snowboardzie nago jest dobrym pomysłem”. W jaki zatem sposób
    możesz oszukać swój mózg i przekonać go, że Twoje życie zależy od znajomości analizy i projektowania
    obiektowego?
                        Dla kogo jest ta książka?                                                             20
                        Wiemy, co sobie myślisz                                                               21
                        Metapoznanie: myślenie o myśleniu                                                     23
                        Zmuś swój mózg do posłuszeństwa                                                       25
                        Ważne uwagi                                                                           26
                        Recenzenci techniczni                                                                 28
                        Podziękowania                                                                         29
Spis treści

                                      Dobrze zaprojektowane aplikacje są super




                      1
                                      Tu zaczyna się wspaniałe oprogramowanie
                                      A zatem, w jaki sposób w praktyce pisze się wspaniałe oprogramowanie?
                                      Zawsze bardzo trudno jest określić, od czego należy zacząć. Czy aplikacja faktycznie robi to,
                                      co powinna robić i czego od niej oczekujemy? A co z takimi problemami jak powtarzający się kod —
                                      przecież to nie może być dobre ani właściwe rozwiązanie, prawda? Zazwyczaj trudno jest określić, które
                                      z wielu problemów należy rozwikłać w pierwszej kolejności, a jednocześnie mieć pewność, że podczas
                                      wprowadzania poprawek nie popsujemy innych fragmentów aplikacji. Bez obaw. Po zakończeniu lektury
                                      tego rozdziału będziesz już dokładnie wiedział, jak pisać doskonałe oprogramowanie, i pewnie
                                      podążał w kierunku trwałego poprawienia sposobu tworzenia programów. I w końcu zrozumiesz,
                                      dlaczego OOAD to czteroliterowy skrót (pochodzący od angielskich słów: Object-Oriented Analysis
                                      and Design, analiza i projektowanie obiektowe), który Twoja matka chciałaby, byś poznał.




                                                          Rock-and-roll jest wieczny!                                               32
                                                          Nowa elegancka aplikacja Ryśka…                                           33
                                                          Co przede wszystkim zmieniłbyś w aplikacji Ryśka?                         38
               Niby skąd mam wiedzieć, od                 Doskonałe oprogramowanie…
  czego należy zacząć? Mam wrażenie, że ilekroć           ma więcej niż jedną z wymienionych już cech                               40
zaczynam pracę nad nowym projektem, każdy ma inne
zdanie odnośnie tego, co należy zrobić w pierwszej        Wspaniałe oprogramowanie w trzech prostych krokach                        43
   kolejności. Czasami zrobię coś dobrze, lecz
  czasami kończy się na tym, że muszę przerobić           W pierwszej kolejności skoncentruj się na funkcjonalności                 48
  całą aplikację od początku, bo zacząłem w złym
  miejscu. A ja chcę jedynie pisać świetne                Test                                                                      53
    oprogramowanie! A zatem, od czego                     Szukamy problemów                                                         55
         powinienem zacząć pisanie aplikacji
                      dla Ryśka?                          Analiza metody search()                                                   56
                                                          Stosuj proste zasady projektowania obiektowego                            61
                                                          Projekt po raz pierwszy, projekt po raz drugi                             66
                                                          Jak łatwo można wprowadzać zmiany w Twojej aplikacji?                     68
                                                          Poddawaj hermetyzacji to, co się zmienia                                  71
                                                          Delegowanie                                                               73
                                                          Nareszcie doskonałe oprogramowanie (jak na razie)                         76
                                                          OOAD ma na celu tworzenie wspaniałego oprogramowania,
                                                          a nie dodanie Ci papierkowej roboty                                       79
                                                          Kluczowe zagadnienia                                                      80
Spis treści

    Gromadzenie wymagań




2
    Daj im to, czego chcą
    Każdy lubi zadowolonych klientów. Już wiesz, że pierwszy krok w pisaniu doskonałego
    oprogramowania polega na upewnieniu się, czego chce klient. Ale jak się dowiedzieć, czego klient
    oczekuje? Co więcej — skąd mieć pewność, że klient w ogóle wie, czego tak naprawdę chce? Właśnie
    wówczas na arenę wkraczają „dobre wymagania”. W tym rozdziale dowiesz się, w jaki sposób
    zadowolić klientów, upewniając się, że dostarczysz im właśnie to, czego chcą. Kiedy skończysz lekturę,
    wszystkie swoje projekty będziesz mógł opatrzyć etykietą „Satysfakcja gwarantowana” i posuniesz się
    o kolejny krok na drodze do tworzenia doskonałego oprogramowania… i to za każdym razem.


                        Nadszedł czas na kolejny pokaz Twych
                        programistycznych umiejętności                                            84
                        Test programu                                                             87
                        Nieprawidłowe zastosowanie (coś w tym stylu)                              89
                        Czym jest wymaganie?                                                      90
                        Tworzenie listy wymagań                                                   92
                        Zaplanuj, co może się popsuć w systemie                                   96
                        Problemy w działaniu systemu są obsługiwane
                        przez ścieżki alternatywne                                                98
                        (Ponowne) przedstawienie przypadku użycia                                100
                        Jeden przypadek użycia, trzy części                                      102
                        Porównaj wymagania z przypadkami użycia                                  106
                        Twój system musi działać w praktyce                                      113
                        Poznajemy Szczęśliwą Ścieżkę                                             120
                        Przybornik projektanta                                                   134




                                                                                 Drzwiczki
                                                                                 dla psa oraz
                                                                                 pilot stanowią
                                                                                 elementy
                                                                                 systemu, bądź
                                                                                 też znajdują się
                                                                                 wewnątrz niego.
                                                   System
Spis treści

                                      Wymagania ulegają zmianom




                    3
                                      Kocham cię, jesteś doskonały…
                                      A teraz — zmień się
                                      Sądzisz, że dowiedziałeś się już wszystkiego o tym, czego chciał klient? Nie tak
                                      szybko… A zatem przeprowadziłeś rozmowy z klientem, zgromadziłeś wymagania, napisałeś przypadki
                                      użycia, napisałeś i dostarczyłeś klientowi odlotową aplikację. W końcu nadszedł czas na miłego,
                                      relaksującego drinka, nieprawdaż? Pewnie… aż do momentu gdy klient uzna, że tak naprawdę chce
                                      czegoś innego niż to, co Ci powiedział. Bardzo podoba mu się to, co zrobiłeś — poważnie! — jednak
                                      obecnie nie jest już w pełni usatysfakcjonowany. W rzeczywistym świecie wymagania zawsze się
                                      zmieniają; to Ty musisz sobie z tymi zmianami poradzić i pomimo nich zadbać o zadowolenie klienta.


                                                            Jesteś bohaterem!                                                   138
                                                            Jesteś patałachem!                                                  139
                                                            Jedyny pewnik analizy i projektowania obiektowego                   141
                                                            Ścieżka oryginalna? Ścieżka alternatywna? Kto to wie?               146
                                                            Przypadki użycia muszą być zrozumiałe przede wszystkim dla Ciebie   148
                                                            Od startu do mety: jeden scenariusz                                 150
                                                            Wyznanie Ścieżki Alternatywnej                                      152
                                                            Uzupełnienie listy wymagań                                          156
                                                            Powielanie kodu jest bardzo złym pomysłem                           164
                                                            Ostateczny test drzwiczek                                           166
                                                            Napisz swoją własną zasadę projektową!                              167
                                                            Przybornik projektanta                                              168




     public void pressButton() {
       System.out.println(”Naciśnięto przycisk na pilocie...”);
       if (door.isOpen()) {
         door.close();
       } else {
         door.open();

           final Timer timer = new Timer();
           timer.schedule(new TimerTask() {
             public void run() {
               door.close();
               timer.cancel();
             }
           }, 5000);                                  class
                                                      Remote {
       }                                                press-
                                                      Button()
                                                      }
Spis treści

                    Analiza




4
                    Zaczynamy używać naszych aplikacji
                    w rzeczywistym świecie
                    Czas zdać ostatnie egzaminy i zacząć stosować nasze aplikacje
                    w rzeczywistym świecie. Twoje aplikacje muszą robić nieco więcej, niż jedynie działać
                    prawidłowo na komputerze, którego używasz do ich tworzenia — komputerze o dużej mocy
                    i doskonale skonfigurowanym; Twoje aplikacje muszą działać w takich warunkach, w jakich
                    rzeczywiści klienci będą ich używali. W tym rozdziale zastanowimy się, jak zyskać pewność,
                    że nasze aplikacje będą działać w rzeczywistym kontekście. Dowiesz się w nim, w jaki sposób
                    analiza tekstowa może przekształcić stworzony wcześniej przypadek użycia w klasy i metody, które
                    na pewno będą działać zgodnie z oczekiwaniami klienta. A kiedy skończysz lekturę tego rozdziału,
                    także i Ty będziesz mógł powiedzieć: „Dokonałem tego! Moje oprogramowanie jest gotowe do
                    zastosowania w rzeczywistym świecie!”.


                                    Jeden pies, dwa psy, trzy psy, cztery…                                  170
  Kiedy już określiłam,             Twoje oprogramowanie ma kontekst                                        171
jakich klas i operacji będę
 potrzebować, odpowiednio           Określ przyczynę problemu                                               172
zaktualizowałam diagram klas.
                                    Zaplanuj rozwiązanie                                                    173
                                    Opowieść o dwóch programistach                                          180
                                    Delegowanie w kodzie Szymka — analiza szczegółowa                       184
                                    Potęga aplikacji, których elementy są ze sobą luźno powiązane           186
                                    Zwracaj uwagę na rzeczowniki występujące w przypadku użycia             191
                                    Od dobrej analizy do dobrych klas…                                      204
                                    Diagramy klas bez tajemnic                                              206
                                    Diagramy klas to nie wszystko                                           211
                                    Kluczowe zagadnienia                                                    215


                                                                                    rzeczy
                                                               W tym kontekście rót
                                                               przy bierają zły ob
                                                                                 j.
                                                               znacznie częście




                                                                     W rzeczywistym świecie
                                                                     spotykamy inne psy, koty,
                                                                     gryzonie oraz całą masę innych
                                      class                          problemów; a wszystkie te
                                      DogDoor
                                      {
                                                                     czynniki mają tylko jeden cel
                                      }
                                        open()                       — doprowadzić do awarii naszego
                                                                     oprogramowania.
                                  DogDoor.java

                           Rzeczywisty Świat
Spis treści

                           Dobry projekt = elastyczne oprogramowanie




                5
                           Nic nie pozostaje wiecznie takie samo
                           Zmiany są nieuniknione. Niezależnie od tego, jak bardzo podoba Ci się Twoje
                           oprogramowanie w jego obecnej postaci, to najprawdopodobniej jutro zostanie ono
                           zmodyfikowane. A im bardziej utrudnisz wprowadzanie modyfikacji w aplikacji, tym trudniej będzie
                           Ci w przyszłości reagować na zmiany potrzeb klienta. W tym rozdziale mamy zamiar odwiedzić
              (część 1.)   naszego starego znajomego oraz spróbować poprawić projekt istniejącego oprogramowania.
                           Na tym przykładzie przekonamy się, jak niewielkie zmiany mogą doprowadzić do poważnych
                           problemów. Prawdę mówiąc, jak się okaże, odkryte przez nas kłopoty będą tak poważne, że ich
                           rozwiązanie będzie wymagało rozdziału składającego się aż z DWÓCH części!


                                           Firma Gitary/Instrumenty Strunowe Ryśka rozwija się                    222
                                           Klasy abstrakcyjne                                                     225
                                           Diagramy klas bez tajemnic (ponownie)                                  230
                                           Ściągawka z UML-a                                                      231
                                           Porady dotyczące problemów projektowych                                237
                                           Trzy kroki tworzenia wspaniałego oprogramowania (po raz kolejny)       239




                5
                                    Obiektowa K atastrofa!

        (przerywnik)




10
Spis treści

             Dobry projekt = elastyczne oprogramowanie




 5
             Zabierz swoje oprogramowanie
             na 30-minutowy trening
             Czy kiedykolwiek marzyłeś o tym, by być nieco bardziej elastycznym
             w działaniu? Jeśli kiedykolwiek wpadłeś w kłopoty podczas prób wprowadzania zmian
             w aplikacji, to zazwyczaj oznacza to, że Twoje oprogramowanie powinno być nieco bardziej
(część 2.)   elastyczne i odporne. Aby pomóc swojej aplikacji, będziesz musiał przeprowadzić odpowiednią
             analizę, zastanowić się nad niezbędnymi zmianami w projekcie i dowiedzieć się, w jaki sposób
             rozluźnić zależności pomiędzy jej elementami. I w końcu, w wielkim finale, przekonasz się,
             że większa spójność może pomóc w rozwiązaniu problemu powiązań. Brzmi interesująco?
             A zatem przewróć kartkę — przystępujemy do poprawiania nieelastycznej aplikacji.


                             Wróćmy do aplikacji wyszukiwawczej Ryśka                               258
                             Dokładniejsza analiza metody search()                                  261
                             Korzyści, jakie dała nam analiza                                       262
                             Dokładniejsza analiza klas instrumentów                                265
                             Śmierć projektu (decyzja)                                              270
                             Zmieńmy złe decyzje projektowe na dobre                                271
                             Zastosowanie „podwójnej hermetyzacji” w aplikacji Ryśka                273
                             Nigdy nie obawiaj się wprowadzania zmian                               279
                             Elastyczna aplikacja Ryśka                                             282
                             Testowanie dobrze zaprojektowanej aplikacji Ryśka                      285
                             Jak łatwo można zmodyfikować aplikację Ryśka?                          289
                             Wielki konkurs łatwości modyfikacji                                    290
                             Spójna klasa realizuje jedną operację naprawdę dobrze                  293
                             Przegląd zmian wprowadzanych w oprogramowaniu dla Ryśka                296
                             Doskonałe oprogramowanie to zazwyczaj takie,
                             które jest „wystarczająco dobre”                                       298
                             Przybornik projektanta                                                 300




                                                                                                            11
Spis treści

                  Rozwiązywanie naprawdę dużych problemów




              6
                  „Nazywam się Art Vandelay… jestem Architektem”
                  Nadszedł czas, by zbudować coś NAPRAWDĘ DUŻEGO. Czy jesteś gotów?
                  Zdobyłeś już wiele narzędzi do swojego projektanckiego przybornika, jednak w jaki sposób z nich
                  skorzystasz, kiedy będziesz musiał napisać coś naprawdę dużego? Cóż, może jeszcze nie zdajesz sobie
                  z tego sprawy, ale dysponujesz wszystkimi narzędziami, jakie mogą być potrzebne do skutecznego
                  rozwiązywania poważnych problemów. Niebawem poznasz kilka nowych narzędzi, takich jak analiza
                  dziedziny oraz diagramy przypadków użycia, jednak nawet one bazują na wiadomościach, które
                  już zdobyłeś, takich jak uważne słuchanie klienta oraz dokładne zrozumienie, co trzeba napisać, zanim
                  jeszcze przystąpimy do faktycznego pisania kodu. Przygotuj się… nadszedł czas, byś sprawdził, jak sobie
                  radzisz w roli architekta.


                                      Rozwiązywanie dużych problemów                                            302
                                      Wszystko zależy od sposobu spojrzenia na duży problem                     303
                                      Wymagania i przypadki użycia to dobry punkt wyjściowy…                    308
                                      Potrzebujemy znacznie więcej informacji                                   309
                                      Określanie możliwości                                                     312
                                      Możliwość czy wymaganie                                                   314
                                      Przypadki użycia nie zawsze pomagają ujrzeć
                                      ogólny obraz tworzonego oprogramowania                                    316
                                      Diagramy przypadków użycia                                                318
                                      Mały aktor                                                                323
                                      Aktorzy to także ludzie (no dobrze… nie zawsze)                           324
                                      A zatem zabawmy się w analizę dziedziny!                                  329
                                      Dziel i rządź                                                             331
                                      Nie zapominaj, kim tak naprawdę jest klient                               335
                                      Czym jest wzorzec projektowy?                                             337
                                      Potęga OOAD (i trochę zdrowego rozsądku)                                 340
                                      Przybornik projektanta                                                    342




12
Spis treści

                               Architektura




                           7
                               Porządkowanie chaosu
                               Gdzieś musisz zacząć, jednak uważaj, żeby wybrać właściwe „gdzieś ”!
                               Już wiesz, jak podzielić swoją aplikację na wiele małych problemów, jednak oznacza to tylko
                               i wyłącznie tyle, iż obecnie nie masz jednego dużego, lecz WIELE małych problemów. W tym
                               rozdziale spróbujemy pomóc Ci w określeniu, gdzie należy zacząć, i upewnimy się, że nie będziesz
                               marnował czasu na zajmowanie się nie tym, co trzeba. Nadeszła pora, by pozbierać te wszystkie
                               drobne kawałki na Twoim biurku i zastanowić się, jak można je przekształcić w uporządkowaną
                               i dobrze zaprojektowaną aplikację. W tym czasie poznasz niesłychanie ważne „trzy P dotyczące
                               architektury” i dowiesz się, że Risk to znacznie więcej niż jedynie słynna gra wojenna z lat 80.


                                               Czy czujesz się nieco przytłoczony?                                     344
                                               Potrzebujemy architektury                                               346
                                               Zacznijmy od funkcjonalności                                            349
                                               Co ma znaczenie dla architektury                                        351
                                               Trzy „P” dotyczące architektury                                         352
class
Unit {
    Unit()
                 class
                 Tile
                 { ge-
                                               Wszystko sprowadza się do problemu ryzyka                               358
  }        class tUnit()
}          Board }
           { ge-                               Scenariusze pomagają zredukować ryzyko                                  361
           tUnit()
           }

                                               Koncentruj się na jednej możliwości w danej chwili                      369
                                               Architektura jest strukturą Twojego projektu                            371
                                               Podobieństwa po raz kolejny                                             375
                                               Analiza podobieństw: ścieżka do elastycznego oprogramowania             381
                                               Co to znaczy? Zapytaj klienta                                           386
                                               Zmniejszanie ryzyka pomaga pisać wspaniałe oprogramowanie               391
                                               Kluczowe zagadnienia                                                    392




                                                                                                                             13
Spis treści

                         Zasady projektowania




                    8
                         Oryginalność jest przereklamowana
                         Powielanie jest najlepszą formą unikania głupoty. Nic chyba nie daje większej satysfakcji
                         niż opracowanie całkowicie nowego i oryginalnego rozwiązania problemu, który męczy nas od wielu
                         dni… aż do czasu gdy okaże się, że ktoś rozwikłał ten sam problem już wcześniej, a co gorsza — zrobił
                         to znacznie lepiej niż my. W tym rozdziale przyjrzymy się kilku zasadom projektowania, które udało
                         się sformułować podczas tych wszystkich lat stosowania komputerów, i dowiemy się, w jaki sposób
                         mogą one sprawić, że staniesz się lepszym programistą. Porzuć ambitne myśli o „zrobieniu tego lepiej”
                         — lektura tego rozdziału udowodni Ci, jak pisać programy sprytniej i szybciej.


                                             Zasada projektowania — w skrócie                                        396
                                             Zasada otwarte-zamknięte                                                397
                                             OCP, krok po kroku                                                      399
                                             Zasada nie powtarzaj się                                                402
                                             Zasada DRY dotyczy obsługi jednego wymagania
                                             w jednym miejscu                                                        404
Zasada otwarte-
-zamknięte                                   Zasada jednej odpowiedzialności                                         410
                                             Wykrywanie wielu odpowiedzialności                                      412
                                             Przechodzenie od wielu do jednej odpowiedzialności                      415
                                             Zasada podstawienia Liskov                                              420
                                             Studium błędnego sposobu korzystania z dziedziczenia                    421
                                             LSP ujawnia ukryte problemy związane ze strukturą dziedziczenia         422
                                             Musi istnieć możliwość zastąpienia typu bazowego
                                             jego typem pochodnym                                                    423
                                             Naruszenia LSP sprawiają, że powstający kod staje się mylący            424
                    Zasada nie
                    powtarzaj się            Deleguj funkcjonalność do innej klasy                                   426
                                             Użyj kompozycji, by zebrać niezbędne zachowania
                                             z kilku innych klas                                                     428
                                             Agregacja — kompozycja bez nagłego zakończenia                          432
                                             Agregacja a kompozycja                                                  433
                                             Dziedziczenie jest jedynie jedną z możliwości                           434
                                             Kluczowe zagadnienia                                                    437
Zasada jednejości                            Przybornik projektanta                                                  438
odpowiedzialn




                                    Zasada podstawienia
                                    Liskov




1
Spis treści

    Powtarzanie i testowanie




9
    Oprogramowanie jest wciąż przeznaczone
    dla klienta
    Czas pokazać klientowi, jak bardzo Ci na nim zależy. Nękają Cię szefowie? Klienci
    są zmartwieni? Udziałowcy wciąż zadają pytanie: „Czy wszystko będzie zrobione na czas?”. Żadna
    ilość nawet wspaniale zaprojektowanego kodu nie zadowoli Twoich klientów; musisz pokazać im coś
    działającego. Teraz, kiedy dysponujesz już solidnym przybornikiem z narzędziami do programowania
    obiektowego, nadszedł czas, byś udowodnił swoim klientom, że pisane przez Ciebie oprogramowanie
    naprawdę działa. W tym rozdziale poznasz dwa sposoby pracy nad implementacją możliwości
    funkcjonalnych tworzonego oprogramowania — dzięki nim Twoi klienci poczują błogie ciepło, które
    sprawi, że powiedzą o Tobie: „O tak, nie ma co do tego wątpliwości, jest właściwą osobą do
    napisania naszej aplikacji!”.


                       Twój przybornik narzędziowy powoli się wypełnia                      442
                       Wspaniałe oprogramowanie tworzy się iteracyjnie                      444
                       Schodzenie w głąb: dwie proste opcje                                 445
                       Programowanie w oparciu o możliwości                                 446
                       Programowanie w oparciu o przypadki użycia                           447
                       Dwa podejścia do tworzenia oprogramowania                            448
                       Analiza możliwości                                                   452
                       Pisanie scenariuszy testowych                                        455
                       Programowanie w oparciu o testy                                      458
                       Podobieństwa po raz wtóry                                            460
                       Kładziemy nacisk na podobieństwa                                     464
                       Hermetyzujemy wszystko                                               466
                       Dopasuj testy do projektu                                            470
                       Testy bez tajemnic…                                                  472
                       Udowodnij klientowi, że wszystko idzie dobrze                        478
                       Jak dotąd używaliśmy programowania w oparciu o kontrakt              480
                       Tak naprawdę programowanie w oparciu o kontrakt
                       dotyczy zaufania                                                     481
                       Programowanie defensywne                                             482
                       Podziel swoją aplikację na mniejsze fragmenty funkcjonalności        491
                       Kluczowe zagadnienia                                                 493
                       Przybornik projektanta                                               496




                                                                                                  1
Spis treści

                                      Proces projektowania i analizy obiektowej




         10
                                      Scalając to wszystko w jedno
                                      Czy dotarliśmy już do celu? Poświęciliśmy sporo czasu i wysiłku, by poznać wiele różnych
                                      sposobów pozwalających poprawić jakość tworzonego oprogramowania; teraz jednak nadeszła pora,
                                      by połączyć i podsumować wszystkie zdobyte informacje. Na to właśnie czekałeś: mamy zamiar
                                      zebrać wszystko, czego się nauczyłeś, i pokazać Ci, że wszystkie te informacje stanowią części jednego
                                      procesu, którego możesz wielokrotnie używać, by tworzyć wspaniałe oprogramowanie.


                                                                Tworzenie oprogramowania w stylu obiektowym                                               500
                                                                Trans-Obiektów                                                                            504
                                                                Mapa metra w Obiektowie                                                                   506
                                                                Lista możliwości                                                                          509
                                                                Przypadki użycia odpowiadają zastosowaniu,
                                                                możliwości odpowiadają funkcjonalności                                                    515
                                                                A teraz zacznij powtarzać te same czynności                                               519
                                                                Dokładniejsza analiza sposobu reprezentacji sieci metra                                   521
                                                                Używać klasy Line czy też nie używać... oto jest pytanie                                  530
                                                                Najważniejsze sprawy związane z klasą Subway                                              536
                                                                Ochrona własnych klas                                                                     539
                                                                Czas na przerwę                                                                           547
                                                                Wróćmy znowu do etapu określania wymagań                                                  549
                                                                Koncentruj się na kodzie, a potem na klientach                                            551
                                                                Powtarzanie sprawia, że problemy stają się łatwiejsze                                     555
                                                                Jak wygląda trasa?                                                                        560
                                                                Samemu sprawdź Przewodnik Komunikacyjny po Obiektowie                                     564
                                                                Ktoś chętny na trzeci cykl prac?                                                          567
                                                                Podróż jeszcze nie dobiegła końca…                                                        569




                                                                                                                                   owego
                                                    Analiza Ścieżka alternatywna               Hermetyzacja                 obiekt
                                                                           k inicjują
                                                                                      cy                             owania
              Rozmowa z klientem                                        ni                          iwości y projekt
                                           Architektura Zewnętrzny czyn                  zowych możl Zasad
                                                                               Lista kluc Wzorzec projektowy Zasady projektowe
                                                                  Scenariusz                                        Wzorzec projektowy
                                                                                                                                                     ci
                                                                      zenie                                                                   żliwoś
                                               Podobieństwa     Powtór Rozmowa z klientem                                              u o mo
         Lista kluczo          Zewnętrzny                 Ścieżka alternatywna                 Powtórze
                                                                                                        nie    Hermetyzacja w oparci
                                                                                                                          nie
                                                                                                                        a
                     wych możliwoś        czynnik Delegowanie                Analiza tekstowa                    gramow
                                   ci                             Lista wymaga
                                                                               ń                    Spójność Pro
                                                                                        zenie                    Pr
                                                      Różnice                    Powtór       Zasady projektowe ogramowanie w oparciu
                                                                                                                                            o testy
                                                                            Architektura                                         zenie
                                                                                                                          Powtór     Scenariusz




1
Spis treści

    Dodatek A Pozostałości




A
    Dziesięć najważniejszych tematów
    (których nie poruszyliśmy)
    Możesz nam wierzyć albo i nie, ale to jeszcze nie jest koniec. Owszem, wyobraź
    sobie, że nawet po przeczytaniu tych 600 stron wciąż możesz jeszcze znaleźć tematy, o których
    nawet nie wspomnieliśmy. Choć dziesięć zagadnień, jakie mamy zamiar przedstawić w tym dodatku,
    nie zasługuje na wiele więcej niż krótką wzmiankę, to jednak nie chcieliśmy, byś opuszczał Obiektów
    bez informacji na ich temat. Teraz będziesz miał nieco więcej tematów do rozmów podczas firmowej
    imprezy z okazji wygrania telewizyjnego quizu Obiektowa Katastrofa… poza tym któż, od czasu do
    czasu, nie kocha stymulujących rozmów o analizie i projektowaniu?

    Kiedy już skończymy, pozostanie jeszcze… następny dodatek… no i oczywiście indeks, i może kilka
    reklam… ale później dotrzesz wreszcie do końca książki. Obiecujemy.


                        Nr 1. JEST i MA                                                          572
                        Nr 2. Sposoby zapisu przypadków użycia                                   574
                        Nr 3. Antywzorce                                                         577
                        Nr 4. Karty CRC                                                          578
                        Nr 5. Metryki                                                            580
                        Nr 6. Diagramy sekwencji                                                 581
                        Nr 7. Diagramy stanu                                                     582
                        Nr 8. Testowania jednostkowe                                             584
                        Nr 9. Standardy kodowania i czytelny kod                                 586
                        Nr 10. Refaktoryzacja                                                    588




                                                                                                          1
Spis treści

                  Dodatek B Witamy w Obiektowie




              B
                  Stosowanie języka obiektowego
                  Przygotuj się na zagraniczną wycieczkę. Czas odwiedzić Obiektów — miejsce, gdzie
                  obiekty robią to, co powinny, aplikacje są dobrze hermetyzowane (już wkrótce dowiesz się, co to
                  znaczy), a projekty oprogramowania pozwalają na ich wielokrotne stosowanie i rozbudowę. Musisz
                  jeszcze poznać kilka dodatkowych zagadnień i poszerzyć swoje umiejętności językowe. Nie przejmuj
                  się jednak, nie zajmie Ci to wiele czasu i zanim się obejrzysz, już będziesz rozmawiał w języku
                  obiektowym, jakbyś mieszkał w Obiektowie od wielu lat.


                                     UML i diagramy klas                                                  591
                                     Dziedziczenie                                                        593
                                     Polimorfizm                                                          595
                                     Hermetyzacja                                                         596
                                     Kluczowe zagadnienia                                                 600




              S
                  Skorowidz                                                                        603




1
1. Dobrze zaprojektowane aplikacje są super

               Tu się zaczyna wspaniałe
                              oprogramowanie
                     Naprawdę trudno mi
             przyjść po tym wszystkim do siebie,
          ale od kiedy zacząłem stosować analizę
           i projektowanie obiektowe, stałem się
           zupełnie innym człowiekiem… mówię ci,
                       zupełnie innym!




                 A zatem, w jaki sposób w praktyce pisze się wspaniałe oprogramowanie?
                 Zawsze bardzo trudno jest określić, od czego należy zacząć. Czy aplikacja faktycznie
                 robi to, co powinna robić i czego od niej oczekujemy? A co z takimi problemami jak
                 powtarzający się kod — przecież to nie może być dobre ani właściwe rozwiązanie, prawda?
                 Zazwyczaj trudno jest określić, które z wielu problemów należy rozwikłać w pierwszej
                 kolejności, a jednocześnie mieć pewność, że podczas wprowadzania poprawek nie popsujemy
                 innych fragmentów aplikacji. Bez obaw. Po zakończeniu lektury tego rozdziału będziesz już
                 dokładnie wiedział, jak pisać doskonałe oprogramowanie i pewnie podążał w kierunku
                 trwałego poprawienia sposobu tworzenia programów. I w końcu zrozumiesz, dlaczego
                 OOAD to czteroliterowy skrót (pochodzący od angielskich słów: Object-Oriented Analysis
                 and Design, analiza i projektowanie obiektowe), który Twoja matka chciałaby, byś poznał.




                                                                               to jest nowy rozdział        31
Dźwięki drewna i stali


Rock-and-roll jest wieczny!
Nie ma nic lepszego niż dźwięki doskonałej gitary w rękach świetnego
muzyka, a firma Gitary Ryśka specjalizuje się w wyszukiwaniu doskonałych
instrumentów dla wymagających i doświadczonych klientów.



                                                                          Nie uwierzyłbyś,
                                                                  jaki mamy wybór gitar.
                                                                                          Zapraszam,
                                                                powiedz mi, jaka gitara
                                                                                        Cię interesuje,
                                                               a zapewniam, że znajd
                                                                                      ziemy instrument
                                                                       idealnie odpowiadając
                                                                                             y Twoim
                                                                        potrzebom i oczekiwan
                                                                                               iom!




                                                                Poznaj Ryśka, pasjonata
                                                                i miłośnika gitar, a jednocześnie
                                                                właściciela ekskluzywnego
                                                                sklepu z gitarami.




Właśnie kilka miesięcy temu Rysiek porzucił stosowany wcześniej
„papierowy” system informacji o gitarach i podjął decyzję
o przechowywaniu danych o stanie magazynu i transakcjach w systemie
komputerowym. Wynajął w tym celu popularną firmę programistyczną
SzybkoIKiepsko Sp. z o.o., która napisała mu odpowiednią aplikację.
Rysiek poprosił ich nawet o napisanie nowego narzędzia — programu,
który wspomagałby dobieranie gitar dla klientów.




32     Rozdział 1.
Dobrze zaprojektowane aplikacje są super


   Nowa elegancka aplikacja Ryśka…
   Oto aplikacja, którą firma programistyczna napisała dla Ryśka… Jej zespół stworzył system, który całkowicie zastępuje
   papierowe notatki spisywane przez Ryśka wcześniej i który pomaga mu w odnajdywaniu gitar doskonale spełniających
   oczekiwania klientów. Oto diagram UML klas, który programiści firmy przedstawili Ryśkowi, by pokazać, co dla niego zrobili:


      Każda gitara znajdująca się                           Oto cały magazyn sklepu
      w magazynie sklepu Ryśka jest                         Ryśka, który jednocześnie
                                                                                                  Magazyn zawiera list
                                                                                                                        ę
      reprezentowana przez obiekt                           zapewnia mu możliwość
                                                                                                  wszystkich gitar, jakimi
                                                                                                                           Rysiek
      tej klasy.                                            poszukiwania gitar.                                 ponuje.
                                                                                                  aktualnie dys



                            Guitar                                               Inventory
                   serialNumber: String                       guitars: List
                   price: double
                                                              addGuitar(String, double, String, String, String,
                   builder: String
Oto zmienne                                                             String, String)
dostępne
                   model: String
                                                              getGuitar(String): Guitar
w klasie           type: String                                                                                    Do tej metody
Guitar.                                                       search(Guitar): Guitar                               przekazywane
                   backWood: String
                                                                                                                   są szczegółowe
                   topWood: String                                                                                 informacje
                                                        Ta metoda                                                  o gitarze;
                   getSerialNumber(): String            pobiera numer                                             metoda
                                                        seryjny gitary           Ta metoda służy do               ta tworzy
                   getPrice(): double                                            wyszukiwania gitar;
                                                        i zwraca                                                  odpowiedni
                   setPrice(float)                      reprezentujący          pobiera ona obiekt                obiekt Guitar
                   getBuilder(): String                 ją obiekt.              stanowiący reprezentację          i zapisuje go
                                                                                gitary idealnie spełniającej      w magazynie
                   getModel(): String                                           wymagania klienta i zwraca        Ryśka.
  Oto metody       getType(): String                                            wyszukany w magazynie
  klasy                                                                         obiekt gitary, która pasuje
                   getBackWood(): String                                        do zadanych kryteriów.
  Guitar.
                   getTopWood(): String

                                                              do informacji
                                     Rysiek zdecydował, że
                                     opisujących każ  dą gitarę należą:
                                                            producent                    Przygotowaliśmy dla Ciebie kilka
                                     numer seryjny, cena, yczna bądź                     interesujących i ciekawych sma
                                     ora z model, typ (elektr                                                           kołyków,
                                                               k drewna,                znajdziesz je w dodatku B. Jeśl
                                     akustyczna) oraz gatune tał wykonany.                                              i
                                                                                        pierwszy stykasz się z zagadnie po raz
                                     z jakieg o instrument zos                                                           niam
                                                                                        dotyczącymi projektowania obiektow i
                                                                                        lub języka UML, to zajrzyj tam      ego
                                                                                        koniecznie.


      Nowy w Obiektowie?
        Jeśli nie spotkałeś się wcześniej z projektowaniem obiektowym, nie słyszałeś o diagramach UML
        bądź też nie jesteś pewny, czy dobrze rozumiesz znaczenie diagramów przedstawionych na
        powyższym rysunku, nie przejmuj się! Przygotowaliśmy specjalny pakiet ratunkowy „Witamy
        w Obiektowie”, który pomoże Ci wszystko zrozumieć. Zajrzyj na sam koniec książki i przeczytaj
        dodatek B — gwarantujemy Ci, że nie będziesz żałował. Kiedy skończysz, ponownie przeczytaj tę
        stronę, a na pewno wszystko nabierze zupełnie nowego sensu.


                                                                                                             jesteś tutaj      33
Początkowy kod aplikacji Ryśka


Oto jak wygląda kod Guitar.java
Na poprzedniej stronie przedstawiliśmy diagramy klas tworzących aplikację Ryśka;
teraz nadszedł czas, abyś zobaczył, jak wygląda faktyczny kod źródłowy klas Guitar
i Inventory (umieszczony odpowiednio w plikach Guitar.java oraz Inventory.java).

public class Guitar {
  private String serialNumber, builder, model, type, backWood, topWood;              To wszystko są
  private double price;                                                              właściwości, które   niej
  public Guitar(String serialNumber, double price,                                   widzieliśmy już wcześ itar.
                                                                                     na diagramie klasy Gu
                String builder, String model, String type,
                String backWood, String topWood) {
    this.serialNumber = serialNumber;
    this.price = price;
    this.builder = builder;                   Na diagramach UML nie są umieszczane
    this.model = model;                       konstruktory klas; konstruktor klasy Guitar robi
    this.type = type;                         dokładnie to, czego można od niego oczekiwać:
                                              określa początkowe wartości właściwości nowego
    this.backWood = backWood;                 obiektu Guitar.
    this.topWood = topWood;
  }

    public String getSerialNumber() {
      return serialNumber;
    }

    public double getPrice() {
      return price;                                                                   Guitar
    }
                                                                             serialNumber: String
    public void setPrice(float newPrice) {
      this.price = newPrice;                                                 price: double
    }                                                                        builder: String
    public String getBuilder() {                                             model: String
      return builder;                             Łatwo zauważyć,            type: String
                                                  jak diagram klas
    }                                             odpowiada metodom,         backWood: String
    public String getModel() {                    które możemy znaleźć       topWood: String
      return model;                               w kodzie źródłowym
                                                  klasy Guitar               getSerialNumber(): String
    }
    public String getType() {                                                getPrice(): double
      return type;                                                           setPrice(float)
    }                                                                        getBuilder(): String
    public String getBackWood() {                                            getModel(): String
      return backWood;                                                       getType(): String
    }                                                                        getBackWood(): String
    public String getTopWood() {                                                                  class
                                                                             getTopWood(): String {
                                                                                                  Guitar
      return topWood;                                                                              Gui-
                                                                                                tar()
    }                                                                                           }
}
                                                                                               Guitar.java



34      Rozdział 1.
Dobrze zaprojektowane aplikacje są super


Plik Inventory.java…
                                          Pamiętaj, że usunęl iśmy instrukcje
public class Inventory {                                          nieco miejsca.
  private List guitars;                   import, by zaoszczędzić
  public Inventory() {
    guitars = new LinkedList();
  }
  public void addGuitar(String serialNumber, double price,
                        String builder, String model,
                        String type, String backWood, String topWood)
{
    Guitar guitar = new Guitar(serialNumber, price, builder,
                                model, type, backWood, topWood);             Metoda addGuitar() pob
    guitars.add(guitar);                                                     wszystkie informacje iera
                                                                                                 kon
  }                                                                          utworzenia nowego obi ieczne do
                                                                                Guitar i dodaje go do ektu typu
                                                                                                      magazynu.
    public Guitar getGuitar(String serialNumber) {
      for (Iterator i = guitars.iterator(); i.hasNext(); ) {
        Guitar guitar = (Guitar)i.next();
        if (guitar.getSerialNumber().equals(serialNumber)) {
           return guitar;
        }
      }
      return null;                                                                                        plikowana…
    }                                                                 Ta metoda jest nieco bardziej skom obiektu
                                                                                                         ści
                                                                      porównuje ona wszystkie właściwo iu,
                                                                                                      ołan
    public Guitar search(Guitar searchGuitar) {                       Guitar, przekazanego w jej wyw któw tego typu,
      for (Iterator i = guitars.iterator(); i.hasNext(); ) {          z właściwo ściami wszystkich obie
        Guitar guitar = (Guitar)i.next();                             dostępnych w magazynie Ryśka.
        // Ignorujemy numer seryjny bo jest unikalny
        // Ignorujemy cenę gdyż jest unikalna
        String builder = searchGuitar.getBuilder();
        if ((builder != null)  (!builder.equals(“”)) 
             (!builder.equals(guitar.getBuilder()))
           continue;
        String model = searchGuitar.getModel();
        if ((model != null)  (!model.equals(“”)) 
             (!model.equals(guitar.getModel()))
           continue;
        String type = searchGuitar.getType();
        if ((type != null)  (!type.equals(“”)) 
             (!type.equals(guitar.getType()))
           continue;
        String backWood = searchGuitar.getBackWood();
        if ((backWood != null)  (!backWood.equals(“”))                                 Inventory
             (!backWood.equals(guitar.getBackWood()))
           continue;                                                     guitar: List
        String topWood = searchGuitar.getTopWood();
        if ((topWood != null)  (!topWood.equals(“”))                 addGuitar(String, double, String, String, String,
             (!topWood.equals(guitar.getTopWood()))                                String, String)
           continue;                                                     getGuitar(String): Guitar             class
        return guitar;                                                                                         Inven-
      }
                                                                         search(Guitar): Guitar                tory{

      return null;                                                                                             search()
    }
}                                                                                                         Inventory.java

                                                                                              jesteś tutaj               35
Problem brakującej gitary


Wkrótce jednak okazało się, że Rysiek zaczął tracić klientów…
Wygląda na to, że niezależnie od tego, kim jest klient i jakiej gitary szuka, nowy program wyszukujący gitary
prawie nigdy nie jest w stanie dopasować odpowiedniego instrumentu. Jednak Rysiek doskonale wie, że posiada
gitarę, która na pewno spodobałaby się danemu klientowi… Zatem gdzie tkwi problem?
                                                                                              Program FindGuitarTester.java
                                                                                              symuluje typowy dzień pracy
                                                                                              Ryśka… Zjawia się klient,
                                                                                              mówi Ryśkowi, jaka gitara
           public class FindGuitarTester {                                                    by go interesowała, a Rysiek
                                                                                              przeszukuje swój magazyn.
                                                                                   tary
               public static void main(String[] args) {             Ewa szuka gi Fender,
                 // Inicjalizacja zawartości magazynu gitar Ryśka   „Str at” firmy
                                                                                    całości
                 Inventory inventory = new Inventory();             wykonanej w owego.
                                                                    z drew  na olch
                 initializeInventory(inventory);

               Guitar whatEveLikes = new Guitar(“”, 0, “fender”, “Stratocastor”,
                                                      “elektryczna”, “olcha”, “olcha”);
               Guitar guitar = inventory.search(whatEveLikes);
           if (guitar != null) {
                  System.out.println(“Ewo, może spodoba Ci się gitara “ +
                    guitar.getBuilder() + “ model “ + guitar.getModel() + “ “ +
                    guitar.getType() + “ :n    “ +
                    guitar.getBackWood() + “ - tył i boki,n    “ +
                    guitar.getTopWood() + “ - góra.nMożesz ją mieć za “ +
                    guitar.getPrice() + “PLN!”);
               } else {
                  System.out.println(“Przykro mi, Ewo, nie znalazłem nic dla Ciebie.”);
               }
             }

               private static void initializeInventory(Inventory inventory) {
                 ...
               }
           }                                                                class
                                                                            FingGui-
                                                                            tar {
                                                                              main()
                                                                            }


                                                                     FindGuitarTester.java


                                                                                               Przykro mi, Ryśku, ale
                                                                                              wygląda na to, że pójdę
                                                                                                  do innego sklepu.



                                            zie do
            Oto co się dzieje, gdy Ewa wejd leźć
            sklepu Ryśka, a ten spróbuje odna
            gitarę spełniającą jej oczekiwania.


36     Rozdział 1.
Dobrze zaprojektowane aplikacje są super




                   Ale przecież ja wiem,
                                         że
                 świetną gitarę Stratocas mam
                                          tor fir
                      Fender. Jest właśnie tut my
                                               aj:




                    inventory.addGuitar(”V95693”, 1499.95,
                    ”Fender”, ”Stratocastor”,
                    ”elektryczna”, ”olcha”, ”olcha”);

                                                                               Wydaje się, że
                                                                               specyfikacja gitary
                                                                               dokładnie odpowiada
                                                                               oczekiwaniom Ewy…
                                                                               Zatem co się stało?
                  Oto fragment kodu inicjalizującego
                  zawartość magazynu Ryśka. Wygląda na
                  to, że Rysiek faktycznie ma gitarę, która
                  spełnia oczekiwania Ewy.




Zaostrz ołówek
                  W jaki sposób zmieniłbyś projekt aplikacji Ryśka?
                  Przeanalizuj kody aplikacji Ryśka, przedstawione na trzech poprzednich stronach, oraz
                  wyniki wykonanego testu. Jakie problemy udało Ci się zauważyć? Co byś zmienił?
                  Poniżej zapisz PIERWSZĄ rzecz, jaką chciałbyś poprawić w aplikacji Ryśka.




                                                                                      jesteś tutaj       37
Jak pisać wspaniałe programy?


Co przede wszystkim zmieniłbyś w aplikacji Ryśka?
Oczywiste jest, że w aplikacji Ryśka występuje jakiś problem; jednak znacznie trudniej
jest określić, od czego należy zacząć wprowadzanie poprawek. I wygląda na to,
że istnieje wiele różnych opinii na ten temat:
                                                                                Hm… w swoich notatkach właściciel
                                                                                 firmy wyraźnie pisze, że chciałby,
                                                                                żeby klienci mieli możliwość wyboru
               Spójrzcie na te wszystkie                                           wielu instrumentów. Czy zatem
              łańcuchy znaków! To jest po                                           metoda search() nie powinna
             prostu okropne… czy zamiast                                               zwracać listy obiektów?
             nich nie możemy użyć stałych
                     lub obiektów?


                                 Guitar
                        serialNumber: String
                        price: double                                             Inventory
                        builder: String
                        model: String                           guitar: List
                        type: String                            addGuitar(String, double, String, String, String,
                        backWood: String                                  String, String)
                        topWood: String                         getGuitar(String): Guitar
                        getSerialNumber(): String               search(Guitar): Guitar
                        getPrice(): double
                                                               Ten projektant musi być
                        setPrice(float)
                                                         naprawdę kiepski. Klasy Guitar oraz
                        getBuilder(): String
                                                       Inventory w zbyt dużym stopniu zależą
                        getModel(): String             od siebie nawzajem. Nie potrafię sobie
                        getType(): String                wyobrazić, by to była architektura,
                        getBackWood(): String             na której mógłby bazować rozwój
                        getTopWood(): String                  aplikacji. Musimy ją jakoś
                                                                        zmienić.




 Jerzy zajmuje się                                                                                      Julka zyskała reputację
 programowaniem                                                                                         osoby, która zawsze
 stosunkowo krótko, jednak                                                                              oferuje klientom to,
 szczerze i głęboko wierzy                                                                              czego chcą.
 w słuszność pisania kodu
 obiektowego.

     Franek zajmuje się programowan
                                     iem
                                                                               Co zrobiłbyś w pierwszej
     już od jakiegoś czasu i naprawd
     dobrze zna zasady projektowania
                                    ę
                                                                                     kolejności?
     obiektowego i wzorce projektowe.


38      Rozdział 1.
Dobrze zaprojektowane aplikacje są super




   Niby skąd mam wiedzieć, od czego należy zacząć? Mam
 wrażenie, że ilekroć zaczynam pracę nad nowym projektem,
    każdy ma inne zdanie odnośnie tego, co należy zrobić
   w pierwszej kolejności. Czasami zrobię coś dobrze, lecz
czasami kończy się na tym, że muszę przerobić całą aplikację
    od początku, bo zacząłem w złym miejscu. A ja chcę
       jedynie pisać świetne oprogramowanie!
        A zatem, od czego powinienem zacząć pisanie
                     aplikacji dla Ryśka?




                                                       W jaki sposób
                                                       można za każdym
                                                       razem pisać dobre
                                                       oprogramowanie?




                                                                   jesteś tutaj    39
Ale co to znaczy „doskonałe oprogramowanie”?


                      Chwileczkę… nie lubię się wtrącać, ale co
                 to właściwie oznacza „wspaniałe oprogramowanie?
                     To jakieś tajemnicze hasło, które rzuca się
                        w rozmowach, by zrobić odpowiednie
                                      wrażenie?

                       Dobre pytanie… na które można podać
                       wiele różnych odpowiedzi:



                         Programista dbający o dobro klienta odpowie:
                          „Doskonałe oprogramowanie zawsze robi to, czego chce
                          klient. A zatem, nawet jeśli klient wymyśli nowy sposób
                          zastosowania takiego oprogramowania, to nie będzie ono
                          działać niewłaściwie ani zwracać nieoczekiwanych wyników”.


                                                                            Podstawą tego podejścia jest zwrócenie
                                                                            największej uwagi na to, by użytkownik był
                                                                            zadowolony z działania aplikacji.



                 Programista obiektowy odpowie:                                                    To podejście,
                                                                                                   koncentrujące uwagę na
                                                                                                   zagadnieniach projektu,
                    „Doskonałe oprogramowanie to kod napisany obiektowo.                           pozwala na tworzenie
                                                                                                   kodu zoptymalizowanego
                    Jest to zatem kod, w którym nie ma żadnych powtórzeń oraz                      pod kątem przyszłego
                    w którym obiekty w bardzo dużym stopniu kontrolują swoje                       rozwoju aplikacji
                    własne zachowanie. Takie oprogramowanie także łatwo                            i wielokrotnego
                                                                                                   używania jej elementów.
                    rozbudować, gdyż jego projekt jest solidny i elastyczny”.                      Wykorzystuje ono
                                                                                                   wzorce projektowe oraz
                                                                                                   wielokrotnie sprawdzone
                                                                                                   techniki projektowania
                                    ze   Nie jesteś całkiem pewny, co to                           i programowania
Dobrzy programiści obiektowi zaws        wszystko znaczy? Nie martw się…                           obiektowego.
poszukują sposobów na to, by ich         wszystkiego dowiesz się w kole
                                                                        jnych
kod był jak najbardziej elastyczny.      rozdziałach.


     Programista będący autorytetem w dziedzinie projektowania odpowie:
      „Oprogramowanie będzie doskonałe, jeśli podczas jego tworzenia zastosujemy
      wypróbowane i sprawdzone wzorce projektowe i zasady projektowania.
      Zachowałeś luźne powiązania pomiędzy obiektami i sprawiłeś, by kod był
      otwarty na rozszerzanie, lecz zamknięty na modyfikacje. To także powoduje,
      że łatwiejsze będzie wielokrotne wykorzystanie kodu, a dzięki temu nie
      będziesz go musiał przerabiać, chcąc wykorzystać fragmenty aplikacji
      w innych projektach”.


40       Rozdział 1.
Dobrze zaprojektowane aplikacje są super




Zaostrz ołówek                 A co dla Ciebie znaczy termin „doskonałe oprogramowanie”?
                               Dowiedziałeś się już, co termin „doskonałe oprogramowanie” oznacza dla kilku
                               różnych typów programistów. Zatem kto z nich ma rację? A może masz swoją własną
                               definicję, która określa, co sprawia, że tworzona aplikacja będzie doskonała? Jeśli tak,
                               to nadszedł czas, byś napisał własną definicję doskonałego oprogramowania:



                           …                                                        ...a tu podaj co wedłu
             oje personalia
Tu zapisz sw                                                                        termin „doskonałe opr g Ciebie oznacza
                                                                                                         ogramowanie”:




                               uważa, że:

 „



                                                                                                                          ”




                                                                                                     jesteś tutaj            41
Doskonałe oprogramowanie spełnia potrzeby zarówno klienta, jak i programisty


Doskonałe oprogramowanie…
ma więcej niż jedną z wymienianych już cech
Nie sposób określić, czym jest „doskonałe oprogramowanie”, przy użyciu
jednej prostej definicji. W rzeczywistości wszystkie stwierdzenia różnych
programistów dotyczące dobrego oprogramowania, podane na stronie
40, określają cechy, dzięki którym oprogramowanie można uznać za
„doskonałe”.

Przede wszystkim doskonałe oprogramowanie musi spełniać
wymagania i oczekiwania klienta. Oprogramowanie musi
robić to, czego klient od niego oczekuje.


            Podbij swoich klientów.
            Klienci uznają Twoje oprogramowanie za doskonałe,
            jeśli będzie ono robić to, czego od niego oczekują.




Tworzenie oprogramowania, które działa we właściwy sposób, jest czymś
wspaniałym. Co się jednak stanie w sytuacjach, kiedy takie oprogramowanie
trzeba będzie rozbudować lub zastosować jego część w innym projekcie?
Napisanie kodu, który działa zgodnie z oczekiwaniami klienta, to za mało;           O rany, jeśli mój kod naprawdę
równie ważne jest to, by przeszedł on próbę czasu.                               mógłby mieć te wszystkie cechy, to
                                                                                 pisane przeze mnie aplikacje byłyby
Poza tym, doskonałe oprogramowanie powinno być                                   wspaniałe! Potrafię nawet zapisać te
dobrze zaprojektowane, poprawnie napisane oraz musi                              wszystkie wymagania w kilku prostych
zapewniać łatwość utrzymania, wielokrotnego stosowania                            punktach, które można by stosować
i rozszerzania.                                                                       we wszystkich projektach.


            Niech Twój kod będzie tak inteligentny jak Ty sam.
            Zarówno Ty, jak i Twoi współpracownicy sami uznacie, że
            oprogramowanie jest doskonałe, jeśli jego utrzymanie, rozszerzanie
            i wielokrotne stosowanie nie będą przysparzać większych problemów.




42     Rozdział 1.
Dobrze zaprojektowane aplikacje są super


Wspaniałe oprogramowanie
w trzech prostych krokach                                           Obecnie może Ci się wydawać, że to wcale
                                                                    nie jest takie łatwe. Jednak pokażemy, że
                                                                    analiza i projektowanie obiektowe, wraz
                                                                    z kilkoma prostymi zasadami, mogą na
                                                                    zawsze zmienić postać tworzonego przez
                                                                    Ciebie oprogramowania.


1. Upewnij się,
   że oprogramowanie                                                         jemy uwagę na klienci
                                                                                                   e.
                                                     W tym kroku koncentru ŚCI powinieneś zapewnić,
   robi to, czego
                                                                            NO
                                                     W PIERWSZEJ KOLEJ ić to, czego klient od niej
                                                     że aplikacja będzie rob prac dużą rolę odgrywa
                                                                            pie
                                                     oczekuje. Na tym eta
   oczekuje klient.                                  przygotowanie wymagań
                                                     odpowiedniej analizy .
                                                                                i przeprowadzenie




                           2. Zastosuj proste zasady
                             projektowania obiektowego,
                             by poprawić elastyczność
                             oprogramowania.

   Kiedy oprogramowanie
                          będ
   możesz odszukać w nim zie już działać,
   powtarzające się fragm i usunąć
   upewnić się, że zastos enty kodu oraz
   projektowania obiektow owałeś dobre techniki
                         ego.
                                                     3. Staraj się, by projekt
                                                        oprogramowania zapewniał
                                                        łatwość jego utrzymania
                                                        i pozwalał na jego
          Czy uzyskałeś dobrą aplikację obiektową,
          która robi to, co powinna? Nadszedł
                                                        wielokrotne stosowanie.
          czas, by zastosować wzorce i zasady,
          dzięki którym upewnisz się, że Twoje
          oprogramowanie jest odpowiednio
          przygotowane do wieloletniego
          użytkowania.


                                                                                         jesteś tutaj          43
Stosowanie przedstawionych wcześniej kroków


Pamiętasz Ryśka? Pamiętasz klientów, których stracił?
Wypróbujmy nasze pomysły i założenie dotyczące tworzenia doskonałego oprogramowania
i przekonajmy się, czy nadają się one do zastosowania w realnym świecie. Rysiek dostał
program do wyszukiwania gitar, który nie działa poprawnie, i to Twoim zadaniem będzie
jego poprawienie i dołożenie wszelkich starań, by stworzyć naprawdę wspaniałą aplikację.
Przyjrzyjmy się jeszcze raz programowi, jakim obecnie dysponuje Rysiek, i zobaczmy,
w czym tkwi problem:

Oto nasz program
testowy, który
uwidocznił problemy          public class FindGuitarTester {
w działaniu narzędzia            public static void main(String[] args) {
do wyszukiwania                                                                       Aplikacja Ryśk
gitar pasujących do                // Inicjalizacja magazynu gitar Ryśka              powinna dopa a
kryteriów podanych                                                                    te wymaganiasować
                                   Inventory inventory = new Inventory();                          …
 przez użytkownika.
                                   initializeInventory(inventory);
                                   Guitar whatEveLikes = new Guitar(””, 0, ”fender”, ”Stratocastor”,                 ...do tej gitary
                                                                                                                     znajdującej się
                                                                     ”elektryczna”, ”olcha”, ”olcha”);               w magazynie.
                                   Guitar guitar = inventory.search(whatEveLikes);
                                   if (guitar != null) {

                        class
                                                                                     inventory.addGuitar(”V95693”,
                        FingGui-                                                     1499.95, ”Fender”, ”Stratocastor”,
                        tar {
                          main()                                                     ”elektryczna”, ”olcha”, ”olcha”);
                        }


                 FindGuitarTester.java



A zatem wykonajmy nasze trzy
podane wcześniej kroki:
                   1. Upewnij się, że
                      oprogramowanie robi to,
                      czego oczekuje klient.                                                       Na razie nie zawr
                                                                                                                      ac
                                                                                                   głowy stosowaniem aj sobie
                                                                                                  wzorców ani obiek w aplikacji
                                                                                                  technik programo towych
                                                                                                  skoncentruj się wania…
 Pamiętaj, że musimy , by                   2. Zastosuj proste zasady projek-                     działała tak, jak
                                                                                                                    na tym, by
 zacząć od zapewnienia go                                                                                            powinna.
 aplikacja robiła to, cze                      towania obiektowego, by poprawić
                        nie
 oczekuje Rysiek… a        cna                 elastyczność oprogramowania.
 ma wątpliwości, że obe o
                         teg
 aplikacja nie spełnia
  warunku.
                                                                 3. Staraj się, by projekt oprogramowania
                                                                    zapewniał łatwość jego utrzymania
                                                                    i pozwalał na jego wielokrotne stosowanie.
44      Rozdział 1.
Dobrze zaprojektowane aplikacje są super



                                   Skoro zaczynamy od funkcjonalności, to sprawdźmy, co
                                 się dzieje z tą niedziałającą metodą search(). Wygląda na
                                 to, że w magazynie Ryśka nazwa producenta jest zapisana
                                  małymi literami, a w wymaganiach klienta nazwa „Fender             Skorzystajmy
                                 zaczyna się z wielkiej litery. A zatem w metodzie search()           z drobnej pomocy
                                     musimy zastosować wyszukiwanie, które nie będzie                 naszych znajomy
                                                                                                                      ch
                                                                                                      programistów.
                                                 uwzględniać wielkości liter.



Franek: Oczywiście, to by rozwiązało bieżące problemy Ryśka, niemniej
jednak uważam, że istnieje lepszy sposób zapewnienia poprawnego działania
aplikacji niż wywoływanie metody toLowerCase() we wszystkich miejscach,
gdzie są porównywane łańcuchy znaków.

Jerzy: Właśnie, o tym samym pomyślałem. Wydaje mi się, że to całe
porównywanie łańcuchów znaków to nie jest najlepszy pomysł. Czy nie
moglibyśmy zastosować jakichś stałych lub jakiegoś typu wyliczeniowego do
określania producentów gitar oraz gatunków drewna?

Julka: Panowie, wybiegacie myślami zdecydowanie zbyt daleko. Krok 1.
miał polegać na poprawieniu aplikacji w taki sposób, by robiła to, czego od
niej oczekuje klient. Sądziłam, że na tym etapie prac nie będziemy zajmowali
się zagadnieniami związanymi z projektem.

Franek: Cóż, to prawda; rozumiem, że mamy się skoncentrować na
kliencie. Ale możemy przynajmniej zastanowić się, jak inteligentnie                                        Jerzy
rozwiązać aktualnie występujące problemy, nieprawdaż? Chodzi mi o to,            Franek       Julka
by nie tworzyć kolejnych problemów, które w przyszłości znowu będziemy
musieli rozwiązywać.

Julka: Hmm… tak, całkiem słusznie. Na pewno nie chcemy, by
zaproponowane przez nas rozwiązanie bieżących problemów powodowało
pojawienie się nowych problemów projektowych. Jednak pomimo to na razie
nie będziemy zajmowali się innymi fragmentami aplikacji, dobra?

Franek: Dobra. Możemy ograniczyć się do usunięcia tych wszystkich
łańcuchów znaków, porównywania łańcuchów znaków i problemów                    Rozwiązując
związanych z wielkościami liter.

Jerzy: Właśnie. Jeśli zastosujemy typy wyliczeniowe, to zyskamy pewność,       istniejące problemy,
że podczas określania producenta gitary, typu drewna oraz rodzaju
instrumentu będą używane wyłącznie prawidłowe wartości. W ten sposób           nie twórz nowych.
zagwarantujemy, że Rysiek będzie mógł znajdować gitary pasujące do
wymagań i oczekiwań klientów.

Julka: A jednocześnie poprawimy nieco projekt samej aplikacji… super!
A zatem — do roboty, panowie.
                                                                                          jesteś tutaj      45
Krok 1: Zaspokojenie potrzeb klienta


      Eliminacja porównywania łańcuchów znaków
      Pierwszą poprawką, jaką możemy wprowadzić w aplikacji Ryśka, jest usunięcie porównywania łańcuchów
      znaków. Choć można by zastosować metodę toLowerCase(), by rozwiązać problem z porównywaniem liter
      różnych wielkości, to jednak postaramy się w ogóle wyeliminować porównywanie łańcuchów znaków:
                                                                                                                         To wszys
                                                                                                                                   tk
                                                                                                                         wyliczenio o są
                                                                                                                        Javy — e we typy
                        public enum Type {                                                                              działają p num — które
                                                                                                             enum       stałych. odobnie do
                            ACOUSTIC, ELECTRIC;                                                              Type {
 Każdy z typów                                                                                                 to-
                            public String toString() {                                                       String()
 wyliczeniowych                                                                                              }
 zastąpi jedną                  switch(this) {
 z właściwości                      case ACOUSTIC: return ”akustyczna”;
 klasy Guitar,                                                                                             Type.java
 która jest                         case ELECTRIC: return ”elektryczna”;
                                          public enum Builder {
 standardowa dla                                                                                                          enum
                                    default:        return ”nieokreślona”;
 wszystkich jej                              FENDER, MARTIN, GIBSON, COLLINGS, OLSON, RYAN, PRS, ANY;                     Builder{
 obiektów.                      }                                                                                           to-
                                            public String toString() {                                                    String()
                            }                                                                                             }
                                              switch(this) {
                        }
                                                 case FENDER:      return „Fender”;                                     Builder.java
                                                 case MARTIN:   return „Martin”;
                                                         public enum Wood {
                                                 case GIBSON:   return „Gibson”;
                                                           INDIAN_ROSEWOOD, BRAZILIAN_ROSEWOOD, MAHOGANY, MAPLE,
                                                 case COLLINGS: return „Collings”;
                                                           HEBAN, CEDAR, OAK, ALDER, SITKA;
                       y się
Do tych wartości możemzapisu                                 public String toString() {
odwoływać przy użyciu r.
Wood.SITKA lub Builde uniknąć
                     u
                                                                switch(this) {
GIBSON, a dzięki tem a                                             case INDIAN_ROSEWOOD:        return ”palisander indyjski”;
stosow ania porównywani
łańcuchów znaków.                                                  case BRAZILIAN_ROSEWOOD: return ”palisander brazylijski”;

       Jedną ze wspania                                            case MAHOGANY:                return ”mahoń”;
                           łyc
       wyliczeniowych jes h zalet typów                            case MAPLE:                   return ”klon”;
       ograniczenia pote t możliwość
       przekazywanych ncjalnych wartości                           case EBONY:                   return ”heban”;                           enum
      zatem już nigdy w wywołaniach metod…
                                                                                                                                           Wood{
                                                                                                                                             to-
      przejmować się nie będziemy musieli                                                                                                  String()
                       pr
      typograficznymi ostymi błędami                                                                                                       }
                       lub problemami
      z wielkością liter
                         .
                                                                                                                                         Wood.java

               Nie ma
                 niemądrych pytań
      P.:  Nigdy wcześniej nie spotkałem się w języku Java ze
                                                                                 Typy wyliczeniowe pozwalają na podanie nazwy typu, na przykład
                                                                                 Wood, oraz listy wartości, jakie mogą być stosowane w ramach tego
      słowem kluczowym enum. Co to takiego?
                                                                                 typu (na przykład: SITKA, ALDER bądź CEDAR). A zatem odwołanie
      O.:   Pozwala ono na definiowanie typów wyliczeniowych. Ten typ            do konkretnej wartości ma postać: Wood.SITKA.
      danych jest dostępny także w języku C, C++ oraz w Javie w wersji
      5.0 i kolejnych. Co więcej, pojawi się także w wersji 6.0 języka Perl.     P A dlaczego typy wyliczeniowe przydadzą się nam
                                                                                  .:
                                                                                 w aplikacji Ryśka?


     46        Rozdział 1.
Dobrze zaprojektowane aplikacje są super

                     public class FindGuitarTester {                                     Te wszystk
                                                                                                     ie
                                                                                         znaków mog łańcuchy
                        public static void main(String[] args) {                         zastąpić w liśmy już
                                                                                                    ar
                            // Inicjalizacja zawartości magazynu gitar Ryśka             wyliczeniow tościami typów
                                                                                                    ych.
                            Inventory inventory = new Inventory();
Jedyny łańcuch
znaków, jaki nam            initializeInventory(inventory);
pozostał i jaki             Guitar whatEveLikes = new Guitar(“”, 0, Builder.FENDER,
sprawdzamy,                   “Stratocastor”, Type.ELECTRIC, Wood.ALDER, Wood.ALDER);
określa                                                                                                            class
model gitary;               Guitar guitar = inventory.search(whatEveLikes);                                        FindGui-
zostawiliśmy                if (guitar != null) {                                                                  tar {
go, gdyż zbiór                                                                                                     }
                                                                                                                     main()
 dostępnych
 modeli nie jest
 ograniczony,                                                                                              FindGuitarTester.java
 w odróżnieniu
 od producentów
 gitar oraz                 public List search(Guitar searchGuitar) {
 gatunków drewna             for (Iterator i = guitars.iterator(); i.hasNext(); ) {
  używanych do ich
  wytwarzania.                 Guitar guitar = (Guitar)i.next();
                               // Ignorujemy numer seryjny, bo jest unikalny
                               // Ignorujemy cenę, gdyż jest unikalna
                               if (searchGuitar.getBuilder() != guitar.getBuilder())
                                                                                                    Jedyną
                                                                                                    właściwością,
                                 continue;                                                          w jakiej
Wygląda na                     String model = searchGuitar.getModel().toLowerCase();                musimy zadbać
to, że nic się                                                                                      o wielkość
nie zmieniło,                  if ((model != null)  (!model.equals(“”))                          liter, jest
jednak dzięki                       (!model.equals(guitar.getModel().toLowerCase())))                nazwa modelu
zastosowaniu                     continue;                                                           gitary — ona
typów                                                                                                 wciąż jest
wyliczeniowych                 if (searchGuitar.getType() != guitar.getType())                        zwyczajnym
nie musimy już                   continue;                                                            łańcuchem
martwić się, że                if (searchGuitar.getBackWood() != guitar.getBackWood())                znaków.
metoda zwróci
niewłaściwe                      continue;
wyniki ze                      if (searchGuitar.getTopWood() != guitar.getTopWood())
względu na                       continue;
różnice
w wielkości liter              return guitar;
użytych w obu                }
łańcuchach.
                             return null;                                                                          class
                        }                                                                                          Inven-
                                                                                                                   tory {
                                                                                                                   search()
                                                                                                                   }


                                                                                                                Inventory.java


    O.:   Bardzo dużą zaletą typów wyliczeniowych jest to, iż         P Używam starszej wersji języka Java. Czy to oznacza,
                                                                       .:
    zabezpieczają one metody, w których są używane przed              że mam kolejny problem?
    przekazaniem wartości niezdefiniowanych w danym typie.
    A zatem, jeśli tylko wartość typu wyliczeniowego zostanie         O.:    Nie, nie będziesz mieć żadnych problemów. Zajrzyj do plików
    błędnie zapisana, kompilator wygeneruje błąd. Jak widać, typy     z serwera FTP Wydawnictwa Helion — zamieściliśmy tam specjalną
    wyliczeniowe są doskonałym sposobem nie tylko na zapewnienie      wersję aplikacji Ryśka, w której nie są używane typy wyliczeniowe
    bezpieczeństwa typów, lecz także bezpieczeństwa wartości — nie    i która z powodzeniem będzie działać także w starszych wersjach
    sposób użyć niepoprawnych danych, jeśli można je wybrać tylko     języka Java.
    z ograniczonego zakresu lub zbioru ściśle określonych wartości.

                                                                                                             jesteś tutaj           47
Słabe aplikacje łatwo się psują
                                                                                                          Teraz metoda      ra
 Przyjrzyjmy się ogólnej postaci aplikacji Ryśka:                                                         addGuitar() pobie w
                                                                                                                           pó
                                                                                                          kilka wartości tya nie
                                                                                                          wyliczeniowych,     lub
                                                                                                           łańcuchów znaków
                                                                                                           stałych liczbowych.
  Zastąpiliśmy
 większość                        Guitar                                        Inventory
 łańcuchów znaków
 zapisywanych we         serialNumber: String                guitar: List
 właściwościach
                         price: double                                                                             Choć wygląda na
 obiektów Guitar
 wartościami typów                                           addGuitar(String, double, Builder, String, Type       to, że w metodzie
                         builder: Builder                                                                          search() nic się nie
 wyliczeniowych.                                                        Wood, Wood)                                zmieniło, to jednak
                         model: String
                                                             getGuitar(String): Guitar                             teraz używamy wartości
                         type: Type                                                                                typów wyliczeniowych,
                                                             search(Guitar): Guitar
                         backWood: Wood                                                                            dzięki czemu mamy
                         topWood: Wood                                                                             pewność, że nie pojawią
                                                                                                                   się żadne problemy
                                                                                                                   związane z błędami
                         getSerialNumber(): String                                                                 typograficznymi lub
Numer seryjny            getPrice(): double                        Builder                                         zastosowaniem liter
wciąż jest wartością     setPrice(float)                                            Type
                                                                                                                   o różnej wielkości.
unikalną; poza tym                                      toString(): String
                         getBuilder(): Builder
także właściwość
określającą nazwę        getModel(): String                             toString(): String          Wood
modelu gitary            getType(): Type
pozostawiliśmy                                                                           toString(): String
 jako właściwość         getBackWood(): Wood
                                                                 Oto nasze typy
 łańcuchową, gdyż        getTopWood(): Wood                      wyliczeniowe.
 różnych modeli mogą
  być tysiące… a to
  o wiele za dużo, by
  można je zapisać                Klasa Guitar używa tych
  w formie typu                   typów wyliczeniowych do
  wyliczeniowego.                 reprezentacji danych, dzięki
                                  czemu można wyeliminować
                                  błędy związane
                                  z niewłaściwym sposobem
                                  zapisu łańcuchów znaków.



 A zatem co tak naprawdę zrobiliśmy?
 Znacznie zbliżyliśmy się do zakończenia pierwszego                                 1. Upewnij się, że
 z trzech kroków prowadzących do tworzenia doskonałego
 oprogramowania. Problemy Ryśka z odnajdywaniem                                        oprogramowanie
 w magazynie gitar spełniających zadane kryteria to już przeszłość.
                                                                                       robi to, czego
 Co więcej, jednocześnie sprawiliśmy, że aplikacja Ryśka jest bardziej solidna
 i mniej wrażliwa. Nie będzie już tak łatwo przysparzać problemów, gdyż                oczekuje klient.
 poprzez zastosowanie typów wyliczeniowych poprawiliśmy ją zarówno pod
 względem bezpieczeństwa typów, jak i bezpieczeństwa wartości. Z punktu
 widzenia Ryśka oznacza to mniej problemów, a z naszego — łatwiejsze
 utrzymanie aplikacji.

                                       Kod, który nie jest wra
                                       i podatny na awarie, żliwy
                                                              zaz
                                       określa się jako solidn wyczaj
                                                               y.


 48      Rozdział 1.
Dobrze zaprojektowane aplikacje są super

                                                                              Nie ma
Zaostrz ołówek                                                                  niemądrych pytań
           Wykonaj krok 1. w swoim własnym projekcie.                     P A zatem, pracując nad pierwszym krokiem do
                                                                           .:
                                                                          tworzenia doskonałego oprogramowania, można
  Czas przekonać się, czy będziesz w stanie sprostać wymaganiom
                                                                          wprowadzać nieznaczne zmiany w projekcie
  swoich klientów. W pustych wierszach poniżej wpisz krótki opis
                                                                          aplikacji?
  jakiegoś projektu, nad którym aktualnie pracujesz (możesz także
  opisać jakiś projekt, który niedawno ukończyłeś):
                                                                          O.: Tak, o ile tylko cały czas będziesz się koncentrował
                                                                          głównie na potrzebach użytkownika. Chodzi o to, że
                                                                          chcesz, by wszystkie podstawowe cechy i możliwości
                                                                          aplikacji zostały poprawnie zaimplementowane, zanim
  Teraz, w kolejnych pustych wierszach, zapisz pierwszą rzecz, jaką       zaczniesz wprowadzać poważne zmiany w jej projekcie.
  zrobiłeś, rozpoczynając prace nad tym projektem. Czy miało to           Niemniej jednak nic nie stoi na przeszkodzie, byś stosował
  cokolwiek wspólnego z upewnieniem się, że aplikacja będzie działać      dobre praktyki i techniki obiektowe także podczas pracy
  zgodnie z oczekiwaniami i wymaganiami użytkownika?                      nad funkcjonalnością aplikacji, tak by mieć pewność, że
                                                                          od samego początku będzie ona dobrze zaprojektowana.


                                                                          P.:   Czy diagram przedstawiony na stronie 48
                                                                          to diagram klas? Czy też jest to kilka diagramów
                                                                          — w końcu przedstawia więcej niż jedną klasę?
  Jeśli rozpoczynając prace nad projektem, skupisz się na czymś innym
  niż zaspokojenie potrzeb i oczekiwań użytkownika, to zastanów się,
  czym mogłoby się różnić Twoje podejście, gdybyś wiedział o trzech       O.: Jest to diagram klas; na takim diagramie można
  krokach pozwalających na tworzenie wspaniałego oprogramowania.          bowiem zamieścić większą liczbę klas. Prawdę mówiąc,
  Co by się w takim przypadku zmieniło? Czy sądzisz, że Twoja aplikacja   diagramy klas mogą przedstawiać znacznie więcej
  byłaby dzięki temu lepsza, niż jest obecnie, a może gorsza?             informacji na temat klasy, niż pokazaliśmy w diagramach
                                                                          zmieszczonych w tej książce. W kilku kolejnych
                                                                          rozdziałach będziemy dodawali do naszych diagramów
                                                                          kolejne informacje o klasach.


                                                                          P A zatem jesteśmy gotowi, by przejść do
                                                                           .:
                                                                          kroku 2. i rozpocząć stosowanie zasad i technik
                                                                          projektowania obiektowego? Prawda?


                                                                          O.: Niezupełnie. Można wskazać jeszcze kilka innych
                                                                          rzeczy, dzięki którym powinniśmy pomóc Ryśkowi, zanim
                                                                          będziemy gotowi do rozpoczęcia analizy programu
                                                                          i przystąpimy do jego poprawiania. Pamiętaj, że naszym
                                                                          podstawowym zadaniem jest zaspokojenie potrzeb
                                                                          użytkownika; dopiero kiedy to zrobimy, będziemy mogli
                                                                          zająć się poprawianiem projektu naszej aplikacji.




                                                                                                           jesteś tutaj             49
Podobne, ale inne



                                    A ja myślałem, że moja nowa
                                 aplikacja jest doskonała… Dopiero
                                   potem uświadomiłem sobie, że
                                w magazynie mam dwie gitary, które
                                doskonale spełniały oczekiwania Ewy.
                                Czy moglibyście zmienić wyszukiwanie
                                   w taki sposób, by zwracało oba
                                      odnalezione instrumenty?




                            Rysiek jest bardzo zad
                                                   ow
                            z wprowadzonych modyf olony
                            naprawdę pilnie jest ikacji, jednak
                                                 mu
                            możliwość, by aplikacja potrzebna
                           wszystkie odszukane      zwracała
                                                 gitary pasujące
                           do wymagań użytkown
                           jedną z nich.         ika, a nie tylko




                                                                       Rysiek naprawdę
                                                                       chciałby, żeby Ewa
                                                                       mogła obejrzeć obie
                                                                       gitary.
         inventory.addGuitar(”V95693”,
         1499.95, Builder.FENDER,
         ”Stratocastor”, Type.ELECTRIC,
         Wood.ALDER, Wood.ALDER);




  Obie te gitary są           inventory.addGuitar(”V95612”,
  niemal identyczne.
  Różnią się jedynie          1549.95, Builder.FENDER,
  numerem seryjnym            ”Stratocastor”, Type.ELECTRIC,
  i ceną.
                              Wood.ALDER, Wood.ALDER);




50    Rozdział 1.
Dobrze zaprojektowane aplikacje są super


Klienci Ryśka chcą mieć wybór
Rysiek wymyślił nowe wymaganie dla swojej aplikacji: chciałby, żeby narzędzie
wyszukiwawcze zwracało wszystkie gitary znajdujące się w magazynie i spełniające
wymagania klienta, a nie tylko pierwszą z nich.


                                                      Inventory
                                                                                      Chcemy, by w przypadku gdy
                                                                                      Rysiek dysponuje większą liczbą
                                    guitar: List                                      instrumentów spełniających
                                                                                      wymagania podane przez klienta,
                                    addGuitar(String, double, Builder, String, Type   metoda search() mogła zwracać
                                              Wood, Wood)                             wiele obiektów Guitar.
                                    getGuitar(String): Guitar
                                    search(Guitar): List



                Magnesiki z kodem
                Kontynuujemy pracę nad pierwszym z trzech kroków do stworzenia wspaniałej aplikacji i skupiamy
                uwagę na tym, by nasza aplikacja zaczęła działać poprawnie. Poniżej został przedstawiony kod
                metody search() wyszukującej gitary w magazynie Ryśka; umieściliśmy w nim kilka pustych miejsc,
                które Ty musisz wypełnić. Użyj do tego celu magnesików z kodem pokazanych u dołu strony.
                Pamiętaj, że Twoim zadaniem jest takie zmodyfikowanie kodu metody search(), by zwracała ona
                wszystkie odnalezione w magazynie gitary spełniające kryteria podane przez użytkownika.

          public ______ search(Guitar searchGuitar) {
            ______ ______________ = new __________();
            for (Iterator i = guitars.iterator(); i.hasNext(); ) {
              Guitar guitar = (Guitar)i.next();
              // Ignorujemy numer seryjny, bo jest unikalny
              // Ignorujemy cenę, gdyż jest unikalna
              if (searchGuitar.getBuilder() != guitar.getBuilder())
                 continue;
              String model = searchGuitar.getModel().toLowerCase();
              if ((model != null)  (!model.equals(””)) 
                   (!model.equals(guitar.getModel().toLowerCase())))
                 continue;
              if (searchGuitar.getType() != guitar.getType())
                 continue;
              if (searchGuitar.getBackWood() != guitar.getBackWood())
                 continue;
              if (searchGuitar.getTopWood() != guitar.getTopWood())
                 continue;
              _____________._____(_____);
            }
            return ______________;
          }
                                                                    ArrayList
           LinkedList
                      List             matchingGuitars         guitar       ArrayList
      List
            List          matchingGuitars            ngGuitars       guitar
   List          LinkedList               add matchi     matchingGuitars
                                                                                              jesteś tutaj        51
Utrzymanie, projekt i wymagania


                   Magnesiki z kodem
                   Kontynuujemy pracę nad pierwszym z trzech kroków do stworzenia wspaniałej aplikacji
                   i koncentrujemy uwagę na tym, by nasza aplikacja zaczęła działać poprawnie. Poniżej został
                   przedstawiony kod metody search() wyszukującej gitary w magazynie Ryśka; umieściliśmy w nim kilka
                   pustych miejsc, które Ty musisz wypełnić. Użyj do tego celu magnesików z kodem, pokazanych u dołu
                   strony. Pamiętaj, że Twoim zadaniem jest takie zmodyfikowanie kodu metody search(), by zwracała
                   ona wszystkie odnalezione w magazynie gitary spełniające kryteria podane przez użytkownika.

                           public   List search(Guitar
                                     List                   searchGuitar) {
                             List
                           List     matchingGuitars =
                                         matchingGuitars    new LinkedList();
                                                                  LinkedList                                   m miejscu
                                                                                               W praktyce w ty zarówno
                             for (Iterator i = guitars.iterator(); i.hasNext(); ) {            można  zastosować
                                                                                                               , jak
                               Guitar guitar = (Guitar)i.next();                               klasę LinkedList spełniłyby
                                                                                                                ie
                               // Ignorujemy numer seryjny, bo jest unikalny                   i ArrayList… ob
                                                                                               swoje zadanie.
                               // Ignorujemy cenę, gdyż jest unikalna
                               if (searchGuitar.getBuilder() != guitar.getBuilder())
                                 continue;
                               String model = searchGuitar.getModel().toLowerCase();
        Gitary pasują          if ((model != null)  (!model.equals(„”)) 
       do podanych ce
       wymagań zost                 (!model.equals(guitar.getModel().toLowerCase())))
       dodane do lis ają         continue;
      wszystkich ty            if (searchGuitar.getType() != guitar.getType())
      instrumentów
     którymi klient,             continue;
     może być                  if (searchGuitar.getBackWood() != guitar.getBackWood())
     zainteresowan               continue;
                    y.
                               if (searchGuitar.getTopWood() != guitar.getTopWood())                            ArrayList
                                 continue;                                                             List
                                                                                                          guitar ArrayList
                           matchingGuitars . add
                               matchingGuitars               guitar
                                                   add (______________);
                                                                                                               chingGuitars
                               }                                                                LinkedList mat
                                                                                                                      List
                               return matchingGuitars;ars
                                       matchingGuit
                           }
                                                                                        Niewykorzystane magnesiki.

                                                     Nie ma
                                                       niemądrych pytań
P.:   A zatem pierwszego etapu prac            P A dlaczego zakończenie tego etapu
                                                .:                                             P Wydaje mi się, że zwracacie
                                                                                                .:
nie można uznać za zakończony aż do            prac przed rozpoczęciem kolejnego jest          nadmierną uwagę na ten „krok 1.”
momentu, gdy aplikacja będzie działać          takie ważne?                                    i „krok 2.”. A co, jeśli ja projektuję swoje
tak, jak sobie tego życzy klient?                                                              aplikacje w inny sposób?
                                               O.:   Zapewnienie poprawnego działania
O.: Właśnie. Powinieneś upewnić się, że        oprogramowania wymaga zazwyczaj dokonania       O.: Nie musisz ściśle trzymać się podawanych
aplikacja działa tak, jak powinna, zanim       w nim bardzo wielu zmian. Wprowadzanie          przez nas informacji o każdym z kroków.
zajmiesz się zastosowaniem w niej wzorców      zbyt wielu zmian w strukturze aplikacji przed   Niemniej jednak stanowią one prostą sekwencję
projektowych bądź nim zaczniesz wprowadzać     zagwarantowaniem poprawnego działania           czynności, której wykonanie gwarantuje, że
jakiekolwiek poważniejsze zmiany w jej         przynajmniej jej podstawowych możliwości        tworzone oprogramowanie będzie działać
strukturze.                                    funkcjonalnych może się okazać stratą czasu     zgodnie z oczekiwaniami, będzie dobrze
                                               i wysiłku, gdyż struktura programu niekiedy     zaprojektowane i nie wystąpią problemy w jego
                                               ulega poważnym zmianom podczas                  wielokrotnym użytkowaniu. Jeśli w inny sposób
                                               implementacji jego kolejnych możliwości.        zrealizujesz te same cele, to super!
52       Rozdział 1.
Dobrze zaprojektowane aplikacje są super


      Test
      Wielokrotnie pisaliśmy o potrzebie uzyskania od klienta informacji o wymaganiach
      stawianych tworzonemu oprogramowaniu; teraz jednak nadszedł czas, by przekonać
      się, czy te wymagania są dobrze realizowane przez nasz kod. Sprawdźmy zatem, czy
      nasza aplikacja działa tak, jakby sobie tego życzył Rysiek:

Oto program
testowy,                                                                           Używamy w
zaktualizowany       public class FindGuitarTester {
                                                                                  wyliczeniow nim typów
tak, by mógł                                                                      nie będzie ych. Tym razem
korzystać z nowej      public static void main(String[] args) {                   problemów więc żadnych
wersji narzędzia
                         // Inicjalizacja zawartości magazynu gitar Ryśka        niewłaściw spowodowanych
wyszukującego                                                                               ie                        W tej wersji
                         Inventory inventory = new Inventory();                  łańcuchami zapisanymi
gitary                                                                                       znaków!                  programu
w magazynie              initializeInventory(inventory);                                                              testowego
Ryśka.                                                                                                                musimy przejrzeć
                                                                                                                      całą listę
                           Guitar whatEveLikes = new Guitar(””, 0, Builder.FENDER, “Stratocastor”,                    instrumentów
                                                                Type.ELECTRIC, Wood.ALDER, Wood.ALDER);               zwróconą przez
                                                                                                                      narzędzie
                           List matchingGuitars = inventory.search(whatEveLikes);                                     wyszukujące.
                           if (!matchingGuitars.isEmpty()) {
                             System.out.println(“Ewo, może spodobają Ci się następujące gitary:”);
                             for (Iterator i = matchingGuitars.iterator(); i.hasNext(); ) {
 Teraz                         Guitar guitar = (Guitar)i.next();
 otrzymujemy                   System.out.println(“ Mamy w magazynie gitarę “ +
 całą listę                      guitar.getBuilder() + “ model “ + guitar.getModel() + “, jest “ +
 gitar, które
  spełniają                      “to gitara” + guitar.getType() + “ :n      “ +
  wymagania                      guitar.getBackWood() + “ - tył i boki,n      “ +
  określone                      guitar.getTopWood() + “ - góra.n Możesz ją mieć za “ +
  przez klienta.
                                 guitar.getPrice() + “PLN!n ----”);
                             }
                           } else {
                             System.out.println(“Przykro mi, Ewo, nie znalazłem nic dla Ciebie.”);
                           }                                                                               class
                       }                                                                                   FindGui-
                                                                                                           tar {
                                                                                                             main()
                                                                                                           }


                                                                                                    FindGuitarTester.java


                                                                                                 O tak! Teraz program działa
                                                                                                  dokładnie tak, jak tego
                                                                                                          chciałem.



    Wszystko zadziałało tak
    Ewa mogła obejrzeć kilk , jak powinno!
    jej gitar, a klienci na a poleconych
    kupować instrumenty powrót zaczęli
                           w sklepie Ryśka.

                                                                                                   jesteś tutaj            53
Zastosowanie zasad obiektowych


Wróćmy do naszych trzech kroków
Teraz, kiedy nasza aplikacja działa już tak, jak Rysiek sobie tego życzył,
możemy zacząć stosować zasady projektowania obiektowego, by poprawić
jej elastyczność i zapewnić, że będzie dobrze zaprojektowana.
                                                                             Teraz, kiedy aplikacja działa już tak,
                                                                             jak chciał Rysiek, ten krok możemy
                                                                             uznać za zakończony.

1. Upewnij się, że oprogramowanie robi
   to, czego oczekuje klient.
                                                                                          Zatem w końcu nadszedł czas,
                                                                                           gdy możemy upewnić się, że
                                                                                         w aplikacji nie ma powtarzających
                                                                                           się fragmentów kodu oraz że
                                                                                         wszystkie używane w niej obiekty
                                                                                         zostały dobrze zaprojektowane.


2. Zastosuj proste zasady
  projektowania obiektowego,
  by poprawić elastyczność
  oprogramowania.
                                                                                    y
                                                           W tym kroku analizujem
                                                           dzi ałający program
                                                                                  gmenty,
                                                           i sprawdzamy, czy fra scalone
                                                           z jakich  się składa, są
                                                           w sensowny sposób.



3. Staraj się, by projekt
   oprogramowania zapewniał
   łatwość jego utrzymania
   i pozwalał na jego wielokrotne
   stosowanie.




54     Rozdział 1.
Dobrze zaprojektowane aplikacje są super


Szukamy problemów
Przyjrzyjmy się nieco dokładniej naszemu narzędziu do wyszukiwania gitar
i sprawdźmy, czy uda się nam znaleźć jakieś problemy, które moglibyśmy rozwiązać
przy wykorzystaniu prostych zasad projektowania obiektowego. Zacznijmy od
przeanalizowania sposobu działania metody search() klasy Inventory.



                              Szukam akustycznej
                              gitary firmy Martin, masz
                                  może jakąś, Ryśku?



                                                               Klient podaje zbiór kryteriów, jakie
                                                               musi spełniać jego wymarzona gitara;
                                                               jest on przekazywany w formie
                                                               obiektu Guitar.


                                            null, null
                                            Builder.MARTIN
                                            Type.ACUSTIC
                                            „OM-18”
                                            Wood.MAHOGANY
                                                                                Do metody search()       gania
                                            Wood.OAK                            przekazywane są wyma,
                                              Guitar                            określ one przez klienta
                                                                                                          je
                                                                                a jej wywołanie powodu nia
                                                                                                       wa
                    Klient nie podaje ani                                       rozpoczęcie przeszuki
                    ceny poszukiwanej                             class         magazynu Ryśka.
                    gitary, ani                                   Inven-
                                                                                                             Każda gitara
                                                                  tory {
                    numeru seryjnego                                                                         znajdująca się
                   — informacje te są                  search ()
                                                                  Search()}                                  w magazynie
                   bowiem unikalne                                                                           Ryśka jest
                   dla każdej gitary.                                                                        porównywana
                   Podawane są jedynie                         Inventory.java
                                                                                                             ze specyfikacją
                   te wymagania, na                                                                          użytkownika,
                   podstawie których                                                                         przekazaną
                   należy odszukać                                                                           w formie obiektu
                   gitarę.                                                                                   Guitar.



           WYTĘŻ                                                                   Guitar
                                                                                          Guitar
           umYsł                                                                              Guitar
  Czy coś tu jest nie w porządku? Jakie
  problemy mogą wystąpić w narzędziu
                                                                                                 Guitar
  wyszukiwawczym aplikacji Ryśka?
                                                          Oto obiekty Guitar reprezentujące
 funkcjami. Czy zauważyłeś coś dziwnego?                  wszystkie gitary dostępne
 wszystkich obiektów i porównaj je z ich                  w magazynie Ryśka; zawierają
                                                          one numer seryjny, cenę oraz
 Podpowiedź: Przyjrzyj się nazwom                         wszelkie inne informacje
                                                          o poszczególnych instrumentach.

                                                                                                  jesteś tutaj         55
Analiza metody search()


Analiza metody search()
Poświęćmy nieco czasu na dokładniejsze przeanalizowanie tego, co się dzieje w metodzie search() klasy
Inventory. Zanim przyjrzymy się samemu kodowi tej metody, zastanówmy się nad tym, co ona powinna robić.

     1 Klient podaje swoje wymagania dotyczące poszukiwanej gitary.                         Klient może wskazać
                                                                                            jedynie ogólne cechy
       Każdy z klientów przychodzących do sklepu Ryśka chciałby, żeby                       instrumentu. Dlatego też
       poszukiwana gitara posiadała określone cechy: gatunek używanego                      klienci nigdy nie podają
                                                                                            ani ceny, ani numeru
       drewna, typ gitary bądź też określony model, określonego producenta.                 seryjnego poszukiwanej
       Klienci podają te cechy Ryśkowi, który z kolei przekazuje je do narzędzia            gitary.
       przeszukującego magazyn.

     2 Narzędzie wyszukujące przegląda zawartość magazynu.
       Kiedy narzędzie wyszukujące wie, czego chce klient, rozpoczyna
       wykonywanie pętli, w której sprawdzane są wszystkie gitary znajdujące się
       w magazynie.
                                                                                                                    kie
                                                                                                   e właściwości, ta
                                                                                   Wszystkie ogóln i drewna, producent
     3 Każda gitara jest analizowana pod kątem zgodności                           jak uż yte gatunk                  e
                                                                                                     y, są porównywan
       z wymaganiami określonymi przez klienta.                                    czy też typ gitar reślonymi przez
                                                                                   z wymagania   mi ok
       Dla każdej gitary znajdującej się w magazynie narzędzie wyszukujące          klienta.
       sprawdza, czy spełnia ona wymagania określone przez klienta. Jeśli
       wymagania są spełnione, to gitara jest dodawana do listy pasujących
       instrumentów.

     4 Klientowi przedstawiana jest lista wszystkich instrumentów
       spełniających zadane kryteria.
       W końcu lista pasujących instrumentów jest wyświetlana, a Rysiek może ją
       przedstawić klientowi. Klient może wybrać odpowiadający mu instrument,
       a Rysiek — zainkasować zapłatę.




  Spróbuj słownie opisać rozwiązywany problem,
  aby upewnić się, że projekt rozwiązania odpowiada
  planowanym możliwościom funkcjonalnym aplikacji.


56     Rozdział 1.
Dobrze zaprojektowane aplikacje są super


Tajemnica
  obiektów                                       W lepiej zaprojektowanych dzielnicach Obiektowa obiekty bardzo
                                                 poważnie i precyzyjnie podchodzą do swoich zadań. Każdy z nich
  o niedopasowanych                              jest zainteresowany tylko i wyłącznie swoimi zadaniami i stara się je
                                                 wykonywać jak najlepiej. Nie ma niczego, co dobrze zaprojektowane

  typach                                         obiekty nie cierpiałyby bardziej niż wykonywanie zadań, do których tak
                                                 naprawdę nie zostały przeznaczone.
                                                 Niestety, jak udało się nam zauważyć, właśnie taka sytuacja występuje
                                                 w narzędziu służącym do przeszukiwania magazynu gitar Ryśka:
                                                 w pewnym jego miejscu pewien obiekt jest używany do wykonywania
                                                 operacji, których tak naprawdę nie powinien wykonywać. Twoim
                                                 zadaniem jest rozwiązanie tej zagadki i określenie, w jaki sposób można
                                                 poprawić aplikację Ryśka.
                                                 Aby ułatwić Ci zadanie, poniżej podaliśmy kilka przydatnych wskazówek,
                                                 które pomogą Ci rozpocząć poszukiwania niepasującego typu obiektów:

                                                 1. Zadania wykonywane przez obiekty powinny pasować do
                                                 nazwy tych obiektów.
                                                 Jeśli pewien obiekt należy do klasy Odrzutowiec, to prawdopodobnie
                                                 powinien on mieć metody ląduj() oraz startuj(), jednak nie powinien
                                                 udostępniać metody kontrolujBilety() — bo kontrola biletów jest
                                                 zadaniem należącym do jakiegoś innego obiektu.

                                                 2. Każdy obiekt powinien reprezentować jedno pojęcie.
                                                 Nie chcesz używać obiektów realizujących dwa lub trzy różne obowiązki.
                                                 Unikaj obiektów, które będą reprezentować „prawdziwą” kwaczącą
                                                 kaczkę, żółtą, plastykową kaczuszkę do kąpieli oraz osobę, która schyla
                                                 głowę, by uniknąć trafienia piłką na meczu w baseball.

                                                 3. Nieużywane właściwości obiektów są podejrzane.
   STOP! Spróbuj rozwiązać tę                    Jeśli okaże się, że właściwości w obiekcie bardzo często mają wartości
   zagadkę, zanim zaczniesz czytać               null lub w ogóle nie są używane, to może to oznaczać, że obiekt
   następną stronę.                              wykonuje więcej niż jedno zadanie. Skoro jakaś właściwość obiektu
                                                 bardzo rzadko ma jakieś wartości, to dlaczego stanowi ona część tego
                                                 obiektu? Czy nie lepiej byłoby ją umieścić w jakimś innym obiekcie,
                                                 zawierającym tylko podzbiór właściwości oryginalnego obiektu?


    Jak myślisz, jaki typ obiektu jest nieprawidłowo używany w aplikacji Ryśka? Zapisz odpowiedź poniżej.




    A jak myślisz, co należy zrobić, by rozwiązać ten problem? Jakie zmiany w aplikacji byś wprowadził?




                                                                                               jesteś tutaj           57
Powtarzający się kod to coś okropnego


                                        No wiecie… Klienci Ryśka tak
                                        naprawdę nie przekazują mu
                                     obiektów klasy Guitar… Chodzi mi
                                    o to, że w istocie nie dają mu gitar,
                                       które on następnie porównuje
                                       z instrumentami w magazynie.


                                Franek: Fakt — masz rację. Nie pomyślałem o tym wcześniej.
                                Julka: No i co z tego? Zastosowanie obiektu Guitar znacznie ułatwia wykonywanie
                                porównywania w metodzie search().
                                Jerzy: Nie bardziej niż zastosowanie jakiegokolwiek innego obiektu. Spójrzcie:

                                       if (searchGuitar.getBuilder() !=
                                                 guitar.getBuilder()) {                      To niewielki fragment
                                         continue;                                           metody search() klasy
                                       }                                                     Inventory.


                                Jerzy: Tak naprawdę nie ma znaczenia, jakiego obiektu tu używamy, o ile tylko
                                będziemy w stanie określić, na jakich cechach gitary zależy klientowi.
                                Franek: Tak… Myślę, że powinniśmy stworzyć nowy typ obiektów, który
                                przechowywałby jedynie takie specyfikacje, jakie klient chce przekazać do metody
                                search(). W takim przypadku do tej metody nie byłyby przekazywane obiekty Guitar,
                                co, swoją drogą, nigdy mi się nie wydawało szczególnie sensowne.
                                Julka: Jednak czy takie rozwiązanie nie spowoduje powielania kodu w aplikacji? Jeśli
Hermetyzacja                    stworzymy obiekt, w którym będzie można zapisać wszystkie specyfikacje podawane
                                przez klienta, a oprócz tego mamy obiekt Guitar ze wszystkimi jego właściwościami
pozwala podzielić               i metodami, to w efekcie uzyskamy dwie metody getBuilder(), dwie metody
                                getWood() i tak dalej… To chyba niezbyt dobrze…
aplikację na                    Franek: W takim razie dlaczego nie hermetyzować tych właściwości i nie przenieść
logiczne części.                z klasy Guitar do jakiejś innej, nowej?
                                Jerzy: O rany… Rozumiałem wszystko do chwili, gdy powiedziałeś „hermetyzować”.
                                Myślałem, że hermetyzacja polega na zdefiniowaniu zmiennych jako prywatne, tak by
                                nikt nie mógł ich używać w niewłaściwy sposób. Ale co to ma wspólnego
                  szy
     Po raz pierw z terminem    z właściwościami gitary?
     spotkałeś się ? Zajrzyj
     hermetyzacja przeczytaj    Franek: Hermetyzacja to także podział aplikacji na logiczne fragmenty oraz
     do dodatku B, acje         zachowanie ich separacji. A zatem, podobnie jak dane w klasie separujemy od działania
                    rm
      skrócone info i dopiero
      o  Obiektowie lektury     pozostałych fragmentów aplikacji, tak i same właściwości gitary możemy oddzielić od
      potem wróć do             samego obiektu Guitar.
                     u.
       tego rozdział
                                Julka: Czy w takim przypadku w klasie Guitar pozostałaby jedynie zmienna zawierająca
                                referencję do obiektu nowego typu, który gromadziłby wszystkie informacje o gitarze?
                                Franek: Dokładnie! W ten sposób udałoby się nam hermetyzować właściwości gitary
                                od obiektu Guitar i umieścić je w osobnym obiekcie. Spójrzcie, moglibyśmy zrobić coś
                                takiego…
58       Rozdział 1.
Dobrze zaprojektowane aplikacje są super


Zaostrz ołówek             Utworzenie obiektu GuitarSpec
       Poniżej przedstawiliśmy diagram klasy Guitar oraz nowej klasy o nazwie GuitarSpec, o której przed chwilą
       rozmawiali Franek, Julka i Jerzy. Twoim zadaniem jest dodanie do klasy GuitarSpec wszystkich właściwości
       i metod, które według Ciebie będą w niej niezbędne. Następnie wykreśl z klasy Guitar wszystkie właściwości
       i metody, które nie będą już w niej potrzebne. W końcu w diagramie klasy Guitar pozostawimy Ci nieco
       wolnego miejsca, na wypadek gdybyś doszedł do wniosku, że będziesz musiał dodać do niej jakieś nowe
       właściwości lub metody.



                Guitar                                                             GuitarSpec
   serialNumber: String
   price: double
   builder: Builder
   model: String
                                                Usuń z klasy
   type: Type                                   Guitar wszystko,
                                                co według Ciebie
   backWood: Wood                               powinno się
                                                znaleźć w klasie
   topWood: Wood                                GuitarSpec.




   getSerialNumber(): String
   getPrice(): double                            Do klasy Guitar
                                                 możesz dodać
   setPrice(float)                               nowe właściwości
                                                 i metody, jeśli
   getBuilder(): Builder                         uznasz to za
                                                 właściwe.
   getModel(): String
   getType(): Type
   getBackWood(): Wood
   getTopWood(): Wood




                                        * Jeśli będziesz mieć problemy, to pomyśl
                                          o wspólnych elementach, które występują
                                          w obiekcie Guitar i które są przekazywane
                                          w wywołaniu metody search().

                                                                                                    jesteś tutaj    59
Hermetyzacja tego, co może być różne


      Zaostrz ołówek            Utworzenie obiektu GuitarSpec
            Rozwiązanie         Poniżej przedstawiliśmy diagram klasy Guitar oraz nowej klasy o nazwie GuitarSpec, o której
                                przed chwilą rozmawiali Franek, Julka i Jerzy. Twoim zadaniem jest dodanie do klasy GuitarSpec
                                wszystkich właściwości i metod, które według Ciebie będą w niej niezbędne. Następnie wykreśl
                                z klasy Guitar wszystkie właściwości i metody, które nie będą już w niej potrzebne. W końcu
                                w diagramie klasy Guitar pozostawimy Ci nieco wolnego miejsca, na wypadek gdybyś doszedł
                                do wniosku, że będziesz musiał dodać do niej jakieś nowe właściwości lub metody.



  Te dwie
  właściwości wciąż
 są unikalne dla
                                Guitar                                      GuitarSpec
 każdej gitary,
 a zatem muszą        serialNumber: String                          builder: Builder
 pozostać bez
 zmian.
                      price: double                                 model: String
                      builder: Builder                              type: Type
To są
właściwości,          model: String                                 backWood: Wood
które klienci         type: Type                                                                                  Powtarzanie się
Ryśka                                                               topWood: Wood                                 kodu eliminujemy,
przekazują do         backWood: Wood                                                                              przenosząc
metody search();                                                                                                  wszystkie wspólne
a zatem możemy        topWood: Wood                                                                               właściwości
 je przenieść                                                                                                     i związane
 do klasy             spec: GuitarSpec                                                                            z nimi metody
 GuitarSpec.                                                                                                      do obiektu,
                                                                                                                   którego będziemy
                                                                                                                   używali zarówno
  Będzie nam
  także potrzebna
                      getSerialNumber(): String                     getBuilder(): Builder                          do określania
                      getPrice(): double                                                                           właściwości gitar,
  referencja                                                        getModel(): String                             jak i kryteriów
  do obiektu
  GuitarSpec          setPrice(float)                               getType(): Type                                wyszukiwania.
  opisującego
  daną gitarę.        getBuilder(): Builder                         getBackWood(): Wood
                      getModel(): String                            getTopWood(): Wood
                      getType(): Type
                      getBackWood(): Wood
                      getTopWood(): Wood
                      getSpec(): GuitarSpec


                       my dokładnie
 Z metodami postępujewościami —
 tak samo jak z właści
                      my wszelkie
 z klasy Guitar usuwa ą się w niej
 metod y, które powtarzaj
                                            A teraz uaktualnij swój kod
                         itarSpec.
 oraz w nowej klasie Gu
                                            Dysponując powyższym diagramem, powinieneś być w stanie dodać do
                                            aplikacji nową klasę GuitarSpec i zaktualizować kod klasy Guitar. Od razu
                                            wprowadź niezbędne modyfikacje także w klasie Inventory, tak by całą
                                            aplikację można było poprawnie skompilować.

 60     Rozdział 1.
Dobrze zaprojektowane aplikacje są super


                             Nie ma
                               niemądrych pytań                                               Zobaczmy, jak postępują prace nad
                                                                                              naszymi trzema krokami, które mają
P Rozumiem, dlaczego potrzebujemy obiektu, by przekazywać
 .:                                                                                           nam pozwolić na stworzenie doskonałej
                                                                                              aplikacji.
wymagania klienta do metody search()… ale dlaczego używamy go
w obiekcie Guitar do przechowywania informacji o właściwościach gitary?


O.:   Załóżmy, że zastosowalibyśmy obiekt GuitarSpec jedynie do
                                                                                          1. Upewnij się, że
przechowywania wymagań klienta i przekazywania ich do metody search(),                        oprogramowanie robi to,
natomiast klasa Guitar pozostałaby niezmieniona. W takim przypadku, gdyby
Rysiek zaczął handlować gitarami 12-strunowymi i chciał dodać właściwość                      czego oczekuje klient.
numStrings, to musiałbyś dodać taką właściwość — oraz towarzyszący jej kod
metody getNumStrings() — zarówno w klasie GuitarSpec, jak i Guitar.                       Zajmujemy się teraz
Rozumiesz zapewne, że to prowadziłoby do powtarzania się tego samego kodu.                krokiem 2. — czyli
                                                                                          pracujemy nad
Zamiast tego cały (potencjalnie) powtarzający się kod możemy umieścić w klasie            projektem aplikacji.
GuitarSpec, a do klasy Guitar dodać referencję do obiektu GuitarSpec.
W ten sposób unikniemy powtarzania się kodu.
                                                                                          2. Zastosuj proste
     Za każdym razem gdy zauważysz                                                           zasady projektowania
   powtarzający się kod, poszukaj okazji                                                     obiektowego, by
      do zastosowania hermetyzacji.                                                          poprawić elastyczność
P Wciąż nie do końca rozumiem, dlaczego takie rozwiązanie jest
 .:                                                                                          oprogramowania.
traktowane jako hermetyzacja. Czy możecie mi to jeszcze raz wyjaśnić?                                                                          z
                                                                                                           To właśnie w tym miejscu zaczynas      a
                                                                                                           szuk ać poważnych problemów, zwłaszcz
                                                                                                                    ch z takimi zagadnieniami jak
O.:   Ideą hermetyzacji jest zabezpieczenie informacji gromadzonych w jednym
                                                                                                           związany
                                                                                                           powtarzający się kod lub nieprawi
                                                                                                                                             dłowo
miejscu aplikacji przed jej pozostałymi fragmentami. W najprostszym przypadku                              zaprojektowane klasy.
pewną informację przechowywaną w klasie można chronić przed pozostałym
kodem aplikacji poprzez zdefiniowanie jej jako składowej prywatnej. Jednak                3. Staraj się, by projekt
czasami będziemy chcieli w taki sposób zabezpieczyć nie jedną, lecz całą grupę
właściwości — takich jak szczegółowe informacje o cechach gitary — a nawet                    oprogramowania zapewniał
zachowania, na przykład sposób, w jaki latają poszczególne gatunki kaczek.
Jeśli usuniemy zachowanie poza klasę, będziemy mogli je zmieniać bez                          łatwość jego utrzymania
konieczności jednoczesnej modyfikacji samej klasy. A zatem, gdybyś zmienił
sposób przechowywania właściwości, to nie musiałbyś wprowadzać                                i pozwalał na jego
jakichkolwiek modyfikacji w klasie Guitar, gdyż właściwości zostały z niej
usunięte i przeniesione do innej klasy.                                                       wielokrotne stosowanie.
Właśnie na tym polega potęga hermetyzacji: poprzez podzielenie aplikacji na
części uzyskujemy możliwość modyfikowania jednej z nich, bez konieczności                 Pamiętaj, że w tym kroku też będz
                                                                                          mieli sporo pracy związanej z proj iemy
wprowadzania zmian w innych. Ogólnie rzecz biorąc, powinieneś hermetyzować                aplikacji; a zatem, zanim ukończysektem
te części aplikacji, które mogą się zmieniać, oddzielając je od fragmentów aplikacji,     prace, Twój kod będzie naprawd z
                                                                                                                         ę łatw
które zmieniać się nie będą.                                                              rozbudowy i wielokrotnego zastosow y do
                                                                                                                              ania.

                                                                                                                   jesteś tutaj        61
Aktualizacja magazynu


Aktualizacja klasy Inventory
Teraz, kiedy już hermetyzowaliśmy specyfikację gitary, musimy
wprowadzić w naszym kodzie kilka następnych zmian.



                           Inventory
         guitar: List
         addGuitar(String, double, Builder, String, Type,
                   Wood, Wood)
         getGuitar(String): Guitar
         search(GuitarSpec): List



Teraz do
metody search()         public class Inventory {
przekazywany jest
obiekt GuitarSpec,
a nie Guitar.              // właściwości, konstruktor, inne metody

                          public List search(GuitarSpec searchSpec) {
                            List matchingGuitars = new LinkedList();
Obecnie wszystkie           for (Iterator i = guitars.iterator(); i.hasNext(); ) {
informacje, jakich            Guitar guitar = (Guitar)i.next();
używamy podczas
porównywania,                 GuitarSpec guitarSpec = guitar.getSpec();
pochodzą z obiektów           if (searchSpec.getBuilder() != guitarSpec.getBuilder())
GuitarSpec, a nie
 Guitar.                         continue;
                              String model = searchSpec.getModel().toLowerCase();
                              if ((model != null)  (!model.equals(“”)) 
                                   (!model.equals(guitarSpec.getModel().toLowerCase())))
                                 continue;
    Ten kod jest niemal
    identyczny jak            if (searchSpec.getType() != guitarSpec.getType())
    wcześniej, a jedyna          continue;
    różnica polega na
    tym, że porównujemy       if (searchSpec.getBackWood() != guitarSpec.getBackWood())
   informacje                    continue;
   przechowywane
   w obiekcie                 if (searchSpec.getTopWood() != guitarSpec.getTopWood())
   GuitarSpec.                   continue;
                              matchingGuitars.add(guitar);                               class
                                                                                         Inven-
                            }                                                            tory {
                            return matchingGuitars;                                      Search()}
                          }
                        }                                     y nieznaczne            Inventory.java
                                             oć wprowadziliśm
                                                 Ch               dzie klasy, to ta
                                                 modyfikacje w ko raca listę gitar
                                                 metoda wciąż zw ne kryteria.
                                                                  da
                                                 spełniających za

62     Rozdział 1.
Dobrze zaprojektowane aplikacje są super


Przygotuj się na kolejny test
Aby przetestować te wszystkie modyfikacje, będziesz musiał
wprowadzić kilka zmian w klasie FindGuitarTester:

              public class FindGuitarTester {

                   public static void main(String[] args) {
                     // Inicjalizacja zawartości magazynu gitar Ryśka
                     Inventory inventory = new Inventory();
                     initializeInventory(inventory);
Tym razem klient
przekazuje do
metody search()        GuitarSpec whatEveLikes =
obiekt klasy
GuitarSpec.              new GuitarSpec(Builder.FENDER, “Stratocastor”,
                                          Type.ELECTRIC, Wood.ALDER, Wood.ALDER);
                       List matchingGuitars = inventory.search(whatEveLikes);
                       if (!matchingGuitars.isEmpty()) {
                         System.out.println(“Ewo, może spodobają Ci się następujące gitary:”;
                         for (Iterator i = matchingGuitars.iterator(); i.hasNext(); ) {
                           Guitar guitar = (Guitar)i.next();
                           GuitarSpec spec = guitar.getSpec();                            Także tutaj
                           System.out.println(“ Mamy w magazynie gitarę “ +               używamy
                              spec.getBuilder() + “ model “ + spec.getModel() + “,        nowej klasy
                                                                                          GuitarSpec.
                              jest “ + “to gitara” + spec.getType() + “ :n       “ +
                              spec.getBackWood() + “ - tył i boki,n      “ +
                              spec.getTopWood() + “ - góra.n Możesz ją mieć za “ +
                              spec.getPrice() + “PLN!n ----”);
                         }
                       } else {
                         System.out.println(“Przykro mi, Ewo, nie znalazłem nic dla Ciebie.”);
                       }
                   }

                   private static void initializeInventory(Inventory inventory) {
                     // dodanie gitar do magazynu
                   }
              }                                                                            class
                                                                                           FindGui-
                                                                                           tar {
                                                                                             main()
                                                                                           }


                                                                                    FindGuitarTester.java




                                                                                            jesteś tutaj    63
Zastosowanie zasad projektowania obiektowego




                                                                ?
                                                                                                          

                                   dlaczEgo mam na To
                              
                                    zWracać uWagĘ ?
                                                                              

                 Dowiedziałeś się już całkiem dużo na temat pisania doskonałego oprogramowania, jednak
                 wciąż jeszcze musisz się wiele nauczyć. Weź głęboki oddech i zastanów się nad niektórymi
                 terminami i zasadami, które przedstawiliśmy w tym rozdziale. Połącz słowa umieszczone
                 w lewej kolumnie z wyjaśnieniami celów stosowania danych technik lub zasad, znajdującymi
                 się w kolumnie prawej.

                                                       Beze mnie Twój klient nigdy nie będzie zadowolony. Bez względu na to, jak wspaniale byłaby
     Elastyczność                                      zaprojektowana i napisana aplikacja, to właśnie ja sprawiam, że na twarzy klienta pojawia się
                                                       uśmiech.
                                                       Ja wkraczam do akcji tam, gdzie chodzi o możliwości wielokrotnego wykorzystania tego samego kodu
     Hermetyzacja                                      i uzyskanie pewności, że nie trzeba będzie rozwiązywać problemów, które ktoś inny rozwiązał już
                                                       wcześniej.
                                                       Programiści używają mnie po to, by oddzielić od siebie fragmenty kodu, które ulegają zmianom,
     Funkcjonalność                                    od tych, które pozostają takie same; dzięki temu łatwo można wprowadzać w kodzie modyfikacje
                                                       — bez obawy, że cała aplikacja przestanie działać.
                                                       Programiści używają mnie po to, by oprogramowanie można było rozwijać i zmieniać bez konieczności
     Wzorce projektowe                                 ciągłego pisania go od samego początku. To ja sprawiam, że aplikacje stają się solidne i odporne.
                                                                                                                 Odpowiedzi znajdziesz na stronie 81

                                                      Nie ma
                                                        niemądrych pytań
P Hermetyzacja nie jest jedyną zasadą projektowania
 .:                                                                        P Ale nie do końca rozumiem, w jaki sposób ta cała
                                                                            .:
obiektowego, jakiej używamy na tym etapie prac nad                         hermetyzacja poprawia elastyczność mojego kodu. Możecie
aplikacją, prawda?                                                         to jeszcze raz wyjaśnić?

O.: Nie. Kolejnymi z zasad, o których warto pamiętać, są                   O.: Kiedy już poprawiłeś swój kod i zapewniłeś, że działa on
dziedziczenie i polimorfizm. Jednak także i one łączą się                  tak, jak sobie tego życzył klient, największym problemem staje się
z powtarzalnością kodu oraz hermetyzacją; dlatego rozpoczynanie            elastyczność. Co się stanie, jeśli klient poprosi o dodanie do aplikacji
pracy od poszukiwania miejsc, w jakich można zastosować                    kilku nowych właściwości lub możliwości funkcjonalnych? Jeśli
hermetyzację, zawsze będzie dobrym rozwiązaniem.                           w aplikacji będzie wiele powtarzających się fragmentów kodu bądź
W dalszej części książki przedstawimy znacznie więcej informacji           jeśli jej struktura dziedziczenia będzie skomplikowana i niejasna,
o zasadach projektowania obiektowego (a w rozdziale 8.                     to wprowadzanie modyfikacji w takim programie może stać się
zaprezentujemy nawet kilka przykładów); zatem nie przejmuj się,            prawdziwym koszmarem.
jeśli teraz jeszcze nie wszystko dokładnie rozumiesz. Zanim skończysz      Jednak dzięki zastosowaniu takich zasad jak hermetyzacja oraz
lekturę tej książki, dowiesz się znacznie więcej o hermetyzacji,           poprawne projektowanie klas wprowadzanie zmian może być
projektowaniu klas oraz o wielu innych zagadnieniach.                      znacznie łatwiejsze, a sama aplikacja stanie się bardziej elastyczna.

64      Rozdział 1.
Dobrze zaprojektowane aplikacje są super


 Wróćmy do aplikacji Ryśka…
 Sprawdźmy teraz, czy wszystkie wprowadzone zmiany nie wpłynęły
 negatywnie na poprawność działania aplikacji Ryśka. Skompiluj
 wszystkie klasy i jeszcze raz uruchom program FindGuitarTester:




Tym razem wyniki      od
niczym się nie różnią o,
uzyskiwanych poprzedni
jednak sama aplikacja ana
jest lepiej zaprojektow
 i znacznie bardziej
 elastyczna.




                                                                   WYTĘŻ
                                                                   umYsł
                                                       Czy jesteś w stanie podać trzy konkretne czynniki, które sprawiają, że
                                                       w dobrze zaprojektowanym oprogramowaniu wprowadzanie zmian
                                                       jest łatwiejsze niż w oprogramowaniu, w którym fragmenty kodu
                                                       powielają się?




                                                                                                   jesteś tutaj           65
Czas na poważne projektowanie


Projekt po raz pierwszy, projekt po raz drugi
Skoro już raz przeanalizowałeś aplikację i zastosowałeś w niej podstawowe
techniki projektowania obiektowego, czas przyjrzeć się jej ponownie
i upewnić, że jest ona nie tylko elastyczna, lecz także zapewnia łatwość
wielokrotnego stosowania fragmentów kodu oraz ich rozszerzania.




1. Upewnij się, że
   oprogramowanie robi to,
   czego oczekuje klient.                                                           Nadszedł
                                                                          czas, by poważnie
                                                                   rozważyć możliwości wielokrotnego
                                                                wykorzystania kodu aplikacji oraz łatwość
                                                                   wprowadzania w nim zmian. To właśnie
                                                                     teraz możesz zmienić poprawnie
                                                                  zaprojektowane klasy na rozszerzalne,
                                                                       nadające się do wielokrotnego
2. Zastosuj proste zasady                                                  stosowania komponenty.

   projektowania obiektowego,
   by poprawić elastyczność
   oprogramowania.
                                          Skoro już za
                                          zasady proj stosowałeś podstaw
                                          nadszedł cz ektowania obiektowegowe
                                         wzorce proj as, by wykorzystać o,
                                         się na moż ektowe i skoncentrow
                                         stosowania liwościach wielokrotneać
                                                     kodu.                go



3. Staraj się, by projekt
   oprogramowania
   zapewniał łatwość jego
   utrzymania i pozwalał
   na jego wielokrotne
   stosowanie.
66     Rozdział 1.
Dobrze zaprojektowane aplikacje są super


                                    (naprawdę)
 Sprawdźmy, czy klasa Inventory.java jest dobrze zaprojektowana
 W poprzedniej części rozdziału zastosowaliśmy hermetyzację, by poprawić projekt             Teraz, kiedy już oddałe
 narzędzia do wyszukiwania gitar w magazynie Ryśka, niemniej jednak w naszym                 sprawne narzędzie wy ś Ryśkowi
                                                                                                                   szu
 kodzie wciąż można znaleźć miejsca, które należałoby poprawić w celu pozbycia się          w magazynie gitary pas kujące
                                                                                            wymagań określanych ujące do
 potencjalnych problemów. Dzięki temu nasz kod będzie można łatwiej rozszerzać,             masz pewność, że Rysprzez klientów,
                                                                                                                   iek
                                                                                            do Ciebie zadzwoni, jeś ponownie
 kiedy Rysiek wymyśli kolejne możliwości, które chciałby dodać do aplikacji, oraz
                                                                                            zmienić w swojej aplikali zechce coś
 łatwiej wykorzystać, jeśli zechcemy zastosować jego fragmenty w innej aplikacji.                                    cji.




                     public List search(GuitarSpec searchSpec) {
                         List matchingGuitars = new LinkedList();
                         for (Iterator i = guitars.iterator(); i.hasNext(); ) {
                            Guitar guitar = (Guitar)i.next();
                            GuitarSpec guitarSpec = guitar.getSpec();
Oto kod metody              if (searchSpec.getBuilder() != guitarSpec.getBuilder())
search() klasy                continue;
Inventory.                  String model = searchSpec.getModel().toLowerCase();
Przyjrzyj mu się            if ((model != null)  (!model.equals(””)) 
dokładniej.
                                (!model.equals(guitarSpec.getModel().toLowerCase())))
                              continue;
                            if (searchSpec.getType() != guitarSpec.getType())
                              continue;
                            if (searchSpec.getBackWood() != guitarSpec.getBackWood())
                              continue;
                            if (searchSpec.getTopWood() != guitarSpec.getTopWood())
                              continue;
                            matchingGuitars.add(guitar);
                         }
                         return matchingGuitars;                                                        class
                       }                                                                                Inven-
                                                                                                        tory {

                                                                                                        Search()}


                                                                                                     Inventory.java


Zaostrz ołówek

                                                                                                                            Zobacz, co napisaliśmy na stronie 82
          Co zmieniłbyś w tym fragmencie kodu?
          W przedstawionym kodzie występuje poważny problem, Twoim zadaniem jest go odnaleźć. W poniższych pustych
          wierszach zapisz, na czym według Ciebie polega ten problem oraz w jaki sposób można by go rozwiązać.




                                                                                                   jesteś tutaj       67
Czy proste zmiany naprawdę są proste?



                          No wiesz… Zawsze uwielbiałem
                         grać na 12-strunowych gitarach.
                        Jak trudne byłoby zmodyfikowanie
                        aplikacji w taki sposób, bym mógł
                        sprzedawać takie gitary i aby moi
                           klienci mogli je wyszukiwać?




                                Jak łatwo będzie wprowadzić
                                taką zmianę do aplikacji Ryśka?

                                Przeanalizuj diagram klas tworzących aplikację
                                Ryśka i zastanów się, co należałoby zrobić, aby
                                wyposażyć ją w możliwość obsługi gitar
                                12-strunowych. Jakie właściwości i metody musiałbyś
                                dodać, w jakich klasach należałoby je umieścić?
                                Jakie zmiany musiałbyś wprowadzić w kodzie
                                aplikacji, by zapewnić klientom Ryśka możliwość
                                wyszukiwania 12-strunowych gitar?

                                Ile klas musiałeś zmienić, by wprowadzić powyższą
                                modyfikację? Czy dalej uważasz, że aplikacja Ryśka
                                jest dobrze zaprojektowana?




                                                                            Guitar
                                                               serialNumber: String
                                                               price: double
                                                               spec: GuitarSpec
                                                               getSerialNumber(): String
                                                               getPrice(): double
                                                               setPrice(float)
                                                               getSpec(): GuitarSpec



68   Rozdział 1.
Dobrze zaprojektowane aplikacje są super



                                     Przypiski do diagramu klas aplikacji Ryśka                Zaostrz ołówek

      GuitarSpec                     Rysiek chciałby sprzedawać 12-strunowe gitary.
                                     Przygotuj zatem ołówek i umieść na diagramie klas
                                     notatki zawierające następujące informacje:
builder: Builder
                                     1. Do jakiej klasy dodałbyś nową właściwość o nazwie numStrings, która będzie
model: String                           służyć do przechowywania informacji o liczbie strun w danej gitarze?
type: Type                           2. Gdzie dodałabyś nową metodę o nazwie getNumStrings(), która zwraca
                                        liczbę strun w gitarze?
backWood: Wood
                                     3. W jakich innych miejscach aplikacji powinieneś wprowadzić zmiany w kodzie,
topWood: Wood                           tak by podczas wyszukiwania gitar klienci Ryśka mogli określać, że interesują ich
                                        gitary 12-strunowe?
getBuilder(): Builder
                                     W końcu, w umieszczonych poniżej pustych wierszach zapisz wszelkie problemy
getModel(): String                   związane z projektem aplikacji, które odnalazłeś podczas dodawania obsługi
                                     12-strunowych gitar.
getType(): Type
getBackWood(): Wood
getTopWood(): Wood

                                                                                           Oto podpowiedź: zapisana
                                                                                           tutaj odpowiedź powinna być
                        Inventory                                                          związana z informacjami,
                                                                                           które zapisałeś w pustych
                                                                                           wierszach na stronie 67.
   guitars: List
   addGuitar(String, double, Builder, String, Type,
             Wood, Wood)
   getGuitar(String): Guitar
   search(GuitarSpec): List



              Builder                                                                WYTĘŻ
toString(): String         Type                                                      umYsł
                                                                              Na czym polega przewaga utworzenia
         toString(): String        Wood                                       nowej właściwości o nazwie numStrings
                                                                              nad dodaniem właściwości logicznej
                     toString(): String                                       określającej, czy dana gitara ma 12 strun?


                                                                                                jesteś tutaj              69
Mamy problem z hermetyzacją


     Zaostrz ołówek              Przypiski do diagramu klas aplikacji Ryśka
          Rozwiązanie            Rysiek chciałby sprzedawać 12-strunowe gitary. Przygotuj zatem ołówek i umieść na diagramie klas
                                 notatki zawierające następujące informacje:

       1. Do jakiej klasy dodałbyś nową właściwość o nazwie numStrings, która będzie służyć do przechowywania informacji
          o liczbie strun w danej gitarze?
       2. Gdzie dodałabyś nową metodę o nazwie getNumStrings(), która zwraca liczbę strun w gitarze?
       3. W jakich innych miejscach aplikacji powinieneś wprowadzić zmiany w kodzie, tak by podczas wyszukiwania gitar klienci
          Ryśka mogli określać, że interesują ich gitary 12-strunowe?
       W końcu, w umieszczonych poniżej pustych wierszach zapisz wszelkie problemy związane z projektem aplikacji, które
       odnalazłeś podczas dodawania obsługi 12-strunowych gitar.
                                                                                                            Musimy zmienić
                                                                                                            konstruktor tej
       Dodajemy właściwość do klasy GuitarSpec,                                                             klasy, gdyż
                                                                                                            w jego wywołaniu
       lecz jednocześnie musimy zmienić kod metody                                                          są przekazywane
                                                                                                            wszystkie informacje,
       search() w klasie Inventory oraz konstruktor                                  Guitar                  które następnie
                                                                                                             zapisujemy w obiekcie
       klasy Guitar.                                                     serialNumber: String                GuitarSpec.
                                                                         price: double
                                                                         spec: GuitarSpec                    Metoda addGuitar()
                         Oto co my wymyśliliśmy.
                                                                                                             zdefiniowana w tej klasie
                         Czy Ty zapisałeś coś                            getSerialNumber(): String           także operuje na wszystkich
                         podobnego?
                                                                         getPrice(): double                  właściwościach gitary.
                                                                                                             Dodanie nowej właściwości
                                                                         setPrice(float)                     oznacza zatem konieczność
        Do klasy GuitarSpec                                              getSpec(): GuitarSpec               wprowadzenia modyfikacji
        musimy dodać                                                                                         w tej metodzie — a to jest
        właściwość numStrings.                                                                               problem.


                                  GuitarSpec                                                             Inventory
                            builder: Builder                                        guitar: List
                            model: String
                                                                                    addGuitar(String, double, Builder, String, Type,
                            type: Type
                                                                                              Wood, Wood)
                            backWood: Wood
                                                                                    getGuitar(String): Guitar
                            topWood: Wood
                                                                                    search(GuitarSpec): List
                            getBuilder(): Builder
                            getModel(): String
Do tej klasy musimy         getType(): Type                                                                           Kolejny problem:
także dodać metodę          getBackWood(): Wood                                                                       musimy zmienić
getNumStrings(), która                                                  Builder                                       metodę search()
będzie zwracać liczbę       getTopWood(): Wood                                                                        klasy Inventory,
strun, jaką posiada                                            toString(): String    Type                             aby uwzględnić
dana gitara.                                                                                                          nowe właściwości
                                                                        toString(): String Wood                       dodane do klasy
                                                                                                                      GuitarSpec.
                                                                                    toString(): String


70     Rozdział 1.
Dobrze zaprojektowane aplikacje są super


                                    Czyli problem polega właśnie na
                                  tym? Dodawanie nowych właściwości
                                    do klasy GuitarSpec nie powinno
                                     zmuszać nas do wprowadzania
                                    modyfikacji w klasach Guitar oraz
                                   Inventory. Czy także ten problem
                                   możemy rozwiązać, wykorzystując
                                             hermetyzację?



                       Owszem — musimy hermetyzować
                       specyfikacje gitary i lepiej izolować je od
                       pozostałych fragmentów aplikacji Ryśka.

                       Choć nowe właściwości dodajemy jedynie do klasy
                       GuitarSpec, to jednak przy tej okazji musimy
                       zmodyfikować dwie inne klasy: Guitar oraz Inventory.
                       W konstruktorze klasy Guitar należy przekazywać
                       dodatkowy argument, a w metodzie search() klasy
                       Inventory trzeba dodać jedno porównanie.                                                                m
                                                                                                Zastosowanie tego kodu w inny
                                                                                                programie nie będzie prostym
                                                                                                                                acji
                   Ten konstruktor tworzy obiekt                                                zadaniem. Wszystkie klasy aplik
                   GuitarSpec, zatem każda                                                      Ryśk a są od siebie wzajemnie
                                                                                                                                  ać
                   zmiana specyfikacji gitary                                                   uzależnione i nie można zastosow
                   będzie powodować konieczność                                                 jednej z nich bez jednoczesnego
                   zmiany samego konstruktora.                                                  wykorzystania wszystkich
                                                                                                pozostałych.



public Guitar (String serialNumber,                    public List search (GuitarSpec searchSpec) {
                 double price,                             List matchingGuitars = new LinkedList();
                 Builder builder,                          for (Iterator i = guitars.iterator(); i.hasNext(); ) {
                 String model, Type type,                     Guitar guitar = (Guitar)i.next();
                 Wood backWood, Wood topWood ) {              GuitarSpec guitarSpec = guitar.getSpec();
    this.serialNumber = serialNumber;                         if (searchSpec.getBuilder() != guitarSpec.getBuilder())
    this.price = price;                                         continue;
    this.spec = new GuitarSpec (builder, model,               String model = searchSpec.getModel().toLowerCase();
                       type, backWood, topWood);              if ((model != null)  (!model.equals(„”)) 
                                                                  (!model.equals(guitarSpec.getModel().toLowerCase())))
                                           class                continue;
                                           Guitar             if (searchSpec.getType() != guitarSpec.getType())
                                              Gui-
                                           tar()                continue;
                                           }                  if (searchSpec.getBackWood() != guitarSpec.getBackWood())
                                                                continue;
                                         Guitar.java          if (searchSpec.getTopWood() != guitarSpec.getTopWood())
                                                                continue;
                                                              matchingGuitars.add(guitar);
                                                           }                                                  class
                                                           return matchingGuitars;                            Inven-
                                                                                                              tory {
                                                         }
                                                                                                              Search()}


                                                                                                          Inventory.java

                                                                                                      jesteś tutaj        71
Staraj się, by fragmenty aplikacji można było wielokrotnie używać




                     Zagadka projektowa
     Sama wiedza, że w aplikacji Ryśka coś szwankuje, to zdecydowanie za mało. Nie wystarczy także świadomość,
     że konieczne jest ponowne zastosowanie hermetyzacji. Teraz musisz wymyślić, jak należy poprawić tę aplikację,
     by łatwiej można było wielokrotnie stosować jej fragmenty oraz by jej rozszerzanie nie przysparzało tylu
     problemów.

           Problem:
                Dodanie nowej właściwości do klasy GuitarSpec zmusza nas do wprowadzenia zmian także
                w kodzie klas Guitar oraz Inventory. Strukturę aplikacji należy zmienić w taki sposób,
                by dodawanie nowych właściwości do klasy GuitarSpec nie powodowało konieczności
                modyfikacji innych klas.

           Twoje zadanie:
            1   Dodaj właściwość numStrings oraz metodę getNumStrings() do klasy GuitarSpec.

            2   Zmodyfikuj kod klasy Guitar w taki sposób, by właściwości obiektu GuitarSpec zostały        Nie wiesz, co —
                                                                                                            w tym kontekście
                usunięte z konstruktora tej klasy.                                                          — oznacza
                                                                                                            „delegowanie”
            3   Zmień metodę search() w klasie Inventory w taki sposób, by porównywanie dwóch               — sprawdź…
                obiektów GuitarSpec zostało delegowane do klasy GuitarSpec, a nie było realizowane
                bezpośrednio w tej metodzie.

           4    Zmodyfikuj kod klasy FindGuitarTester, by działała ona poprawnie ze zmienionymi
                klasami Guitar, GuitarSpec i Inventory, a następnie upewnij się, że wyszukiwanie gitar
                działa poprawnie.

            5   Porównaj odpowiedzi, które podałeś, z naszymi odpowiedziami zamieszczonymi na stronie
                74; następnie przygotuj się na kolejny test, który pozwoli Ci przekonać się, czy wreszcie
                zakończyłeś prace nad aplikacją Ryśka.

 Jedyna zmiana
 wprowadzić w , jaką będziesz musiał
kodu tworzące tym miejscu, dotyczy
magazynu i pogo testową zawartość
nowego konstr lega na zastosowaniu
             uktora klasy
                          Guitar.




72     Rozdział 1.
Dobrze zaprojektowane aplikacje są super


                                                    Nie ma
                                                      niemądrych pytań
P Napisaliście, że powinienem „delegować”
 .:                                                                 P A niby w jaki sposób to delegowanie ma ułatwiać
                                                                     .:
porównywanie do klasy GuitarSpec. Co to jest                        możliwości wielokrotnego wykonywania kodu?
delegowanie?

                                                                    O.:    Dzięki delegowaniu każdy obiekt może sam zajmować
O.: O delegowaniu mówimy w sytuacji, gdy obiekt, który musi         się porównywaniem z innymi obiektami (lub wykonywaniem
wykonać pewną czynność, zamiast wykonać ją samemu, prosi o jej      jakiejkolwiek innej operacji). To z kolei oznacza, że obiekty mogą
wykonanie (w całości lub częściowo) inny obiekt.                    być bardziej niezależne albo luźniej powiązane. Takie luźniej
A zatem, w swojej zagadce projektowej nie chcesz, by metoda         powiązane obiekty łatwiej jest zastosować w innej aplikacji, gdyż
search() klasy Inventory porównywała dwie specyfikacje              nie są one ściśle uzależnione od kodu innych obiektów.
GuitarSpec bezpośrednio w swoim kodzie. Zamiast tego chcesz,

                                                                    P Jeszcze raz — co oznacza „luźne powiązanie”?
                                                                     .:
by poprosiła obiekt GuitarSpec o określenie, czy specyfikacje
te odpowiadają sobie. A zatem metoda search() deleguje
porównanie do obiektu GuitarSpec.

                                                                    O.:    Luźne powiązanie oznacza, że każdy z obiektów używanych
P A dlaczego używamy takiego rozwiązania?
 .:                                                                 w aplikacji wykonuje tylko i wyłączenie swoje zadania. A zatem
                                                                    cała funkcjonalność aplikacji jest rozdzielona i zaimplementowana

O.: Delegowanie ułatwia wielokrotne wykorzystywanie kodu            w dobrze zdefiniowanych obiektach, z których każdy w doskonały
                                                                    sposób realizuje przypisane mu zadania.
aplikacji. Oprócz tego pozwala ono, by każdy obiekt koncentrował
się wyłącznie na swoich możliwościach funkcjonalnych i chroni
przed umieszczaniem zachowań związanych z jednym obiektem           P.:   A dlaczego to jest dobre rozwiązanie?
w różnych miejscach kodu aplikacji.
Jednym z najczęściej spotykanych przykładów delegowania
w języku Java jest metoda equals(). Metoda ta nie stara             O.:   Aplikacje tworzone przez luźno powiązane obiekty zazwyczaj
się samodzielnie sprawdzać, czy dwa obiekty są sobie równe,         są bardziej elastyczne i łatwiej można wprowadzać w nich
lecz zamiast tego wywołuje metodę equals() jednego                  modyfikacje. Ponieważ poszczególne obiekty nie są zwykle w żaden
z porównywanych obiektów i przekazuje w jej wywołaniu drugi         sposób zależne od innych obiektów, zatem możemy zmienić
obiekt. Następnie pobiera jedynie wartość true lub false zwróconą   zachowanie jednego z obiektów bez konieczności wprowadzania
przez wywołanie metody equals().                                    modyfikacji w innych. Dzięki temu dodawanie nowych właściwości
                                                                    lub możliwości funkcjonalnych staje się znacznie prostsze.




  Kącik naukowy
                 delegowanie — proces polegający na tym, iż jeden
                 obiekt zleca wykonanie pewnej operacji innemu, który
                     wykonuje ją w imieniu pierwszego obiektu.




                                                                                                            jesteś tutaj            73
Jeszcze więcej hermetyzacji




                       Zagadka projektowa — Rozwiązanie
     Sama wiedza, że w aplikacji Ryśka coś szwankuje, to zdecydowanie za mało. Nie wystarczy także
     świadomość, że konieczne jest ponowne zastosowanie hermetyzacji. Teraz musisz wymyślić, jak należy
     poprawić tę aplikację, by łatwiej można było wielokrotnie stosować jej fragmenty oraz by jej rozszerzanie
     nie przysparzało tylu problemów.

            Problem:
                 Dodanie nowej właściwości do klasy GuitarSpec zmusza nas do wprowadzenia zmian także
                 w kodzie klas Guitar oraz Inventory. Strukturę aplikacji należy zmienić w taki sposób, by
                 dodawanie nowych właściwości do klasy GuitarSpec nie powodowało konieczności modyfikacji
                 innych klas.

            Twoje zadanie:

             1   Dodaj właściwość numStrings oraz metodę getNumStrings() do klasy GuitarSpec.

                   public class GuitarSpec {
                                                                               Nie zapomnij zmodyfiko
                                                                               konstruktora klasy     wać
                   // inne właściwości
                                                                               GuitarSpec.
                       private int numStrings;

 To było
 naprawdę              public GuitarSpec(Builder builder, String model, Type type,
 łatwe…                                        int numStrings, Wood backWood, Wood topWood) {
                           this.builder = builder;
                           this.model = model;
                           this.type = type;
                           this.numStrings = numStrings;
                           this.backWood = backWood;
                           this.topWood = topWood;
                       }


                       // inne metody


                       public int getNumStrings() {
                           return numStrings;
                       }
                   }                                                                            class
                                                                                                Guitar
                                                                                                Spec {
                                                                                                  get-
                                                                                                Num-
                                                                                                Strings()

                                                                                          GuitarSpec.java

74    Rozdział 1.
Dobrze zaprojektowane aplikacje są super


       2   Zmodyfikuj kod klasy Guitar w taki sposób, by właściwości obiektu GuitarSpec
           zostały usunięte z konstruktora tej klasy.


       public Guitar(String serialNumber, double price, GuitarSpec spec) {
           this.serialNumber = serialNumber;
           this.price = price;
           this.spec = spec;
       }                                                          class
                                                                      Guitar {
                                                                         Gui-
                                                      arSpec          tar()
                      Teraz nie tworzymy obiektu Guit                 }
                      w konstruktorze klasy Guitar, lecz ołania
                                                      wyw
                      przekazujemy go jako argument                 Guitar.java
                      konstruktora.



       3   Zmień metodę search() w klasie Inventory w taki sposób, by porównywanie
           dwóch obiektów GuitarSpec zostało delegowane do klasy GuitarSpec, a nie było
           realizowane bezpośrednio w tej metodzie.

                                                                                             Metoda search()
                                                      {
                                itarSpec searchSpec)                                         stała się znacznie
           public List search(Gu
                                   = new LinkedList();                                       prostsza.
             List matchingGuitars                       asNext(); ) {
                                   tars.iterator(); i.h
             for (Iterator i = gui
                                    itar)i.next();
                Guitar guitar = (Gu
                                            s(searchSpec))
                if (guitar.getSpec().matche                                       class
                  matchingGuitars.add(guitar);                                    Inven-
                                                                                  tory {
                }
                                       ;
                return matchingGuitars                                            search()
            }
                                                                            Inventory.java


     Znaczna część               public boolean matches(GuitarSpec otherSpec) {
     kodu metody                   if (builder != otherSpec.builder)
     search() została                return false;
     z niej usunięta               if ((model != null)  (!model.equals(””)) 
     i przeniesiona                     (!model.toLowerCase().equals(otherSpec.model.toLowerCase())))
     do metody                       return false;
     matches() klasy               if (type != otherSpec.type)
     GuitarSpec.                     return false;
                                   if (numStrings != otherSpec.numStrings)
                                     return false;
                                   if (backWood != otherSpec.backWood)
                                     return false;
                                   if (topWood != otherSpec.topWood)
                                     return false;
                                   return true;
                                                                                                      class
Teraz dodawanie nowych           }                                                                    Guitar
właściwości do klasy                                                                                       Spec {
GuitarSpec wymaga jedynie                                                                                    get-
wprowadzania zmian w kodzie                                                                                Num-
                                                                                                           Strings()
tej kasy — klasy Guitar oraz
Inventory nie muszą być                                                                                GuitarSpec.java
modyfikowane.

                                                                                                            jesteś tutaj    75
Test aplikacji


Ostatni test aplikacji
(przygotowanej do wielokrotnego używania kodu)
O rany… naprawdę wykonaliśmy kawał dobrej roboty od momentu, gdy Rysiek
pokazał nam pierwszą wersję swojej aplikacji. Upewnijmy się, czy jej ostatnia wersja
wciąż poprawnie działa — zarówno z punktu widzenia Ryśka, jak i jego klientów —
i czy spełnia nasze wymagania dotyczące poprawnego projektu, prostego utrzymania
oraz kodu, którego z łatwością będzie można wielokrotnie używać.




  Oto co powinieneś
  zobaczyć po wykonaniu
  nowej wersji programu
  FindGuitarTester.




                                                                                       Ewa może obejrzeć kilka wyszuka
                                                                                       dla niej gitar, a Rysiek — znów nych
                                                                                       sprzedawać instrumenty swojej
                                                                                       wyszukanej klienteli.




                                                  Gratulujemy!
                                                  Udało Ci się zmodyfikować
                                                  niedziałającą aplikację przeszukującą
                                                  magazyn Ryśka i zmienić ją
                                                  w poprawnie zaprojektowane,
                                                  doskonałe oprogramowanie.

76     Rozdział 1.
Dobrze zaprojektowane aplikacje są super


Oto co zrobiliśmy
Przyjrzyjmy się pokrótce, w jaki sposób udało nam się sprawić, że
obecnie aplikacja przeszukująca magazyn Ryśka działa tak dobrze:
                                                                                 Czy pamiętasz nasze
                                                                                Wykonaliśmy je kolejn trzy kroki?
                                                                                                       o,
                                                                                przekształcić niedziała by
                                                                                                       jąc
                                                                                przeszukujące magazyn e narzędzie
                                                                                we w pełni funkcjona Ryśka
                                                                                                     lną
                                                                                zaprojektowaną aplika , dobrze
                                                                                                      cję.
     Rozpoczęliśmy od
     poprawienia niektórych
     problemów związanych               1. Upewnij się, że
     z działaniem aplikacji.
                                           oprogramowanie robi to,
 Następnie dodaliśmy do niej
 kilka nowych możliwości
                                           czego oczekuje klient.
 funkcjonalnych, dzięki czemu
 wyszukiwanie zwracało nie
 jedną, lecz całą listę gitar.



                   żliwości
Rozbudowując mo acji,
fu nkcjonalne aplik
                  , że podejmowan
upewniliśmy się z jej strukturą
                                 e      2. Zastosuj proste zasady
decyzje  związane
 są właściwe i do
                    bre.
                                           projektowania obiektowego,
                                           by poprawić elastyczność
                                           oprogramowania.
 Oprócz tego hermetyz
                       owaliśmy
 właściwości gitary i
                      zapewniliśmy,
 że dodawanie nowych
                       nie
 stanowić większego pro będzie
                        blemu.



                                        3. Staraj się, by projekt
                                           oprogramowania zapewniał
                                           łatwość jego utrzymania
   Udało się nam nawet ,
                                           i pozwalał na jego
                          nie
   zastosować delegowa          wane
   dzi ęki czemu obiekty uży j
   w apl  ikacji stały się luźnie iło
                            popraw
   powiązane; to z kolei o
   możliwości wielokrotneg
                           cji.
                                           wielokrotne stosowanie.
    używania kodu aplika



                                                                                           jesteś tutaj      77
Analiza i projektowanie obiektowe pomagają w tworzeniu doskonałego oprogramowania


Czy pamiętasz tego biednego gościa?


                                                           On chciał jedynie pisać wspaniałe
                                                           oprogramowanie. Jaka jest zatem
                                                           odpowiedź? W jaki sposób można
                                                           konsekwentnie pisać wspaniałe
                                                           oprogramowanie?

                                                           Potrzebujesz w tym celu sekwencji czynności,
                                                           które pozwolą Ci upewnić się, że Twoje
                                                           oprogramowanie działa i jest dobrze
                                                           zaprojektowane. Nie musi ona być ani długa, ani
                                                           skomplikowana — wystarczy prosta, trójetapowa
                                                           sekwencja, której użyliśmy do poprawienia
                                                           aplikacji Ryśka i której z powodzeniem będziesz
                                                           mógł używać we wszystkich swoich projektach.




                                                           Analiza i projektowanie obiektowe
                                                           pomoże Ci pisać wspaniałe programy,
                                                           i to za każdym razem.
                                     

                                                           Za każdym razem gdy w tym rozdziale
                                                           wspominaliśmy o trzech krokach pozwalających
                                                           na pisanie wspaniałego oprogramowania, tak
                                     Wyrażenie to
                                     będziemy określali    naprawdę mieliśmy na myśli OOAD — analizę
                                     skrótowo jako         i projektowanie obiektowe.
                                     OOAD, od
                                     angielskich słów
                                     Object-Oriented       W istocie analiza i projektowanie obiektowe jest
                                      Analysis  Design.   sposobem pisania oprogramowania. Jej celem jest
                                                           to, by oprogramowanie robiło, co do niego należy,
                                                           i było dobrze zaprojektowane. To z kolei oznacza,
                                                           że kod jest elastyczny, łatwo można wprowadzać
                                                           w nim zmiany, jest prosty w utrzymaniu i nadaje
                                                           się do wielokrotnego używania.




78   Rozdział 1.
Dobrze zaprojektowane aplikacje są super


     OOAD ma na celu tworzenie wspaniałego oprogramowania,
     a nie dodanie Ci papierkowej roboty!
                     Klienci są zadowoleni, kiedy ich aplikacje DZIAŁAJĄ.
                     Możemy uzyskać od klienta wymagania, które pozwolą nam upewnić się, że tworzona
  O wymaganiach
  napiszemy
                     aplikacja będzie robić to, o co klient prosił. Z powodzeniem można do tego celu zastosować
  w rozdziale 2.     tak zwane przypadki użycia oraz diagramy, niemniej jednak wszystko sprowadza się do tego,
                     by dowiedzieć się, jakich możliwości klient oczekuje od aplikacji.

                     Klienci są zadowoleni, kiedy ich aplikacje będą DZIAŁAĆ DŁUGO.
                     Nikt nie przepada za sytuacją, gdy aplikacja, która do tej pory działała dobrze, nagle
  Już nieco się      zacznie szwankować. Jeśli dobrze zaprojektujemy nasze aplikacje, to będą one solidne
  dowiedziałeś
  o wrażliwych       i nie będą przysparzać problemów za każdym razem, gdy użytkownik zacznie ich używać
  i kiepskich        w niezwykły bądź niespodziewany sposób. Klasy oraz diagramy sekwencji mogą pomóc
  aplikacjach.       w wykryciu usterek w projekcie aplikacji, jednak kluczowe znaczenie ma pisanie dobrze
                     zaprojektowanego i elastycznego kodu.

                     Klienci są zadowoleni, kiedy ich aplikacje można UAKTUALNIĆ.
                     Kiedy klient prosi o dodanie kilku nowych, prostych możliwości, to dla niego nie ma nic
 Chcesz poznać       gorszego, niż usłyszeć, że wykonanie poprawek zajmie dwa tygodnie i będzie kosztowało
 więcej informacji                                                                                                   To wszystko
 o delegowaniu,      kilkadziesiąt tysięcy złotych. Dzięki zastosowaniu technik projektowania obiektowego, takich    nazywamy
 złożeniach                                                                                                          właśnie analizą
                     jak hermetyzacja, składanie oraz delegowanie, tworzone oprogramowanie będzie proste             i projektowaniem
 i agregacji?
 Wszystkie te        w utrzymaniu i łatwe do rozszerzania oraz aktualizacji.                                         obiektowym. Nie
 zagadnienia                                                                                                         chodzi tu wcale
 poruszymy                                                                                                           o tworzenie
                     Programiści są zadowoleni, kiedy napisanego kodu można                                          głupkowatych
 szczegółowo
  najpierw           WIELOKROTNIE UŻYWAĆ.                                                                            diagramów…
  w rozdziale 5.,                                                                                                    a o pisanie
  a następnie
                     Czy kiedyś napisałeś jakiś program dla jednego klienta, a następnie zauważyłeś, że po           odlotowych
  w rozdziale 8.     wprowadzeniu bardzo nieznacznych modyfikacji będzie on spełniał wymagania innego                aplikacji, które
                                                                                                                     uszczęśliwią
                     klienta? Wystarczy się nieco zastanowić nad tworzoną aplikacją, by uniknąć takich               klientów,
                     problemów jak wzajemne uzależnienie klas i niepotrzebne powiązania pomiędzy nimi,               a Tobie zapewnią
                     i w efekcie uzyskać kod, który z powodzeniem będzie można wielokrotnie stosować. Takie          błogie poczucie
                                                                                                                     wielkości
                     pojęcia jak Zasada Otwarte-Zamknięte (ang. Open-Close Principle, w skrócie OCP) oraz            i satysfakcji.
                     Zasada Pojedynczej Odpowiedzialności (ang. Single Responsibility Principle, w skrócie SRP)
W rozdziale 8.       bardzo mogą w tym pomóc.
przekonasz się,
jak duże znaczenie
i wpływ na           Programiści są zadowoleni, kiedy pisane przez nich aplikacje są ELASTYCZNE.
powstający kod
mają te zasady.      Czasami tylko nieznaczne zmiany i refaktoryzacja pozwalają zamienić dobrą aplikację we
                     wspaniały framework, którego z powodzeniem będzie można używać do przeróżnych zadań.
                     To właśnie umiejętność wprowadzania takich zmian sprawi, że powoli przestaniesz być
                     zwyczajnym koderem i zaczniesz myśleć jak prawdziwy architekt oprogramowania (o tak…
                     ci goście zarabiają znacznie więcej). Tu chodzi o ogarnięcie całości zagadnienia.

                                                                       się umiejętnością ogólnego
                                       W rozdziałach 6. i 7. zajmiemy
                                       spojrzenia na rozwiązywan y problem i tworzeniem dobrej
                                       architektury dla pisanych aplikacji.
                                                                                                          jesteś tutaj    79
Powtórzenie i prośba



                                              No i fantastycznie! Dzięki
                                               temu nowemu narzędziu do
                                           wyszukiwania nie mogę się opędzić
                                             od klientów. Ale swoją drogą…
                                           mam parę pomysłów na kilka nowych
                                                      możliwości…



                             propozycję
       Widzisz? Już dostałeś Rysiek
       dalszej pracy. Jednak
                            poczekać
       będzie musiał z tym    my kilka
       aż do rozdziału 5… Ma którymi
                             w,
       ważniejszych problemó tępnym
       musimy się zająć w nas
        rozdziale.




               KlUCZOWe
               ZAGADNieNiA
  u	Jeśli aplikacja jest wrażliwa, byle co może doprowadzić do    u	 zapewnieniu elastyczności projektu możesz zastosować
                                                                    Po
     jej awarii.                                                     wzorce projektowe, by dodatkowo go poprawić i ułatwić
                                                                     wielokrotne stosowanie kodu aplikacji.
  u	Dzięki zastosowaniu zasad projektowania obiektowego,
     takich jak hermetyzacja i delegowanie, można tworzyć         u	Odszukaj te fragmenty aplikacji, które często ulegają
     znacznie bardziej elastyczne aplikacje.                         zmianom, i postaraj się oddzielić je od pozostałych
                                                                     fragmentów, które się nie zmieniają.
  u	Hermetyzacja polega na dzieleniu aplikacji na logiczne
     części.                                                      u	Tworzenie aplikacji, które działają, lecz są nieprawidłowo
                                                                     zaprojektowane, zadowoli klienta, jednak Tobie przysporzy
  u	 delegowaniu mówimy wtedy, gdy jeden obiekt przekazuje
    O
                                                                     wielu zmartwień, problemów i nieprzespanych nocy,
     wykonanie pewnego zadania do innego obiektu.
                                                                     zmarnowanych na żmudne poprawianie błędów.
  u	Zawsze zaczynaj prace nad projektem od określenia,
                                                                  u	Analiza i projektowanie obiektowe udostępnia metody
     czego chce klient.
                                                                     pozwalające na tworzenie poprawnie zaprojektowanych
  u	Kiedy uda Ci się już poprawnie zaimplementować                   aplikacji spełniających wymagania nie tylko klienta, lecz
     podstawowe możliwości funkcjonalne aplikacji, możesz            także programisty.
     zająć się zmodyfikowaniem jej struktury i zapewnieniem jej
     elastyczności.


80    Rozdział 1.
Dobrze zaprojektowane aplikacje są super




                                                                                                                     rozwiązania
                                                                                                                     ćwiczeń




                                              ?
                                                                                        

                     dlaczEgo mam na To
                 
                      zWracać uWagĘ ?
                                                            

      Dowiedziałeś się już całkiem dużo na temat pisania doskonałego oprogramowania, jednak
      wciąż jeszcze musisz się wiele nauczyć. Weź głęboki oddech i zastanów się nad niektórymi
      terminami i zasadami, które przedstawiliśmy w tym rozdziale. Połącz słowa umieszczone
      w lewej kolumnie z wyjaśnieniami celów stosowania danych technik lub zasad, znajdującymi
      się w kolumnie prawej.

                                       Beze mnie Twój klient nigdy nie będzie zadowolony. Bez względu na to, jak wspaniale byłaby
Elastyczność                           zaprojektowana i napisana aplikacja, to właśnie ja sprawiam, że na twarzy klienta pojawia się
                                       uśmiech.
                                       Ja wkraczam do akcji tam, gdzie chodzi o możliwości wielokrotnego wykorzystania tego samego kodu
Hermetyzacja                           i uzyskanie pewności, że nie trzeba będzie rozwiązywać problemów, które ktoś inny rozwiązał już
                                       wcześniej.
                                       Programiści używają mnie po to, by oddzielić od siebie fragmenty kodu, które ulegają zmianom,
Funkcjonalność                         od tych, które pozostają takie same; dzięki temu łatwo można wprowadzać w kodzie modyfikacje
                                       — bez obawy, że cała aplikacja przestanie działać.
                                       Programiści używają mnie po to, by oprogramowanie można było rozwijać i zmieniać bez konieczności
Wzorce projektowe                      ciągłego pisania go od samego początku. To ja sprawiam, że aplikacje stają się solidne i odporne.




                                                                                                         jesteś tutaj                 81
Rozwiązania ćwiczeń

     Zaostrz ołówek
          Rozwiązanie       Co byś zmienił w tym kodzie?
                            W przedstawionym kodzie występuje poważny problem, Twoim zadaniem jest go odnaleźć.
                            W poniższych pustych wierszach zapisz, na czym według Ciebie polega ten problem
                            oraz w jaki sposób można by go rozwiązać.

                     Za każdym razem, gdy do klasy GuitarSpec zostanie dodana nowa właściwość bądź gdy zmieni
                     się któraś z jej metod, konieczne będzie wprowadzenie zmian także w metodzie search() klasy
                     Inventory. Porównywaniem powinna się zajmować klasa GuitarSpec; do niej powinniśmy także
                     przenieść cały kod operujący na specyfikacji gitary, umieszczony obecnie w klasie Inventory.




                     public List search(GuitarSpec searchSpec) {
                         List matchingGuitars = new LinkedList();
                         for (Iterator i = guitars.iterator(); i.hasNext(); ) {
                            Guitar guitar = (Guitar)i.next();
To nie jest                 GuitarSpec guitarSpec = guitar.getSpec();
przykład dobrego            if (searchSpec.getBuilder() != guitarSpec.getBuilder())
projektu. Za                  continue;
każdym razem,               String model = searchSpec.getModel().toLowerCase();
gdy do klasy
 GuitarSpec                 if ((model != null)  (!model.equals(””)) 
 zostanie dodana                (!model.equals(guitarSpec.getModel().toLowerCase())))
 nowa właściwość,             continue;
 konieczne będzie
 wprowadzenie               if (searchSpec.getType() != guitarSpec.getType())
 zmian w tym                  continue;
  kodzie.                   if (searchSpec.getBackWood() != guitarSpec.getBackWood())
                              continue;
                            if (searchSpec.getTopWood() != guitarSpec.getTopWood())
                              continue;
                            matchingGuitars.add(guitar);
                         }
                         return matchingGuitars;
                       }
                                                                                                        class
                                                                                                        Inven-
                                                                                                        tory {

                                                                                                        search()


                                                                                                    Inventory.java
                                Zastanów się: czy klasa Inventor
                               na obsłudze magazynu gitar Ryśk y naprawdę koncentruje się
                                                                a?
                               tym, co sprawia, że dwie specyfika Czy też koncentruje się na
                               GuitarSpec — są sobie równe?       cje gitar — czyli dwa obiekty
                                                               Chce
                               się na swoich zadaniach. Porówny sz, by Twoje klasy skupiały
                               jest zadaniem, którego wykonani wanie obiektów GuitarSpec
                                                              em
                               GuitarSpec, a nie klasa Inventor powinna zajmować się klasa
                                                               y.



82     Rozdział 1.

More Related Content

PDF
Sztuka zarządzania projektami
PDF
Head First Software Development. Edycja polska
PDF
Head First Design Patterns. Edycja polska
PPTX
Badania systemow finansowych. WUD Krakow 2012
PDF
Asystent.osoby.niepelnosprawnej 346[02] o1.03_u
PDF
Proces projektowy metodyka
PDF
Skuteczne Zarządzanie Projektami Internetowymi 2015
PDF
Projektowanie zorientowane obiektowo. Wzorce projektowe. Wydanie II
Sztuka zarządzania projektami
Head First Software Development. Edycja polska
Head First Design Patterns. Edycja polska
Badania systemow finansowych. WUD Krakow 2012
Asystent.osoby.niepelnosprawnej 346[02] o1.03_u
Proces projektowy metodyka
Skuteczne Zarządzanie Projektami Internetowymi 2015
Projektowanie zorientowane obiektowo. Wzorce projektowe. Wydanie II

Viewers also liked (20)

PDF
.NET Framework 2.0. Zaawansowane programowanie
PDF
Розвиток публічного права в Україні (доповідь за 2009–2010 роки)
PDF
Scalone dokumenty (10)
PDF
Інфляція в Україні в грудні 2015 року
RTF
Постанова ВР "Про відсіч збройній агресії Російської Федерації та подолання ї...
PPTX
Matura z geografii w 2015 r
PPTX
Value Co-Creation in Incubation Process
PDF
Українська середня освіта вкрай застаріла
PDF
B2B e-Commerce w TIM SA
PDF
Проблеми теорії та практики інспекційної діяльності публічної адміністрації в...
PPTX
Rodzaje wolnych licencji.
PDF
Взаємодія Верховної Ради України, Президента України та Кабінету Міністрів Ук...
PDF
Raport uzytkowanie form reklamy internetowej w 2013r
PDF
Malarz-tapeciarz
PDF
Technik.logistyk 342[04] z1.04_u
PDF
15 2009
PPT
Nauka 2.0: nowe narzędzia komunikacji naukowej
PDF
Nik p-13-058-terminal-lng
PDF
Kwantologia stosowana 5
.NET Framework 2.0. Zaawansowane programowanie
Розвиток публічного права в Україні (доповідь за 2009–2010 роки)
Scalone dokumenty (10)
Інфляція в Україні в грудні 2015 року
Постанова ВР "Про відсіч збройній агресії Російської Федерації та подолання ї...
Matura z geografii w 2015 r
Value Co-Creation in Incubation Process
Українська середня освіта вкрай застаріла
B2B e-Commerce w TIM SA
Проблеми теорії та практики інспекційної діяльності публічної адміністрації в...
Rodzaje wolnych licencji.
Взаємодія Верховної Ради України, Президента України та Кабінету Міністрів Ук...
Raport uzytkowanie form reklamy internetowej w 2013r
Malarz-tapeciarz
Technik.logistyk 342[04] z1.04_u
15 2009
Nauka 2.0: nowe narzędzia komunikacji naukowej
Nik p-13-058-terminal-lng
Kwantologia stosowana 5
Ad

Similar to Head First Object-Oriented Analysis and Design. Edycja polska (20)

PDF
J2EE. Podstawy programowania aplikacji korporacyjnych
PDF
Visual Basic .NET. Wzorce projektowe
PDF
Sprzedaj swój program. Droga do udanych projektów programistycznych
PDF
Projektowanie oprogramowania. Wstęp do programowania i techniki komputerowej
PDF
C#. Wzorce projektowe
PDF
C++. Inżynieria programowania
PDF
Agile. Programowanie zwinne: zasady, wzorce i praktyki zwinnego wytwarzania o...
PDF
Programowanie zorientowane obiektowo
PDF
Redesign Playmobile.pl - Polish IA Summit 2011
PDF
Paulina Rzymska, Marcin Piotrowski "Playmobile pl case study Polish IA Summit"
PDF
Podejście “cały zespół” a rola QA/BA
PDF
[33rd] x driven-y niczego nie zmienią
PDF
8. Programowanie w środowisku języka strukturalnego
PPTX
Metoda analizy i specyfikowania wymagań na oprogramowanie
PDF
UML. Inżynieria oprogramowania. Wydanie II
PPTX
Pomysł na analizę w Agile: Agile Modeling
PDF
Więcej niż architektura oprogramowania
PDF
MS Project 2002. Zarządzanie projektami
PDF
J2EE. Wzorce projektowe. Wydanie 2
PPTX
Modele wdrażania i zarządzania projektami erp
J2EE. Podstawy programowania aplikacji korporacyjnych
Visual Basic .NET. Wzorce projektowe
Sprzedaj swój program. Droga do udanych projektów programistycznych
Projektowanie oprogramowania. Wstęp do programowania i techniki komputerowej
C#. Wzorce projektowe
C++. Inżynieria programowania
Agile. Programowanie zwinne: zasady, wzorce i praktyki zwinnego wytwarzania o...
Programowanie zorientowane obiektowo
Redesign Playmobile.pl - Polish IA Summit 2011
Paulina Rzymska, Marcin Piotrowski "Playmobile pl case study Polish IA Summit"
Podejście “cały zespół” a rola QA/BA
[33rd] x driven-y niczego nie zmienią
8. Programowanie w środowisku języka strukturalnego
Metoda analizy i specyfikowania wymagań na oprogramowanie
UML. Inżynieria oprogramowania. Wydanie II
Pomysł na analizę w Agile: Agile Modeling
Więcej niż architektura oprogramowania
MS Project 2002. Zarządzanie projektami
J2EE. Wzorce projektowe. Wydanie 2
Modele wdrażania i zarządzania projektami erp
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'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
Ajax, JavaScript i PHP. Intensywny trening
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
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'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
Ajax, JavaScript i PHP. Intensywny trening
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

Head First Object-Oriented Analysis and Design. Edycja polska

  • 1. Head First Object-Oriented Analysis and Design. Edycja polska Autor: Brett D. McLaughlin, Gary Pollice, David West T³umaczenie: Piotr Rajca ISBN: 978-83-246-0965-9 Tytu³ orygina³u: Head First Object-Oriented Analysis and Design Format: 200x230, stron: 616 Przyk³ady na ftp: 165 kB Poznaj techniki analizy i projektowania obiektowego • Naucz siê zbieraæ wymagania od u¿ytkowników systemu • Zarz¹dzaj zmianami w specyfikacji • PrzeprowadŸ analizê i wykonaj projekt Systemy informatyczne staj¹ siê coraz bardziej rozbudowane. Programowanie obiektowe znacznie u³atwia ich tworzenie i póŸniejsze modyfikacje, aby jednak system by³ sprawny i funkcjonalny, musi zostaæ zaprojektowany w oparciu o prawid³owo zebrane wymagania. Tu równie¿ z pomoc¹ przychodzi metodologia obiektowa – wzorce projektowe, jêzyk UML i odpowiednie narzêdzia niezwykle u³atwiaj¹ przygotowanie dobrego projektu. Jeœli rozbudowane przyk³ady, skomplikowane diagramy i niezrozumia³e wywody teoretyczne wywo³uj¹ w Tobie niechêæ, koniecznie siêgnij po tê ksi¹¿kê! Dziêki niej poznasz metody analizy i projektowania obiektowego w nietypowy i ciekawy sposób, wykorzystuj¹cy najnowsze teorie skutecznego przekazywania wiedzy. Przeczytasz o tym, w jaki sposób warto gromadziæ wymagania i oczekiwania u¿ytkowników wobec projektowanego systemu, jak uwzglêdniaæ w projekcie postulowane zmiany i przeprowadzaæ proces analizy obiektowej. Nauczysz siê stosowaæ notacjê UML do przedstawiania struktury systemu i przetwarzanych przez niego danych. Dowiesz siê tak¿e, jak testowaæ projektowany system. • Zasady i cele projektowania obiektowego • Gromadzenie wymagañ • Przypadki u¿ycia Wydawnictwo Helion • Analiza obiektowa ul. Koœciuszki 1c • Diagramy UML przedstawiaj¹ce strukturê systemu 44-100 Gliwice • Korzystanie ze wzorców projektowych tel. 032 230 98 63 e-mail: helion@helion.pl • Projektowanie architektury systemu • Testowanie
  • 2. Spis treści Spis treści (skrócony) 1 Dobrze zaprojektowane aplikacje są super. Tu zaczyna się wspaniałe 31 oprogramowanie 2 Gromadzenie wymagań. Daj im to, czego chcą 83 3 Wymagania ulegają zmianom. Kocham cię, jesteś doskonały… 137 A teraz — zmień się 4 Analiza. Zaczynamy używać naszych aplikacji w rzeczywistym świecie 169 5 Część 1. Dobry projekt = elastyczne oprogramowanie. Nic nie pozostaje 221 wiecznie takie samo Przerywnik. Obiektowa katastrofa 245 Część 2. Dobry projekt = elastyczne oprogramowanie. Zabierz swoje 257 oprogramowanie na 30-minutowy trening 6 Rozwiązywanie naprawdę dużych problemów. „Nazywam się Art Vandelay... 301 jestem Architektem” 7 Architektura. Porządkowanie chaosu 343 8 Zasady projektowania. Oryginalność jest przereklamowana 395 9 Iteracja i testowanie. Oprogramowanie jest wciąż przeznaczone dla klienta 441 10 Proces projektowania i analizy obiektowej. Scalając to wszystko w jedno 499 Dodatek A Pozostałości 571 Dodatek B Witamy w Obiektowie 589 Skorowidz 603 Spis treści (szczegółowy) Wprowadzenie Twój mózg koncentruje się na analizie i projektowaniu obiektowym. Podczas gdy Ty starasz się czegoś nauczyć, Twój mózg robi Ci przysługę i dba o to, abyś przez przypadek nie zapamiętał zdobywanych informacji. Myśli sobie: „Lepiej zostawić trochę miejsca na bardziej istotne sprawy, na przykład jakich zwierząt unikać albo czy jazda na snowboardzie nago jest dobrym pomysłem”. W jaki zatem sposób możesz oszukać swój mózg i przekonać go, że Twoje życie zależy od znajomości analizy i projektowania obiektowego? Dla kogo jest ta książka? 20 Wiemy, co sobie myślisz 21 Metapoznanie: myślenie o myśleniu 23 Zmuś swój mózg do posłuszeństwa 25 Ważne uwagi 26 Recenzenci techniczni 28 Podziękowania 29
  • 3. Spis treści Dobrze zaprojektowane aplikacje są super 1 Tu zaczyna się wspaniałe oprogramowanie A zatem, w jaki sposób w praktyce pisze się wspaniałe oprogramowanie? Zawsze bardzo trudno jest określić, od czego należy zacząć. Czy aplikacja faktycznie robi to, co powinna robić i czego od niej oczekujemy? A co z takimi problemami jak powtarzający się kod — przecież to nie może być dobre ani właściwe rozwiązanie, prawda? Zazwyczaj trudno jest określić, które z wielu problemów należy rozwikłać w pierwszej kolejności, a jednocześnie mieć pewność, że podczas wprowadzania poprawek nie popsujemy innych fragmentów aplikacji. Bez obaw. Po zakończeniu lektury tego rozdziału będziesz już dokładnie wiedział, jak pisać doskonałe oprogramowanie, i pewnie podążał w kierunku trwałego poprawienia sposobu tworzenia programów. I w końcu zrozumiesz, dlaczego OOAD to czteroliterowy skrót (pochodzący od angielskich słów: Object-Oriented Analysis and Design, analiza i projektowanie obiektowe), który Twoja matka chciałaby, byś poznał. Rock-and-roll jest wieczny! 32 Nowa elegancka aplikacja Ryśka… 33 Co przede wszystkim zmieniłbyś w aplikacji Ryśka? 38 Niby skąd mam wiedzieć, od Doskonałe oprogramowanie… czego należy zacząć? Mam wrażenie, że ilekroć ma więcej niż jedną z wymienionych już cech 40 zaczynam pracę nad nowym projektem, każdy ma inne zdanie odnośnie tego, co należy zrobić w pierwszej Wspaniałe oprogramowanie w trzech prostych krokach 43 kolejności. Czasami zrobię coś dobrze, lecz czasami kończy się na tym, że muszę przerobić W pierwszej kolejności skoncentruj się na funkcjonalności 48 całą aplikację od początku, bo zacząłem w złym miejscu. A ja chcę jedynie pisać świetne Test 53 oprogramowanie! A zatem, od czego Szukamy problemów 55 powinienem zacząć pisanie aplikacji dla Ryśka? Analiza metody search() 56 Stosuj proste zasady projektowania obiektowego 61 Projekt po raz pierwszy, projekt po raz drugi 66 Jak łatwo można wprowadzać zmiany w Twojej aplikacji? 68 Poddawaj hermetyzacji to, co się zmienia 71 Delegowanie 73 Nareszcie doskonałe oprogramowanie (jak na razie) 76 OOAD ma na celu tworzenie wspaniałego oprogramowania, a nie dodanie Ci papierkowej roboty 79 Kluczowe zagadnienia 80
  • 4. Spis treści Gromadzenie wymagań 2 Daj im to, czego chcą Każdy lubi zadowolonych klientów. Już wiesz, że pierwszy krok w pisaniu doskonałego oprogramowania polega na upewnieniu się, czego chce klient. Ale jak się dowiedzieć, czego klient oczekuje? Co więcej — skąd mieć pewność, że klient w ogóle wie, czego tak naprawdę chce? Właśnie wówczas na arenę wkraczają „dobre wymagania”. W tym rozdziale dowiesz się, w jaki sposób zadowolić klientów, upewniając się, że dostarczysz im właśnie to, czego chcą. Kiedy skończysz lekturę, wszystkie swoje projekty będziesz mógł opatrzyć etykietą „Satysfakcja gwarantowana” i posuniesz się o kolejny krok na drodze do tworzenia doskonałego oprogramowania… i to za każdym razem. Nadszedł czas na kolejny pokaz Twych programistycznych umiejętności 84 Test programu 87 Nieprawidłowe zastosowanie (coś w tym stylu) 89 Czym jest wymaganie? 90 Tworzenie listy wymagań 92 Zaplanuj, co może się popsuć w systemie 96 Problemy w działaniu systemu są obsługiwane przez ścieżki alternatywne 98 (Ponowne) przedstawienie przypadku użycia 100 Jeden przypadek użycia, trzy części 102 Porównaj wymagania z przypadkami użycia 106 Twój system musi działać w praktyce 113 Poznajemy Szczęśliwą Ścieżkę 120 Przybornik projektanta 134 Drzwiczki dla psa oraz pilot stanowią elementy systemu, bądź też znajdują się wewnątrz niego. System
  • 5. Spis treści Wymagania ulegają zmianom 3 Kocham cię, jesteś doskonały… A teraz — zmień się Sądzisz, że dowiedziałeś się już wszystkiego o tym, czego chciał klient? Nie tak szybko… A zatem przeprowadziłeś rozmowy z klientem, zgromadziłeś wymagania, napisałeś przypadki użycia, napisałeś i dostarczyłeś klientowi odlotową aplikację. W końcu nadszedł czas na miłego, relaksującego drinka, nieprawdaż? Pewnie… aż do momentu gdy klient uzna, że tak naprawdę chce czegoś innego niż to, co Ci powiedział. Bardzo podoba mu się to, co zrobiłeś — poważnie! — jednak obecnie nie jest już w pełni usatysfakcjonowany. W rzeczywistym świecie wymagania zawsze się zmieniają; to Ty musisz sobie z tymi zmianami poradzić i pomimo nich zadbać o zadowolenie klienta. Jesteś bohaterem! 138 Jesteś patałachem! 139 Jedyny pewnik analizy i projektowania obiektowego 141 Ścieżka oryginalna? Ścieżka alternatywna? Kto to wie? 146 Przypadki użycia muszą być zrozumiałe przede wszystkim dla Ciebie 148 Od startu do mety: jeden scenariusz 150 Wyznanie Ścieżki Alternatywnej 152 Uzupełnienie listy wymagań 156 Powielanie kodu jest bardzo złym pomysłem 164 Ostateczny test drzwiczek 166 Napisz swoją własną zasadę projektową! 167 Przybornik projektanta 168 public void pressButton() { System.out.println(”Naciśnięto przycisk na pilocie...”); if (door.isOpen()) { door.close(); } else { door.open(); final Timer timer = new Timer(); timer.schedule(new TimerTask() { public void run() { door.close(); timer.cancel(); } }, 5000); class Remote { } press- Button() }
  • 6. Spis treści Analiza 4 Zaczynamy używać naszych aplikacji w rzeczywistym świecie Czas zdać ostatnie egzaminy i zacząć stosować nasze aplikacje w rzeczywistym świecie. Twoje aplikacje muszą robić nieco więcej, niż jedynie działać prawidłowo na komputerze, którego używasz do ich tworzenia — komputerze o dużej mocy i doskonale skonfigurowanym; Twoje aplikacje muszą działać w takich warunkach, w jakich rzeczywiści klienci będą ich używali. W tym rozdziale zastanowimy się, jak zyskać pewność, że nasze aplikacje będą działać w rzeczywistym kontekście. Dowiesz się w nim, w jaki sposób analiza tekstowa może przekształcić stworzony wcześniej przypadek użycia w klasy i metody, które na pewno będą działać zgodnie z oczekiwaniami klienta. A kiedy skończysz lekturę tego rozdziału, także i Ty będziesz mógł powiedzieć: „Dokonałem tego! Moje oprogramowanie jest gotowe do zastosowania w rzeczywistym świecie!”. Jeden pies, dwa psy, trzy psy, cztery… 170 Kiedy już określiłam, Twoje oprogramowanie ma kontekst 171 jakich klas i operacji będę potrzebować, odpowiednio Określ przyczynę problemu 172 zaktualizowałam diagram klas. Zaplanuj rozwiązanie 173 Opowieść o dwóch programistach 180 Delegowanie w kodzie Szymka — analiza szczegółowa 184 Potęga aplikacji, których elementy są ze sobą luźno powiązane 186 Zwracaj uwagę na rzeczowniki występujące w przypadku użycia 191 Od dobrej analizy do dobrych klas… 204 Diagramy klas bez tajemnic 206 Diagramy klas to nie wszystko 211 Kluczowe zagadnienia 215 rzeczy W tym kontekście rót przy bierają zły ob j. znacznie częście W rzeczywistym świecie spotykamy inne psy, koty, gryzonie oraz całą masę innych class problemów; a wszystkie te DogDoor { czynniki mają tylko jeden cel } open() — doprowadzić do awarii naszego oprogramowania. DogDoor.java Rzeczywisty Świat
  • 7. Spis treści Dobry projekt = elastyczne oprogramowanie 5 Nic nie pozostaje wiecznie takie samo Zmiany są nieuniknione. Niezależnie od tego, jak bardzo podoba Ci się Twoje oprogramowanie w jego obecnej postaci, to najprawdopodobniej jutro zostanie ono zmodyfikowane. A im bardziej utrudnisz wprowadzanie modyfikacji w aplikacji, tym trudniej będzie Ci w przyszłości reagować na zmiany potrzeb klienta. W tym rozdziale mamy zamiar odwiedzić (część 1.) naszego starego znajomego oraz spróbować poprawić projekt istniejącego oprogramowania. Na tym przykładzie przekonamy się, jak niewielkie zmiany mogą doprowadzić do poważnych problemów. Prawdę mówiąc, jak się okaże, odkryte przez nas kłopoty będą tak poważne, że ich rozwiązanie będzie wymagało rozdziału składającego się aż z DWÓCH części! Firma Gitary/Instrumenty Strunowe Ryśka rozwija się 222 Klasy abstrakcyjne 225 Diagramy klas bez tajemnic (ponownie) 230 Ściągawka z UML-a 231 Porady dotyczące problemów projektowych 237 Trzy kroki tworzenia wspaniałego oprogramowania (po raz kolejny) 239 5 Obiektowa K atastrofa! (przerywnik) 10
  • 8. Spis treści Dobry projekt = elastyczne oprogramowanie 5 Zabierz swoje oprogramowanie na 30-minutowy trening Czy kiedykolwiek marzyłeś o tym, by być nieco bardziej elastycznym w działaniu? Jeśli kiedykolwiek wpadłeś w kłopoty podczas prób wprowadzania zmian w aplikacji, to zazwyczaj oznacza to, że Twoje oprogramowanie powinno być nieco bardziej (część 2.) elastyczne i odporne. Aby pomóc swojej aplikacji, będziesz musiał przeprowadzić odpowiednią analizę, zastanowić się nad niezbędnymi zmianami w projekcie i dowiedzieć się, w jaki sposób rozluźnić zależności pomiędzy jej elementami. I w końcu, w wielkim finale, przekonasz się, że większa spójność może pomóc w rozwiązaniu problemu powiązań. Brzmi interesująco? A zatem przewróć kartkę — przystępujemy do poprawiania nieelastycznej aplikacji. Wróćmy do aplikacji wyszukiwawczej Ryśka 258 Dokładniejsza analiza metody search() 261 Korzyści, jakie dała nam analiza 262 Dokładniejsza analiza klas instrumentów 265 Śmierć projektu (decyzja) 270 Zmieńmy złe decyzje projektowe na dobre 271 Zastosowanie „podwójnej hermetyzacji” w aplikacji Ryśka 273 Nigdy nie obawiaj się wprowadzania zmian 279 Elastyczna aplikacja Ryśka 282 Testowanie dobrze zaprojektowanej aplikacji Ryśka 285 Jak łatwo można zmodyfikować aplikację Ryśka? 289 Wielki konkurs łatwości modyfikacji 290 Spójna klasa realizuje jedną operację naprawdę dobrze 293 Przegląd zmian wprowadzanych w oprogramowaniu dla Ryśka 296 Doskonałe oprogramowanie to zazwyczaj takie, które jest „wystarczająco dobre” 298 Przybornik projektanta 300 11
  • 9. Spis treści Rozwiązywanie naprawdę dużych problemów 6 „Nazywam się Art Vandelay… jestem Architektem” Nadszedł czas, by zbudować coś NAPRAWDĘ DUŻEGO. Czy jesteś gotów? Zdobyłeś już wiele narzędzi do swojego projektanckiego przybornika, jednak w jaki sposób z nich skorzystasz, kiedy będziesz musiał napisać coś naprawdę dużego? Cóż, może jeszcze nie zdajesz sobie z tego sprawy, ale dysponujesz wszystkimi narzędziami, jakie mogą być potrzebne do skutecznego rozwiązywania poważnych problemów. Niebawem poznasz kilka nowych narzędzi, takich jak analiza dziedziny oraz diagramy przypadków użycia, jednak nawet one bazują na wiadomościach, które już zdobyłeś, takich jak uważne słuchanie klienta oraz dokładne zrozumienie, co trzeba napisać, zanim jeszcze przystąpimy do faktycznego pisania kodu. Przygotuj się… nadszedł czas, byś sprawdził, jak sobie radzisz w roli architekta. Rozwiązywanie dużych problemów 302 Wszystko zależy od sposobu spojrzenia na duży problem 303 Wymagania i przypadki użycia to dobry punkt wyjściowy… 308 Potrzebujemy znacznie więcej informacji 309 Określanie możliwości 312 Możliwość czy wymaganie 314 Przypadki użycia nie zawsze pomagają ujrzeć ogólny obraz tworzonego oprogramowania 316 Diagramy przypadków użycia 318 Mały aktor 323 Aktorzy to także ludzie (no dobrze… nie zawsze) 324 A zatem zabawmy się w analizę dziedziny! 329 Dziel i rządź 331 Nie zapominaj, kim tak naprawdę jest klient 335 Czym jest wzorzec projektowy? 337 Potęga OOAD (i trochę zdrowego rozsądku) 340 Przybornik projektanta 342 12
  • 10. Spis treści Architektura 7 Porządkowanie chaosu Gdzieś musisz zacząć, jednak uważaj, żeby wybrać właściwe „gdzieś ”! Już wiesz, jak podzielić swoją aplikację na wiele małych problemów, jednak oznacza to tylko i wyłącznie tyle, iż obecnie nie masz jednego dużego, lecz WIELE małych problemów. W tym rozdziale spróbujemy pomóc Ci w określeniu, gdzie należy zacząć, i upewnimy się, że nie będziesz marnował czasu na zajmowanie się nie tym, co trzeba. Nadeszła pora, by pozbierać te wszystkie drobne kawałki na Twoim biurku i zastanowić się, jak można je przekształcić w uporządkowaną i dobrze zaprojektowaną aplikację. W tym czasie poznasz niesłychanie ważne „trzy P dotyczące architektury” i dowiesz się, że Risk to znacznie więcej niż jedynie słynna gra wojenna z lat 80. Czy czujesz się nieco przytłoczony? 344 Potrzebujemy architektury 346 Zacznijmy od funkcjonalności 349 Co ma znaczenie dla architektury 351 Trzy „P” dotyczące architektury 352 class Unit { Unit() class Tile { ge- Wszystko sprowadza się do problemu ryzyka 358 } class tUnit() } Board } { ge- Scenariusze pomagają zredukować ryzyko 361 tUnit() } Koncentruj się na jednej możliwości w danej chwili 369 Architektura jest strukturą Twojego projektu 371 Podobieństwa po raz kolejny 375 Analiza podobieństw: ścieżka do elastycznego oprogramowania 381 Co to znaczy? Zapytaj klienta 386 Zmniejszanie ryzyka pomaga pisać wspaniałe oprogramowanie 391 Kluczowe zagadnienia 392 13
  • 11. Spis treści Zasady projektowania 8 Oryginalność jest przereklamowana Powielanie jest najlepszą formą unikania głupoty. Nic chyba nie daje większej satysfakcji niż opracowanie całkowicie nowego i oryginalnego rozwiązania problemu, który męczy nas od wielu dni… aż do czasu gdy okaże się, że ktoś rozwikłał ten sam problem już wcześniej, a co gorsza — zrobił to znacznie lepiej niż my. W tym rozdziale przyjrzymy się kilku zasadom projektowania, które udało się sformułować podczas tych wszystkich lat stosowania komputerów, i dowiemy się, w jaki sposób mogą one sprawić, że staniesz się lepszym programistą. Porzuć ambitne myśli o „zrobieniu tego lepiej” — lektura tego rozdziału udowodni Ci, jak pisać programy sprytniej i szybciej. Zasada projektowania — w skrócie 396 Zasada otwarte-zamknięte 397 OCP, krok po kroku 399 Zasada nie powtarzaj się 402 Zasada DRY dotyczy obsługi jednego wymagania w jednym miejscu 404 Zasada otwarte- -zamknięte Zasada jednej odpowiedzialności 410 Wykrywanie wielu odpowiedzialności 412 Przechodzenie od wielu do jednej odpowiedzialności 415 Zasada podstawienia Liskov 420 Studium błędnego sposobu korzystania z dziedziczenia 421 LSP ujawnia ukryte problemy związane ze strukturą dziedziczenia 422 Musi istnieć możliwość zastąpienia typu bazowego jego typem pochodnym 423 Naruszenia LSP sprawiają, że powstający kod staje się mylący 424 Zasada nie powtarzaj się Deleguj funkcjonalność do innej klasy 426 Użyj kompozycji, by zebrać niezbędne zachowania z kilku innych klas 428 Agregacja — kompozycja bez nagłego zakończenia 432 Agregacja a kompozycja 433 Dziedziczenie jest jedynie jedną z możliwości 434 Kluczowe zagadnienia 437 Zasada jednejości Przybornik projektanta 438 odpowiedzialn Zasada podstawienia Liskov 1
  • 12. Spis treści Powtarzanie i testowanie 9 Oprogramowanie jest wciąż przeznaczone dla klienta Czas pokazać klientowi, jak bardzo Ci na nim zależy. Nękają Cię szefowie? Klienci są zmartwieni? Udziałowcy wciąż zadają pytanie: „Czy wszystko będzie zrobione na czas?”. Żadna ilość nawet wspaniale zaprojektowanego kodu nie zadowoli Twoich klientów; musisz pokazać im coś działającego. Teraz, kiedy dysponujesz już solidnym przybornikiem z narzędziami do programowania obiektowego, nadszedł czas, byś udowodnił swoim klientom, że pisane przez Ciebie oprogramowanie naprawdę działa. W tym rozdziale poznasz dwa sposoby pracy nad implementacją możliwości funkcjonalnych tworzonego oprogramowania — dzięki nim Twoi klienci poczują błogie ciepło, które sprawi, że powiedzą o Tobie: „O tak, nie ma co do tego wątpliwości, jest właściwą osobą do napisania naszej aplikacji!”. Twój przybornik narzędziowy powoli się wypełnia 442 Wspaniałe oprogramowanie tworzy się iteracyjnie 444 Schodzenie w głąb: dwie proste opcje 445 Programowanie w oparciu o możliwości 446 Programowanie w oparciu o przypadki użycia 447 Dwa podejścia do tworzenia oprogramowania 448 Analiza możliwości 452 Pisanie scenariuszy testowych 455 Programowanie w oparciu o testy 458 Podobieństwa po raz wtóry 460 Kładziemy nacisk na podobieństwa 464 Hermetyzujemy wszystko 466 Dopasuj testy do projektu 470 Testy bez tajemnic… 472 Udowodnij klientowi, że wszystko idzie dobrze 478 Jak dotąd używaliśmy programowania w oparciu o kontrakt 480 Tak naprawdę programowanie w oparciu o kontrakt dotyczy zaufania 481 Programowanie defensywne 482 Podziel swoją aplikację na mniejsze fragmenty funkcjonalności 491 Kluczowe zagadnienia 493 Przybornik projektanta 496 1
  • 13. Spis treści Proces projektowania i analizy obiektowej 10 Scalając to wszystko w jedno Czy dotarliśmy już do celu? Poświęciliśmy sporo czasu i wysiłku, by poznać wiele różnych sposobów pozwalających poprawić jakość tworzonego oprogramowania; teraz jednak nadeszła pora, by połączyć i podsumować wszystkie zdobyte informacje. Na to właśnie czekałeś: mamy zamiar zebrać wszystko, czego się nauczyłeś, i pokazać Ci, że wszystkie te informacje stanowią części jednego procesu, którego możesz wielokrotnie używać, by tworzyć wspaniałe oprogramowanie. Tworzenie oprogramowania w stylu obiektowym 500 Trans-Obiektów 504 Mapa metra w Obiektowie 506 Lista możliwości 509 Przypadki użycia odpowiadają zastosowaniu, możliwości odpowiadają funkcjonalności 515 A teraz zacznij powtarzać te same czynności 519 Dokładniejsza analiza sposobu reprezentacji sieci metra 521 Używać klasy Line czy też nie używać... oto jest pytanie 530 Najważniejsze sprawy związane z klasą Subway 536 Ochrona własnych klas 539 Czas na przerwę 547 Wróćmy znowu do etapu określania wymagań 549 Koncentruj się na kodzie, a potem na klientach 551 Powtarzanie sprawia, że problemy stają się łatwiejsze 555 Jak wygląda trasa? 560 Samemu sprawdź Przewodnik Komunikacyjny po Obiektowie 564 Ktoś chętny na trzeci cykl prac? 567 Podróż jeszcze nie dobiegła końca… 569 owego Analiza Ścieżka alternatywna Hermetyzacja obiekt k inicjują cy owania Rozmowa z klientem ni iwości y projekt Architektura Zewnętrzny czyn zowych możl Zasad Lista kluc Wzorzec projektowy Zasady projektowe Scenariusz Wzorzec projektowy ci zenie żliwoś Podobieństwa Powtór Rozmowa z klientem u o mo Lista kluczo Zewnętrzny Ścieżka alternatywna Powtórze nie Hermetyzacja w oparci nie a wych możliwoś czynnik Delegowanie Analiza tekstowa gramow ci Lista wymaga ń Spójność Pro zenie Pr Różnice Powtór Zasady projektowe ogramowanie w oparciu o testy Architektura zenie Powtór Scenariusz 1
  • 14. Spis treści Dodatek A Pozostałości A Dziesięć najważniejszych tematów (których nie poruszyliśmy) Możesz nam wierzyć albo i nie, ale to jeszcze nie jest koniec. Owszem, wyobraź sobie, że nawet po przeczytaniu tych 600 stron wciąż możesz jeszcze znaleźć tematy, o których nawet nie wspomnieliśmy. Choć dziesięć zagadnień, jakie mamy zamiar przedstawić w tym dodatku, nie zasługuje na wiele więcej niż krótką wzmiankę, to jednak nie chcieliśmy, byś opuszczał Obiektów bez informacji na ich temat. Teraz będziesz miał nieco więcej tematów do rozmów podczas firmowej imprezy z okazji wygrania telewizyjnego quizu Obiektowa Katastrofa… poza tym któż, od czasu do czasu, nie kocha stymulujących rozmów o analizie i projektowaniu? Kiedy już skończymy, pozostanie jeszcze… następny dodatek… no i oczywiście indeks, i może kilka reklam… ale później dotrzesz wreszcie do końca książki. Obiecujemy. Nr 1. JEST i MA 572 Nr 2. Sposoby zapisu przypadków użycia 574 Nr 3. Antywzorce 577 Nr 4. Karty CRC 578 Nr 5. Metryki 580 Nr 6. Diagramy sekwencji 581 Nr 7. Diagramy stanu 582 Nr 8. Testowania jednostkowe 584 Nr 9. Standardy kodowania i czytelny kod 586 Nr 10. Refaktoryzacja 588 1
  • 15. Spis treści Dodatek B Witamy w Obiektowie B Stosowanie języka obiektowego Przygotuj się na zagraniczną wycieczkę. Czas odwiedzić Obiektów — miejsce, gdzie obiekty robią to, co powinny, aplikacje są dobrze hermetyzowane (już wkrótce dowiesz się, co to znaczy), a projekty oprogramowania pozwalają na ich wielokrotne stosowanie i rozbudowę. Musisz jeszcze poznać kilka dodatkowych zagadnień i poszerzyć swoje umiejętności językowe. Nie przejmuj się jednak, nie zajmie Ci to wiele czasu i zanim się obejrzysz, już będziesz rozmawiał w języku obiektowym, jakbyś mieszkał w Obiektowie od wielu lat. UML i diagramy klas 591 Dziedziczenie 593 Polimorfizm 595 Hermetyzacja 596 Kluczowe zagadnienia 600 S Skorowidz 603 1
  • 16. 1. Dobrze zaprojektowane aplikacje są super Tu się zaczyna wspaniałe oprogramowanie Naprawdę trudno mi przyjść po tym wszystkim do siebie, ale od kiedy zacząłem stosować analizę i projektowanie obiektowe, stałem się zupełnie innym człowiekiem… mówię ci, zupełnie innym! A zatem, w jaki sposób w praktyce pisze się wspaniałe oprogramowanie? Zawsze bardzo trudno jest określić, od czego należy zacząć. Czy aplikacja faktycznie robi to, co powinna robić i czego od niej oczekujemy? A co z takimi problemami jak powtarzający się kod — przecież to nie może być dobre ani właściwe rozwiązanie, prawda? Zazwyczaj trudno jest określić, które z wielu problemów należy rozwikłać w pierwszej kolejności, a jednocześnie mieć pewność, że podczas wprowadzania poprawek nie popsujemy innych fragmentów aplikacji. Bez obaw. Po zakończeniu lektury tego rozdziału będziesz już dokładnie wiedział, jak pisać doskonałe oprogramowanie i pewnie podążał w kierunku trwałego poprawienia sposobu tworzenia programów. I w końcu zrozumiesz, dlaczego OOAD to czteroliterowy skrót (pochodzący od angielskich słów: Object-Oriented Analysis and Design, analiza i projektowanie obiektowe), który Twoja matka chciałaby, byś poznał. to jest nowy rozdział  31
  • 17. Dźwięki drewna i stali Rock-and-roll jest wieczny! Nie ma nic lepszego niż dźwięki doskonałej gitary w rękach świetnego muzyka, a firma Gitary Ryśka specjalizuje się w wyszukiwaniu doskonałych instrumentów dla wymagających i doświadczonych klientów. Nie uwierzyłbyś, jaki mamy wybór gitar. Zapraszam, powiedz mi, jaka gitara Cię interesuje, a zapewniam, że znajd ziemy instrument idealnie odpowiadając y Twoim potrzebom i oczekiwan iom! Poznaj Ryśka, pasjonata i miłośnika gitar, a jednocześnie właściciela ekskluzywnego sklepu z gitarami. Właśnie kilka miesięcy temu Rysiek porzucił stosowany wcześniej „papierowy” system informacji o gitarach i podjął decyzję o przechowywaniu danych o stanie magazynu i transakcjach w systemie komputerowym. Wynajął w tym celu popularną firmę programistyczną SzybkoIKiepsko Sp. z o.o., która napisała mu odpowiednią aplikację. Rysiek poprosił ich nawet o napisanie nowego narzędzia — programu, który wspomagałby dobieranie gitar dla klientów. 32 Rozdział 1.
  • 18. Dobrze zaprojektowane aplikacje są super Nowa elegancka aplikacja Ryśka… Oto aplikacja, którą firma programistyczna napisała dla Ryśka… Jej zespół stworzył system, który całkowicie zastępuje papierowe notatki spisywane przez Ryśka wcześniej i który pomaga mu w odnajdywaniu gitar doskonale spełniających oczekiwania klientów. Oto diagram UML klas, który programiści firmy przedstawili Ryśkowi, by pokazać, co dla niego zrobili: Każda gitara znajdująca się Oto cały magazyn sklepu w magazynie sklepu Ryśka jest Ryśka, który jednocześnie Magazyn zawiera list ę reprezentowana przez obiekt zapewnia mu możliwość wszystkich gitar, jakimi Rysiek tej klasy. poszukiwania gitar. ponuje. aktualnie dys Guitar Inventory serialNumber: String guitars: List price: double addGuitar(String, double, String, String, String, builder: String Oto zmienne String, String) dostępne model: String getGuitar(String): Guitar w klasie type: String Do tej metody Guitar. search(Guitar): Guitar przekazywane backWood: String są szczegółowe topWood: String informacje Ta metoda o gitarze; getSerialNumber(): String pobiera numer metoda seryjny gitary Ta metoda służy do ta tworzy getPrice(): double wyszukiwania gitar; i zwraca odpowiedni setPrice(float) reprezentujący pobiera ona obiekt obiekt Guitar getBuilder(): String ją obiekt. stanowiący reprezentację i zapisuje go gitary idealnie spełniającej w magazynie getModel(): String wymagania klienta i zwraca Ryśka. Oto metody getType(): String wyszukany w magazynie klasy obiekt gitary, która pasuje getBackWood(): String do zadanych kryteriów. Guitar. getTopWood(): String do informacji Rysiek zdecydował, że opisujących każ dą gitarę należą: producent Przygotowaliśmy dla Ciebie kilka numer seryjny, cena, yczna bądź interesujących i ciekawych sma ora z model, typ (elektr kołyków, k drewna, znajdziesz je w dodatku B. Jeśl akustyczna) oraz gatune tał wykonany. i pierwszy stykasz się z zagadnie po raz z jakieg o instrument zos niam dotyczącymi projektowania obiektow i lub języka UML, to zajrzyj tam ego koniecznie. Nowy w Obiektowie? Jeśli nie spotkałeś się wcześniej z projektowaniem obiektowym, nie słyszałeś o diagramach UML bądź też nie jesteś pewny, czy dobrze rozumiesz znaczenie diagramów przedstawionych na powyższym rysunku, nie przejmuj się! Przygotowaliśmy specjalny pakiet ratunkowy „Witamy w Obiektowie”, który pomoże Ci wszystko zrozumieć. Zajrzyj na sam koniec książki i przeczytaj dodatek B — gwarantujemy Ci, że nie będziesz żałował. Kiedy skończysz, ponownie przeczytaj tę stronę, a na pewno wszystko nabierze zupełnie nowego sensu. jesteś tutaj  33
  • 19. Początkowy kod aplikacji Ryśka Oto jak wygląda kod Guitar.java Na poprzedniej stronie przedstawiliśmy diagramy klas tworzących aplikację Ryśka; teraz nadszedł czas, abyś zobaczył, jak wygląda faktyczny kod źródłowy klas Guitar i Inventory (umieszczony odpowiednio w plikach Guitar.java oraz Inventory.java). public class Guitar { private String serialNumber, builder, model, type, backWood, topWood; To wszystko są private double price; właściwości, które niej public Guitar(String serialNumber, double price, widzieliśmy już wcześ itar. na diagramie klasy Gu String builder, String model, String type, String backWood, String topWood) { this.serialNumber = serialNumber; this.price = price; this.builder = builder; Na diagramach UML nie są umieszczane this.model = model; konstruktory klas; konstruktor klasy Guitar robi this.type = type; dokładnie to, czego można od niego oczekiwać: określa początkowe wartości właściwości nowego this.backWood = backWood; obiektu Guitar. this.topWood = topWood; } public String getSerialNumber() { return serialNumber; } public double getPrice() { return price; Guitar } serialNumber: String public void setPrice(float newPrice) { this.price = newPrice; price: double } builder: String public String getBuilder() { model: String return builder; Łatwo zauważyć, type: String jak diagram klas } odpowiada metodom, backWood: String public String getModel() { które możemy znaleźć topWood: String return model; w kodzie źródłowym klasy Guitar getSerialNumber(): String } public String getType() { getPrice(): double return type; setPrice(float) } getBuilder(): String public String getBackWood() { getModel(): String return backWood; getType(): String } getBackWood(): String public String getTopWood() { class getTopWood(): String { Guitar return topWood; Gui- tar() } } } Guitar.java 34 Rozdział 1.
  • 20. Dobrze zaprojektowane aplikacje są super Plik Inventory.java… Pamiętaj, że usunęl iśmy instrukcje public class Inventory { nieco miejsca. private List guitars; import, by zaoszczędzić public Inventory() { guitars = new LinkedList(); } public void addGuitar(String serialNumber, double price, String builder, String model, String type, String backWood, String topWood) { Guitar guitar = new Guitar(serialNumber, price, builder, model, type, backWood, topWood); Metoda addGuitar() pob guitars.add(guitar); wszystkie informacje iera kon } utworzenia nowego obi ieczne do Guitar i dodaje go do ektu typu magazynu. public Guitar getGuitar(String serialNumber) { for (Iterator i = guitars.iterator(); i.hasNext(); ) { Guitar guitar = (Guitar)i.next(); if (guitar.getSerialNumber().equals(serialNumber)) { return guitar; } } return null; plikowana… } Ta metoda jest nieco bardziej skom obiektu ści porównuje ona wszystkie właściwo iu, ołan public Guitar search(Guitar searchGuitar) { Guitar, przekazanego w jej wyw któw tego typu, for (Iterator i = guitars.iterator(); i.hasNext(); ) { z właściwo ściami wszystkich obie Guitar guitar = (Guitar)i.next(); dostępnych w magazynie Ryśka. // Ignorujemy numer seryjny bo jest unikalny // Ignorujemy cenę gdyż jest unikalna String builder = searchGuitar.getBuilder(); if ((builder != null) (!builder.equals(“”)) (!builder.equals(guitar.getBuilder())) continue; String model = searchGuitar.getModel(); if ((model != null) (!model.equals(“”)) (!model.equals(guitar.getModel())) continue; String type = searchGuitar.getType(); if ((type != null) (!type.equals(“”)) (!type.equals(guitar.getType())) continue; String backWood = searchGuitar.getBackWood(); if ((backWood != null) (!backWood.equals(“”)) Inventory (!backWood.equals(guitar.getBackWood())) continue; guitar: List String topWood = searchGuitar.getTopWood(); if ((topWood != null) (!topWood.equals(“”)) addGuitar(String, double, String, String, String, (!topWood.equals(guitar.getTopWood())) String, String) continue; getGuitar(String): Guitar class return guitar; Inven- } search(Guitar): Guitar tory{ return null; search() } } Inventory.java jesteś tutaj  35
  • 21. Problem brakującej gitary Wkrótce jednak okazało się, że Rysiek zaczął tracić klientów… Wygląda na to, że niezależnie od tego, kim jest klient i jakiej gitary szuka, nowy program wyszukujący gitary prawie nigdy nie jest w stanie dopasować odpowiedniego instrumentu. Jednak Rysiek doskonale wie, że posiada gitarę, która na pewno spodobałaby się danemu klientowi… Zatem gdzie tkwi problem? Program FindGuitarTester.java symuluje typowy dzień pracy Ryśka… Zjawia się klient, mówi Ryśkowi, jaka gitara public class FindGuitarTester { by go interesowała, a Rysiek przeszukuje swój magazyn. tary public static void main(String[] args) { Ewa szuka gi Fender, // Inicjalizacja zawartości magazynu gitar Ryśka „Str at” firmy całości Inventory inventory = new Inventory(); wykonanej w owego. z drew na olch initializeInventory(inventory); Guitar whatEveLikes = new Guitar(“”, 0, “fender”, “Stratocastor”, “elektryczna”, “olcha”, “olcha”); Guitar guitar = inventory.search(whatEveLikes); if (guitar != null) { System.out.println(“Ewo, może spodoba Ci się gitara “ + guitar.getBuilder() + “ model “ + guitar.getModel() + “ “ + guitar.getType() + “ :n “ + guitar.getBackWood() + “ - tył i boki,n “ + guitar.getTopWood() + “ - góra.nMożesz ją mieć za “ + guitar.getPrice() + “PLN!”); } else { System.out.println(“Przykro mi, Ewo, nie znalazłem nic dla Ciebie.”); } } private static void initializeInventory(Inventory inventory) { ... } } class FingGui- tar { main() } FindGuitarTester.java Przykro mi, Ryśku, ale wygląda na to, że pójdę do innego sklepu. zie do Oto co się dzieje, gdy Ewa wejd leźć sklepu Ryśka, a ten spróbuje odna gitarę spełniającą jej oczekiwania. 36 Rozdział 1.
  • 22. Dobrze zaprojektowane aplikacje są super Ale przecież ja wiem, że świetną gitarę Stratocas mam tor fir Fender. Jest właśnie tut my aj: inventory.addGuitar(”V95693”, 1499.95, ”Fender”, ”Stratocastor”, ”elektryczna”, ”olcha”, ”olcha”); Wydaje się, że specyfikacja gitary dokładnie odpowiada oczekiwaniom Ewy… Zatem co się stało? Oto fragment kodu inicjalizującego zawartość magazynu Ryśka. Wygląda na to, że Rysiek faktycznie ma gitarę, która spełnia oczekiwania Ewy. Zaostrz ołówek W jaki sposób zmieniłbyś projekt aplikacji Ryśka? Przeanalizuj kody aplikacji Ryśka, przedstawione na trzech poprzednich stronach, oraz wyniki wykonanego testu. Jakie problemy udało Ci się zauważyć? Co byś zmienił? Poniżej zapisz PIERWSZĄ rzecz, jaką chciałbyś poprawić w aplikacji Ryśka. jesteś tutaj  37
  • 23. Jak pisać wspaniałe programy? Co przede wszystkim zmieniłbyś w aplikacji Ryśka? Oczywiste jest, że w aplikacji Ryśka występuje jakiś problem; jednak znacznie trudniej jest określić, od czego należy zacząć wprowadzanie poprawek. I wygląda na to, że istnieje wiele różnych opinii na ten temat: Hm… w swoich notatkach właściciel firmy wyraźnie pisze, że chciałby, żeby klienci mieli możliwość wyboru Spójrzcie na te wszystkie wielu instrumentów. Czy zatem łańcuchy znaków! To jest po metoda search() nie powinna prostu okropne… czy zamiast zwracać listy obiektów? nich nie możemy użyć stałych lub obiektów? Guitar serialNumber: String price: double Inventory builder: String model: String guitar: List type: String addGuitar(String, double, String, String, String, backWood: String String, String) topWood: String getGuitar(String): Guitar getSerialNumber(): String search(Guitar): Guitar getPrice(): double Ten projektant musi być setPrice(float) naprawdę kiepski. Klasy Guitar oraz getBuilder(): String Inventory w zbyt dużym stopniu zależą getModel(): String od siebie nawzajem. Nie potrafię sobie getType(): String wyobrazić, by to była architektura, getBackWood(): String na której mógłby bazować rozwój getTopWood(): String aplikacji. Musimy ją jakoś zmienić. Jerzy zajmuje się Julka zyskała reputację programowaniem osoby, która zawsze stosunkowo krótko, jednak oferuje klientom to, szczerze i głęboko wierzy czego chcą. w słuszność pisania kodu obiektowego. Franek zajmuje się programowan iem Co zrobiłbyś w pierwszej już od jakiegoś czasu i naprawd dobrze zna zasady projektowania ę kolejności? obiektowego i wzorce projektowe. 38 Rozdział 1.
  • 24. Dobrze zaprojektowane aplikacje są super Niby skąd mam wiedzieć, od czego należy zacząć? Mam wrażenie, że ilekroć zaczynam pracę nad nowym projektem, każdy ma inne zdanie odnośnie tego, co należy zrobić w pierwszej kolejności. Czasami zrobię coś dobrze, lecz czasami kończy się na tym, że muszę przerobić całą aplikację od początku, bo zacząłem w złym miejscu. A ja chcę jedynie pisać świetne oprogramowanie! A zatem, od czego powinienem zacząć pisanie aplikacji dla Ryśka? W jaki sposób można za każdym razem pisać dobre oprogramowanie? jesteś tutaj  39
  • 25. Ale co to znaczy „doskonałe oprogramowanie”? Chwileczkę… nie lubię się wtrącać, ale co to właściwie oznacza „wspaniałe oprogramowanie? To jakieś tajemnicze hasło, które rzuca się w rozmowach, by zrobić odpowiednie wrażenie? Dobre pytanie… na które można podać wiele różnych odpowiedzi: Programista dbający o dobro klienta odpowie: „Doskonałe oprogramowanie zawsze robi to, czego chce klient. A zatem, nawet jeśli klient wymyśli nowy sposób zastosowania takiego oprogramowania, to nie będzie ono działać niewłaściwie ani zwracać nieoczekiwanych wyników”. Podstawą tego podejścia jest zwrócenie największej uwagi na to, by użytkownik był zadowolony z działania aplikacji. Programista obiektowy odpowie: To podejście, koncentrujące uwagę na zagadnieniach projektu, „Doskonałe oprogramowanie to kod napisany obiektowo. pozwala na tworzenie kodu zoptymalizowanego Jest to zatem kod, w którym nie ma żadnych powtórzeń oraz pod kątem przyszłego w którym obiekty w bardzo dużym stopniu kontrolują swoje rozwoju aplikacji własne zachowanie. Takie oprogramowanie także łatwo i wielokrotnego używania jej elementów. rozbudować, gdyż jego projekt jest solidny i elastyczny”. Wykorzystuje ono wzorce projektowe oraz wielokrotnie sprawdzone techniki projektowania ze Nie jesteś całkiem pewny, co to i programowania Dobrzy programiści obiektowi zaws wszystko znaczy? Nie martw się… obiektowego. poszukują sposobów na to, by ich wszystkiego dowiesz się w kole jnych kod był jak najbardziej elastyczny. rozdziałach. Programista będący autorytetem w dziedzinie projektowania odpowie: „Oprogramowanie będzie doskonałe, jeśli podczas jego tworzenia zastosujemy wypróbowane i sprawdzone wzorce projektowe i zasady projektowania. Zachowałeś luźne powiązania pomiędzy obiektami i sprawiłeś, by kod był otwarty na rozszerzanie, lecz zamknięty na modyfikacje. To także powoduje, że łatwiejsze będzie wielokrotne wykorzystanie kodu, a dzięki temu nie będziesz go musiał przerabiać, chcąc wykorzystać fragmenty aplikacji w innych projektach”. 40 Rozdział 1.
  • 26. Dobrze zaprojektowane aplikacje są super Zaostrz ołówek A co dla Ciebie znaczy termin „doskonałe oprogramowanie”? Dowiedziałeś się już, co termin „doskonałe oprogramowanie” oznacza dla kilku różnych typów programistów. Zatem kto z nich ma rację? A może masz swoją własną definicję, która określa, co sprawia, że tworzona aplikacja będzie doskonała? Jeśli tak, to nadszedł czas, byś napisał własną definicję doskonałego oprogramowania: … ...a tu podaj co wedłu oje personalia Tu zapisz sw termin „doskonałe opr g Ciebie oznacza ogramowanie”: uważa, że: „ ” jesteś tutaj  41
  • 27. Doskonałe oprogramowanie spełnia potrzeby zarówno klienta, jak i programisty Doskonałe oprogramowanie… ma więcej niż jedną z wymienianych już cech Nie sposób określić, czym jest „doskonałe oprogramowanie”, przy użyciu jednej prostej definicji. W rzeczywistości wszystkie stwierdzenia różnych programistów dotyczące dobrego oprogramowania, podane na stronie 40, określają cechy, dzięki którym oprogramowanie można uznać za „doskonałe”. Przede wszystkim doskonałe oprogramowanie musi spełniać wymagania i oczekiwania klienta. Oprogramowanie musi robić to, czego klient od niego oczekuje. Podbij swoich klientów. Klienci uznają Twoje oprogramowanie za doskonałe, jeśli będzie ono robić to, czego od niego oczekują. Tworzenie oprogramowania, które działa we właściwy sposób, jest czymś wspaniałym. Co się jednak stanie w sytuacjach, kiedy takie oprogramowanie trzeba będzie rozbudować lub zastosować jego część w innym projekcie? Napisanie kodu, który działa zgodnie z oczekiwaniami klienta, to za mało; O rany, jeśli mój kod naprawdę równie ważne jest to, by przeszedł on próbę czasu. mógłby mieć te wszystkie cechy, to pisane przeze mnie aplikacje byłyby Poza tym, doskonałe oprogramowanie powinno być wspaniałe! Potrafię nawet zapisać te dobrze zaprojektowane, poprawnie napisane oraz musi wszystkie wymagania w kilku prostych zapewniać łatwość utrzymania, wielokrotnego stosowania punktach, które można by stosować i rozszerzania. we wszystkich projektach. Niech Twój kod będzie tak inteligentny jak Ty sam. Zarówno Ty, jak i Twoi współpracownicy sami uznacie, że oprogramowanie jest doskonałe, jeśli jego utrzymanie, rozszerzanie i wielokrotne stosowanie nie będą przysparzać większych problemów. 42 Rozdział 1.
  • 28. Dobrze zaprojektowane aplikacje są super Wspaniałe oprogramowanie w trzech prostych krokach Obecnie może Ci się wydawać, że to wcale nie jest takie łatwe. Jednak pokażemy, że analiza i projektowanie obiektowe, wraz z kilkoma prostymi zasadami, mogą na zawsze zmienić postać tworzonego przez Ciebie oprogramowania. 1. Upewnij się, że oprogramowanie jemy uwagę na klienci e. W tym kroku koncentru ŚCI powinieneś zapewnić, robi to, czego NO W PIERWSZEJ KOLEJ ić to, czego klient od niej że aplikacja będzie rob prac dużą rolę odgrywa pie oczekuje. Na tym eta oczekuje klient. przygotowanie wymagań odpowiedniej analizy . i przeprowadzenie 2. Zastosuj proste zasady projektowania obiektowego, by poprawić elastyczność oprogramowania. Kiedy oprogramowanie będ możesz odszukać w nim zie już działać, powtarzające się fragm i usunąć upewnić się, że zastos enty kodu oraz projektowania obiektow owałeś dobre techniki ego. 3. Staraj się, by projekt oprogramowania zapewniał łatwość jego utrzymania i pozwalał na jego Czy uzyskałeś dobrą aplikację obiektową, która robi to, co powinna? Nadszedł wielokrotne stosowanie. czas, by zastosować wzorce i zasady, dzięki którym upewnisz się, że Twoje oprogramowanie jest odpowiednio przygotowane do wieloletniego użytkowania. jesteś tutaj  43
  • 29. Stosowanie przedstawionych wcześniej kroków Pamiętasz Ryśka? Pamiętasz klientów, których stracił? Wypróbujmy nasze pomysły i założenie dotyczące tworzenia doskonałego oprogramowania i przekonajmy się, czy nadają się one do zastosowania w realnym świecie. Rysiek dostał program do wyszukiwania gitar, który nie działa poprawnie, i to Twoim zadaniem będzie jego poprawienie i dołożenie wszelkich starań, by stworzyć naprawdę wspaniałą aplikację. Przyjrzyjmy się jeszcze raz programowi, jakim obecnie dysponuje Rysiek, i zobaczmy, w czym tkwi problem: Oto nasz program testowy, który uwidocznił problemy public class FindGuitarTester { w działaniu narzędzia public static void main(String[] args) { do wyszukiwania Aplikacja Ryśk gitar pasujących do // Inicjalizacja magazynu gitar Ryśka powinna dopa a kryteriów podanych te wymaganiasować Inventory inventory = new Inventory(); … przez użytkownika. initializeInventory(inventory); Guitar whatEveLikes = new Guitar(””, 0, ”fender”, ”Stratocastor”, ...do tej gitary znajdującej się ”elektryczna”, ”olcha”, ”olcha”); w magazynie. Guitar guitar = inventory.search(whatEveLikes); if (guitar != null) { class inventory.addGuitar(”V95693”, FingGui- 1499.95, ”Fender”, ”Stratocastor”, tar { main() ”elektryczna”, ”olcha”, ”olcha”); } FindGuitarTester.java A zatem wykonajmy nasze trzy podane wcześniej kroki: 1. Upewnij się, że oprogramowanie robi to, czego oczekuje klient. Na razie nie zawr ac głowy stosowaniem aj sobie wzorców ani obiek w aplikacji technik programo towych skoncentruj się wania… Pamiętaj, że musimy , by 2. Zastosuj proste zasady projek- działała tak, jak na tym, by zacząć od zapewnienia go powinna. aplikacja robiła to, cze towania obiektowego, by poprawić nie oczekuje Rysiek… a cna elastyczność oprogramowania. ma wątpliwości, że obe o teg aplikacja nie spełnia warunku. 3. Staraj się, by projekt oprogramowania zapewniał łatwość jego utrzymania i pozwalał na jego wielokrotne stosowanie. 44 Rozdział 1.
  • 30. Dobrze zaprojektowane aplikacje są super Skoro zaczynamy od funkcjonalności, to sprawdźmy, co się dzieje z tą niedziałającą metodą search(). Wygląda na to, że w magazynie Ryśka nazwa producenta jest zapisana małymi literami, a w wymaganiach klienta nazwa „Fender Skorzystajmy zaczyna się z wielkiej litery. A zatem w metodzie search() z drobnej pomocy musimy zastosować wyszukiwanie, które nie będzie naszych znajomy ch programistów. uwzględniać wielkości liter. Franek: Oczywiście, to by rozwiązało bieżące problemy Ryśka, niemniej jednak uważam, że istnieje lepszy sposób zapewnienia poprawnego działania aplikacji niż wywoływanie metody toLowerCase() we wszystkich miejscach, gdzie są porównywane łańcuchy znaków. Jerzy: Właśnie, o tym samym pomyślałem. Wydaje mi się, że to całe porównywanie łańcuchów znaków to nie jest najlepszy pomysł. Czy nie moglibyśmy zastosować jakichś stałych lub jakiegoś typu wyliczeniowego do określania producentów gitar oraz gatunków drewna? Julka: Panowie, wybiegacie myślami zdecydowanie zbyt daleko. Krok 1. miał polegać na poprawieniu aplikacji w taki sposób, by robiła to, czego od niej oczekuje klient. Sądziłam, że na tym etapie prac nie będziemy zajmowali się zagadnieniami związanymi z projektem. Franek: Cóż, to prawda; rozumiem, że mamy się skoncentrować na kliencie. Ale możemy przynajmniej zastanowić się, jak inteligentnie Jerzy rozwiązać aktualnie występujące problemy, nieprawdaż? Chodzi mi o to, Franek Julka by nie tworzyć kolejnych problemów, które w przyszłości znowu będziemy musieli rozwiązywać. Julka: Hmm… tak, całkiem słusznie. Na pewno nie chcemy, by zaproponowane przez nas rozwiązanie bieżących problemów powodowało pojawienie się nowych problemów projektowych. Jednak pomimo to na razie nie będziemy zajmowali się innymi fragmentami aplikacji, dobra? Franek: Dobra. Możemy ograniczyć się do usunięcia tych wszystkich łańcuchów znaków, porównywania łańcuchów znaków i problemów Rozwiązując związanych z wielkościami liter. Jerzy: Właśnie. Jeśli zastosujemy typy wyliczeniowe, to zyskamy pewność, istniejące problemy, że podczas określania producenta gitary, typu drewna oraz rodzaju instrumentu będą używane wyłącznie prawidłowe wartości. W ten sposób nie twórz nowych. zagwarantujemy, że Rysiek będzie mógł znajdować gitary pasujące do wymagań i oczekiwań klientów. Julka: A jednocześnie poprawimy nieco projekt samej aplikacji… super! A zatem — do roboty, panowie. jesteś tutaj  45
  • 31. Krok 1: Zaspokojenie potrzeb klienta Eliminacja porównywania łańcuchów znaków Pierwszą poprawką, jaką możemy wprowadzić w aplikacji Ryśka, jest usunięcie porównywania łańcuchów znaków. Choć można by zastosować metodę toLowerCase(), by rozwiązać problem z porównywaniem liter różnych wielkości, to jednak postaramy się w ogóle wyeliminować porównywanie łańcuchów znaków: To wszys tk wyliczenio o są Javy — e we typy public enum Type { działają p num — które enum stałych. odobnie do ACOUSTIC, ELECTRIC; Type { Każdy z typów to- public String toString() { String() wyliczeniowych } zastąpi jedną switch(this) { z właściwości case ACOUSTIC: return ”akustyczna”; klasy Guitar, Type.java która jest case ELECTRIC: return ”elektryczna”; public enum Builder { standardowa dla enum default: return ”nieokreślona”; wszystkich jej FENDER, MARTIN, GIBSON, COLLINGS, OLSON, RYAN, PRS, ANY; Builder{ obiektów. } to- public String toString() { String() } } switch(this) { } case FENDER: return „Fender”; Builder.java case MARTIN: return „Martin”; public enum Wood { case GIBSON: return „Gibson”; INDIAN_ROSEWOOD, BRAZILIAN_ROSEWOOD, MAHOGANY, MAPLE, case COLLINGS: return „Collings”; HEBAN, CEDAR, OAK, ALDER, SITKA; y się Do tych wartości możemzapisu public String toString() { odwoływać przy użyciu r. Wood.SITKA lub Builde uniknąć u switch(this) { GIBSON, a dzięki tem a case INDIAN_ROSEWOOD: return ”palisander indyjski”; stosow ania porównywani łańcuchów znaków. case BRAZILIAN_ROSEWOOD: return ”palisander brazylijski”; Jedną ze wspania case MAHOGANY: return ”mahoń”; łyc wyliczeniowych jes h zalet typów case MAPLE: return ”klon”; ograniczenia pote t możliwość przekazywanych ncjalnych wartości case EBONY: return ”heban”; enum zatem już nigdy w wywołaniach metod… Wood{ to- przejmować się nie będziemy musieli String() pr typograficznymi ostymi błędami } lub problemami z wielkością liter . Wood.java Nie ma niemądrych pytań P.: Nigdy wcześniej nie spotkałem się w języku Java ze Typy wyliczeniowe pozwalają na podanie nazwy typu, na przykład Wood, oraz listy wartości, jakie mogą być stosowane w ramach tego słowem kluczowym enum. Co to takiego? typu (na przykład: SITKA, ALDER bądź CEDAR). A zatem odwołanie O.: Pozwala ono na definiowanie typów wyliczeniowych. Ten typ do konkretnej wartości ma postać: Wood.SITKA. danych jest dostępny także w języku C, C++ oraz w Javie w wersji 5.0 i kolejnych. Co więcej, pojawi się także w wersji 6.0 języka Perl. P A dlaczego typy wyliczeniowe przydadzą się nam .: w aplikacji Ryśka? 46 Rozdział 1.
  • 32. Dobrze zaprojektowane aplikacje są super public class FindGuitarTester { Te wszystk ie znaków mog łańcuchy public static void main(String[] args) { zastąpić w liśmy już ar // Inicjalizacja zawartości magazynu gitar Ryśka wyliczeniow tościami typów ych. Inventory inventory = new Inventory(); Jedyny łańcuch znaków, jaki nam initializeInventory(inventory); pozostał i jaki Guitar whatEveLikes = new Guitar(“”, 0, Builder.FENDER, sprawdzamy, “Stratocastor”, Type.ELECTRIC, Wood.ALDER, Wood.ALDER); określa class model gitary; Guitar guitar = inventory.search(whatEveLikes); FindGui- zostawiliśmy if (guitar != null) { tar { go, gdyż zbiór } main() dostępnych modeli nie jest ograniczony, FindGuitarTester.java w odróżnieniu od producentów gitar oraz public List search(Guitar searchGuitar) { gatunków drewna for (Iterator i = guitars.iterator(); i.hasNext(); ) { używanych do ich wytwarzania. Guitar guitar = (Guitar)i.next(); // Ignorujemy numer seryjny, bo jest unikalny // Ignorujemy cenę, gdyż jest unikalna if (searchGuitar.getBuilder() != guitar.getBuilder()) Jedyną właściwością, continue; w jakiej Wygląda na String model = searchGuitar.getModel().toLowerCase(); musimy zadbać to, że nic się o wielkość nie zmieniło, if ((model != null) (!model.equals(“”)) liter, jest jednak dzięki (!model.equals(guitar.getModel().toLowerCase()))) nazwa modelu zastosowaniu continue; gitary — ona typów wciąż jest wyliczeniowych if (searchGuitar.getType() != guitar.getType()) zwyczajnym nie musimy już continue; łańcuchem martwić się, że if (searchGuitar.getBackWood() != guitar.getBackWood()) znaków. metoda zwróci niewłaściwe continue; wyniki ze if (searchGuitar.getTopWood() != guitar.getTopWood()) względu na continue; różnice w wielkości liter return guitar; użytych w obu } łańcuchach. return null; class } Inven- tory { search() } Inventory.java O.: Bardzo dużą zaletą typów wyliczeniowych jest to, iż P Używam starszej wersji języka Java. Czy to oznacza, .: zabezpieczają one metody, w których są używane przed że mam kolejny problem? przekazaniem wartości niezdefiniowanych w danym typie. A zatem, jeśli tylko wartość typu wyliczeniowego zostanie O.: Nie, nie będziesz mieć żadnych problemów. Zajrzyj do plików błędnie zapisana, kompilator wygeneruje błąd. Jak widać, typy z serwera FTP Wydawnictwa Helion — zamieściliśmy tam specjalną wyliczeniowe są doskonałym sposobem nie tylko na zapewnienie wersję aplikacji Ryśka, w której nie są używane typy wyliczeniowe bezpieczeństwa typów, lecz także bezpieczeństwa wartości — nie i która z powodzeniem będzie działać także w starszych wersjach sposób użyć niepoprawnych danych, jeśli można je wybrać tylko języka Java. z ograniczonego zakresu lub zbioru ściśle określonych wartości. jesteś tutaj  47
  • 33. Słabe aplikacje łatwo się psują Teraz metoda ra Przyjrzyjmy się ogólnej postaci aplikacji Ryśka: addGuitar() pobie w pó kilka wartości tya nie wyliczeniowych, lub łańcuchów znaków stałych liczbowych. Zastąpiliśmy większość Guitar Inventory łańcuchów znaków zapisywanych we serialNumber: String guitar: List właściwościach price: double Choć wygląda na obiektów Guitar wartościami typów addGuitar(String, double, Builder, String, Type to, że w metodzie builder: Builder search() nic się nie wyliczeniowych. Wood, Wood) zmieniło, to jednak model: String getGuitar(String): Guitar teraz używamy wartości type: Type typów wyliczeniowych, search(Guitar): Guitar backWood: Wood dzięki czemu mamy topWood: Wood pewność, że nie pojawią się żadne problemy związane z błędami getSerialNumber(): String typograficznymi lub Numer seryjny getPrice(): double Builder zastosowaniem liter wciąż jest wartością setPrice(float) Type o różnej wielkości. unikalną; poza tym toString(): String getBuilder(): Builder także właściwość określającą nazwę getModel(): String toString(): String Wood modelu gitary getType(): Type pozostawiliśmy toString(): String jako właściwość getBackWood(): Wood Oto nasze typy łańcuchową, gdyż getTopWood(): Wood wyliczeniowe. różnych modeli mogą być tysiące… a to o wiele za dużo, by można je zapisać Klasa Guitar używa tych w formie typu typów wyliczeniowych do wyliczeniowego. reprezentacji danych, dzięki czemu można wyeliminować błędy związane z niewłaściwym sposobem zapisu łańcuchów znaków. A zatem co tak naprawdę zrobiliśmy? Znacznie zbliżyliśmy się do zakończenia pierwszego 1. Upewnij się, że z trzech kroków prowadzących do tworzenia doskonałego oprogramowania. Problemy Ryśka z odnajdywaniem oprogramowanie w magazynie gitar spełniających zadane kryteria to już przeszłość. robi to, czego Co więcej, jednocześnie sprawiliśmy, że aplikacja Ryśka jest bardziej solidna i mniej wrażliwa. Nie będzie już tak łatwo przysparzać problemów, gdyż oczekuje klient. poprzez zastosowanie typów wyliczeniowych poprawiliśmy ją zarówno pod względem bezpieczeństwa typów, jak i bezpieczeństwa wartości. Z punktu widzenia Ryśka oznacza to mniej problemów, a z naszego — łatwiejsze utrzymanie aplikacji. Kod, który nie jest wra i podatny na awarie, żliwy zaz określa się jako solidn wyczaj y. 48 Rozdział 1.
  • 34. Dobrze zaprojektowane aplikacje są super Nie ma Zaostrz ołówek niemądrych pytań Wykonaj krok 1. w swoim własnym projekcie. P A zatem, pracując nad pierwszym krokiem do .: tworzenia doskonałego oprogramowania, można Czas przekonać się, czy będziesz w stanie sprostać wymaganiom wprowadzać nieznaczne zmiany w projekcie swoich klientów. W pustych wierszach poniżej wpisz krótki opis aplikacji? jakiegoś projektu, nad którym aktualnie pracujesz (możesz także opisać jakiś projekt, który niedawno ukończyłeś): O.: Tak, o ile tylko cały czas będziesz się koncentrował głównie na potrzebach użytkownika. Chodzi o to, że chcesz, by wszystkie podstawowe cechy i możliwości aplikacji zostały poprawnie zaimplementowane, zanim Teraz, w kolejnych pustych wierszach, zapisz pierwszą rzecz, jaką zaczniesz wprowadzać poważne zmiany w jej projekcie. zrobiłeś, rozpoczynając prace nad tym projektem. Czy miało to Niemniej jednak nic nie stoi na przeszkodzie, byś stosował cokolwiek wspólnego z upewnieniem się, że aplikacja będzie działać dobre praktyki i techniki obiektowe także podczas pracy zgodnie z oczekiwaniami i wymaganiami użytkownika? nad funkcjonalnością aplikacji, tak by mieć pewność, że od samego początku będzie ona dobrze zaprojektowana. P.: Czy diagram przedstawiony na stronie 48 to diagram klas? Czy też jest to kilka diagramów — w końcu przedstawia więcej niż jedną klasę? Jeśli rozpoczynając prace nad projektem, skupisz się na czymś innym niż zaspokojenie potrzeb i oczekiwań użytkownika, to zastanów się, czym mogłoby się różnić Twoje podejście, gdybyś wiedział o trzech O.: Jest to diagram klas; na takim diagramie można krokach pozwalających na tworzenie wspaniałego oprogramowania. bowiem zamieścić większą liczbę klas. Prawdę mówiąc, Co by się w takim przypadku zmieniło? Czy sądzisz, że Twoja aplikacja diagramy klas mogą przedstawiać znacznie więcej byłaby dzięki temu lepsza, niż jest obecnie, a może gorsza? informacji na temat klasy, niż pokazaliśmy w diagramach zmieszczonych w tej książce. W kilku kolejnych rozdziałach będziemy dodawali do naszych diagramów kolejne informacje o klasach. P A zatem jesteśmy gotowi, by przejść do .: kroku 2. i rozpocząć stosowanie zasad i technik projektowania obiektowego? Prawda? O.: Niezupełnie. Można wskazać jeszcze kilka innych rzeczy, dzięki którym powinniśmy pomóc Ryśkowi, zanim będziemy gotowi do rozpoczęcia analizy programu i przystąpimy do jego poprawiania. Pamiętaj, że naszym podstawowym zadaniem jest zaspokojenie potrzeb użytkownika; dopiero kiedy to zrobimy, będziemy mogli zająć się poprawianiem projektu naszej aplikacji. jesteś tutaj  49
  • 35. Podobne, ale inne A ja myślałem, że moja nowa aplikacja jest doskonała… Dopiero potem uświadomiłem sobie, że w magazynie mam dwie gitary, które doskonale spełniały oczekiwania Ewy. Czy moglibyście zmienić wyszukiwanie w taki sposób, by zwracało oba odnalezione instrumenty? Rysiek jest bardzo zad ow z wprowadzonych modyf olony naprawdę pilnie jest ikacji, jednak mu możliwość, by aplikacja potrzebna wszystkie odszukane zwracała gitary pasujące do wymagań użytkown jedną z nich. ika, a nie tylko Rysiek naprawdę chciałby, żeby Ewa mogła obejrzeć obie gitary. inventory.addGuitar(”V95693”, 1499.95, Builder.FENDER, ”Stratocastor”, Type.ELECTRIC, Wood.ALDER, Wood.ALDER); Obie te gitary są inventory.addGuitar(”V95612”, niemal identyczne. Różnią się jedynie 1549.95, Builder.FENDER, numerem seryjnym ”Stratocastor”, Type.ELECTRIC, i ceną. Wood.ALDER, Wood.ALDER); 50 Rozdział 1.
  • 36. Dobrze zaprojektowane aplikacje są super Klienci Ryśka chcą mieć wybór Rysiek wymyślił nowe wymaganie dla swojej aplikacji: chciałby, żeby narzędzie wyszukiwawcze zwracało wszystkie gitary znajdujące się w magazynie i spełniające wymagania klienta, a nie tylko pierwszą z nich. Inventory Chcemy, by w przypadku gdy Rysiek dysponuje większą liczbą guitar: List instrumentów spełniających wymagania podane przez klienta, addGuitar(String, double, Builder, String, Type metoda search() mogła zwracać Wood, Wood) wiele obiektów Guitar. getGuitar(String): Guitar search(Guitar): List Magnesiki z kodem Kontynuujemy pracę nad pierwszym z trzech kroków do stworzenia wspaniałej aplikacji i skupiamy uwagę na tym, by nasza aplikacja zaczęła działać poprawnie. Poniżej został przedstawiony kod metody search() wyszukującej gitary w magazynie Ryśka; umieściliśmy w nim kilka pustych miejsc, które Ty musisz wypełnić. Użyj do tego celu magnesików z kodem pokazanych u dołu strony. Pamiętaj, że Twoim zadaniem jest takie zmodyfikowanie kodu metody search(), by zwracała ona wszystkie odnalezione w magazynie gitary spełniające kryteria podane przez użytkownika. public ______ search(Guitar searchGuitar) { ______ ______________ = new __________(); for (Iterator i = guitars.iterator(); i.hasNext(); ) { Guitar guitar = (Guitar)i.next(); // Ignorujemy numer seryjny, bo jest unikalny // Ignorujemy cenę, gdyż jest unikalna if (searchGuitar.getBuilder() != guitar.getBuilder()) continue; String model = searchGuitar.getModel().toLowerCase(); if ((model != null) (!model.equals(””)) (!model.equals(guitar.getModel().toLowerCase()))) continue; if (searchGuitar.getType() != guitar.getType()) continue; if (searchGuitar.getBackWood() != guitar.getBackWood()) continue; if (searchGuitar.getTopWood() != guitar.getTopWood()) continue; _____________._____(_____); } return ______________; } ArrayList LinkedList List matchingGuitars guitar ArrayList List List matchingGuitars ngGuitars guitar List LinkedList add matchi matchingGuitars jesteś tutaj  51
  • 37. Utrzymanie, projekt i wymagania Magnesiki z kodem Kontynuujemy pracę nad pierwszym z trzech kroków do stworzenia wspaniałej aplikacji i koncentrujemy uwagę na tym, by nasza aplikacja zaczęła działać poprawnie. Poniżej został przedstawiony kod metody search() wyszukującej gitary w magazynie Ryśka; umieściliśmy w nim kilka pustych miejsc, które Ty musisz wypełnić. Użyj do tego celu magnesików z kodem, pokazanych u dołu strony. Pamiętaj, że Twoim zadaniem jest takie zmodyfikowanie kodu metody search(), by zwracała ona wszystkie odnalezione w magazynie gitary spełniające kryteria podane przez użytkownika. public List search(Guitar List searchGuitar) { List List matchingGuitars = matchingGuitars new LinkedList(); LinkedList m miejscu W praktyce w ty zarówno for (Iterator i = guitars.iterator(); i.hasNext(); ) { można zastosować , jak Guitar guitar = (Guitar)i.next(); klasę LinkedList spełniłyby ie // Ignorujemy numer seryjny, bo jest unikalny i ArrayList… ob swoje zadanie. // Ignorujemy cenę, gdyż jest unikalna if (searchGuitar.getBuilder() != guitar.getBuilder()) continue; String model = searchGuitar.getModel().toLowerCase(); Gitary pasują if ((model != null) (!model.equals(„”)) do podanych ce wymagań zost (!model.equals(guitar.getModel().toLowerCase()))) dodane do lis ają continue; wszystkich ty if (searchGuitar.getType() != guitar.getType()) instrumentów którymi klient, continue; może być if (searchGuitar.getBackWood() != guitar.getBackWood()) zainteresowan continue; y. if (searchGuitar.getTopWood() != guitar.getTopWood()) ArrayList continue; List guitar ArrayList matchingGuitars . add matchingGuitars guitar add (______________); chingGuitars } LinkedList mat List return matchingGuitars;ars matchingGuit } Niewykorzystane magnesiki. Nie ma niemądrych pytań P.: A zatem pierwszego etapu prac P A dlaczego zakończenie tego etapu .: P Wydaje mi się, że zwracacie .: nie można uznać za zakończony aż do prac przed rozpoczęciem kolejnego jest nadmierną uwagę na ten „krok 1.” momentu, gdy aplikacja będzie działać takie ważne? i „krok 2.”. A co, jeśli ja projektuję swoje tak, jak sobie tego życzy klient? aplikacje w inny sposób? O.: Zapewnienie poprawnego działania O.: Właśnie. Powinieneś upewnić się, że oprogramowania wymaga zazwyczaj dokonania O.: Nie musisz ściśle trzymać się podawanych aplikacja działa tak, jak powinna, zanim w nim bardzo wielu zmian. Wprowadzanie przez nas informacji o każdym z kroków. zajmiesz się zastosowaniem w niej wzorców zbyt wielu zmian w strukturze aplikacji przed Niemniej jednak stanowią one prostą sekwencję projektowych bądź nim zaczniesz wprowadzać zagwarantowaniem poprawnego działania czynności, której wykonanie gwarantuje, że jakiekolwiek poważniejsze zmiany w jej przynajmniej jej podstawowych możliwości tworzone oprogramowanie będzie działać strukturze. funkcjonalnych może się okazać stratą czasu zgodnie z oczekiwaniami, będzie dobrze i wysiłku, gdyż struktura programu niekiedy zaprojektowane i nie wystąpią problemy w jego ulega poważnym zmianom podczas wielokrotnym użytkowaniu. Jeśli w inny sposób implementacji jego kolejnych możliwości. zrealizujesz te same cele, to super! 52 Rozdział 1.
  • 38. Dobrze zaprojektowane aplikacje są super Test Wielokrotnie pisaliśmy o potrzebie uzyskania od klienta informacji o wymaganiach stawianych tworzonemu oprogramowaniu; teraz jednak nadszedł czas, by przekonać się, czy te wymagania są dobrze realizowane przez nasz kod. Sprawdźmy zatem, czy nasza aplikacja działa tak, jakby sobie tego życzył Rysiek: Oto program testowy, Używamy w zaktualizowany public class FindGuitarTester { wyliczeniow nim typów tak, by mógł nie będzie ych. Tym razem korzystać z nowej public static void main(String[] args) { problemów więc żadnych wersji narzędzia // Inicjalizacja zawartości magazynu gitar Ryśka niewłaściw spowodowanych wyszukującego ie W tej wersji Inventory inventory = new Inventory(); łańcuchami zapisanymi gitary znaków! programu w magazynie initializeInventory(inventory); testowego Ryśka. musimy przejrzeć całą listę Guitar whatEveLikes = new Guitar(””, 0, Builder.FENDER, “Stratocastor”, instrumentów Type.ELECTRIC, Wood.ALDER, Wood.ALDER); zwróconą przez narzędzie List matchingGuitars = inventory.search(whatEveLikes); wyszukujące. if (!matchingGuitars.isEmpty()) { System.out.println(“Ewo, może spodobają Ci się następujące gitary:”); for (Iterator i = matchingGuitars.iterator(); i.hasNext(); ) { Teraz Guitar guitar = (Guitar)i.next(); otrzymujemy System.out.println(“ Mamy w magazynie gitarę “ + całą listę guitar.getBuilder() + “ model “ + guitar.getModel() + “, jest “ + gitar, które spełniają “to gitara” + guitar.getType() + “ :n “ + wymagania guitar.getBackWood() + “ - tył i boki,n “ + określone guitar.getTopWood() + “ - góra.n Możesz ją mieć za “ + przez klienta. guitar.getPrice() + “PLN!n ----”); } } else { System.out.println(“Przykro mi, Ewo, nie znalazłem nic dla Ciebie.”); } class } FindGui- tar { main() } FindGuitarTester.java O tak! Teraz program działa dokładnie tak, jak tego chciałem. Wszystko zadziałało tak Ewa mogła obejrzeć kilk , jak powinno! jej gitar, a klienci na a poleconych kupować instrumenty powrót zaczęli w sklepie Ryśka. jesteś tutaj  53
  • 39. Zastosowanie zasad obiektowych Wróćmy do naszych trzech kroków Teraz, kiedy nasza aplikacja działa już tak, jak Rysiek sobie tego życzył, możemy zacząć stosować zasady projektowania obiektowego, by poprawić jej elastyczność i zapewnić, że będzie dobrze zaprojektowana. Teraz, kiedy aplikacja działa już tak, jak chciał Rysiek, ten krok możemy uznać za zakończony. 1. Upewnij się, że oprogramowanie robi to, czego oczekuje klient. Zatem w końcu nadszedł czas, gdy możemy upewnić się, że w aplikacji nie ma powtarzających się fragmentów kodu oraz że wszystkie używane w niej obiekty zostały dobrze zaprojektowane. 2. Zastosuj proste zasady projektowania obiektowego, by poprawić elastyczność oprogramowania. y W tym kroku analizujem dzi ałający program gmenty, i sprawdzamy, czy fra scalone z jakich się składa, są w sensowny sposób. 3. Staraj się, by projekt oprogramowania zapewniał łatwość jego utrzymania i pozwalał na jego wielokrotne stosowanie. 54 Rozdział 1.
  • 40. Dobrze zaprojektowane aplikacje są super Szukamy problemów Przyjrzyjmy się nieco dokładniej naszemu narzędziu do wyszukiwania gitar i sprawdźmy, czy uda się nam znaleźć jakieś problemy, które moglibyśmy rozwiązać przy wykorzystaniu prostych zasad projektowania obiektowego. Zacznijmy od przeanalizowania sposobu działania metody search() klasy Inventory. Szukam akustycznej gitary firmy Martin, masz może jakąś, Ryśku? Klient podaje zbiór kryteriów, jakie musi spełniać jego wymarzona gitara; jest on przekazywany w formie obiektu Guitar. null, null Builder.MARTIN Type.ACUSTIC „OM-18” Wood.MAHOGANY Do metody search() gania Wood.OAK przekazywane są wyma, Guitar określ one przez klienta je a jej wywołanie powodu nia wa Klient nie podaje ani rozpoczęcie przeszuki ceny poszukiwanej class magazynu Ryśka. gitary, ani Inven- Każda gitara tory { numeru seryjnego znajdująca się — informacje te są search () Search()} w magazynie bowiem unikalne Ryśka jest dla każdej gitary. porównywana Podawane są jedynie Inventory.java ze specyfikacją te wymagania, na użytkownika, podstawie których przekazaną należy odszukać w formie obiektu gitarę. Guitar. WYTĘŻ Guitar Guitar umYsł Guitar Czy coś tu jest nie w porządku? Jakie problemy mogą wystąpić w narzędziu Guitar wyszukiwawczym aplikacji Ryśka? Oto obiekty Guitar reprezentujące funkcjami. Czy zauważyłeś coś dziwnego? wszystkie gitary dostępne wszystkich obiektów i porównaj je z ich w magazynie Ryśka; zawierają one numer seryjny, cenę oraz Podpowiedź: Przyjrzyj się nazwom wszelkie inne informacje o poszczególnych instrumentach. jesteś tutaj  55
  • 41. Analiza metody search() Analiza metody search() Poświęćmy nieco czasu na dokładniejsze przeanalizowanie tego, co się dzieje w metodzie search() klasy Inventory. Zanim przyjrzymy się samemu kodowi tej metody, zastanówmy się nad tym, co ona powinna robić. 1 Klient podaje swoje wymagania dotyczące poszukiwanej gitary. Klient może wskazać jedynie ogólne cechy Każdy z klientów przychodzących do sklepu Ryśka chciałby, żeby instrumentu. Dlatego też poszukiwana gitara posiadała określone cechy: gatunek używanego klienci nigdy nie podają ani ceny, ani numeru drewna, typ gitary bądź też określony model, określonego producenta. seryjnego poszukiwanej Klienci podają te cechy Ryśkowi, który z kolei przekazuje je do narzędzia gitary. przeszukującego magazyn. 2 Narzędzie wyszukujące przegląda zawartość magazynu. Kiedy narzędzie wyszukujące wie, czego chce klient, rozpoczyna wykonywanie pętli, w której sprawdzane są wszystkie gitary znajdujące się w magazynie. kie e właściwości, ta Wszystkie ogóln i drewna, producent 3 Każda gitara jest analizowana pod kątem zgodności jak uż yte gatunk e y, są porównywan z wymaganiami określonymi przez klienta. czy też typ gitar reślonymi przez z wymagania mi ok Dla każdej gitary znajdującej się w magazynie narzędzie wyszukujące klienta. sprawdza, czy spełnia ona wymagania określone przez klienta. Jeśli wymagania są spełnione, to gitara jest dodawana do listy pasujących instrumentów. 4 Klientowi przedstawiana jest lista wszystkich instrumentów spełniających zadane kryteria. W końcu lista pasujących instrumentów jest wyświetlana, a Rysiek może ją przedstawić klientowi. Klient może wybrać odpowiadający mu instrument, a Rysiek — zainkasować zapłatę. Spróbuj słownie opisać rozwiązywany problem, aby upewnić się, że projekt rozwiązania odpowiada planowanym możliwościom funkcjonalnym aplikacji. 56 Rozdział 1.
  • 42. Dobrze zaprojektowane aplikacje są super Tajemnica obiektów W lepiej zaprojektowanych dzielnicach Obiektowa obiekty bardzo poważnie i precyzyjnie podchodzą do swoich zadań. Każdy z nich o niedopasowanych jest zainteresowany tylko i wyłącznie swoimi zadaniami i stara się je wykonywać jak najlepiej. Nie ma niczego, co dobrze zaprojektowane typach obiekty nie cierpiałyby bardziej niż wykonywanie zadań, do których tak naprawdę nie zostały przeznaczone. Niestety, jak udało się nam zauważyć, właśnie taka sytuacja występuje w narzędziu służącym do przeszukiwania magazynu gitar Ryśka: w pewnym jego miejscu pewien obiekt jest używany do wykonywania operacji, których tak naprawdę nie powinien wykonywać. Twoim zadaniem jest rozwiązanie tej zagadki i określenie, w jaki sposób można poprawić aplikację Ryśka. Aby ułatwić Ci zadanie, poniżej podaliśmy kilka przydatnych wskazówek, które pomogą Ci rozpocząć poszukiwania niepasującego typu obiektów: 1. Zadania wykonywane przez obiekty powinny pasować do nazwy tych obiektów. Jeśli pewien obiekt należy do klasy Odrzutowiec, to prawdopodobnie powinien on mieć metody ląduj() oraz startuj(), jednak nie powinien udostępniać metody kontrolujBilety() — bo kontrola biletów jest zadaniem należącym do jakiegoś innego obiektu. 2. Każdy obiekt powinien reprezentować jedno pojęcie. Nie chcesz używać obiektów realizujących dwa lub trzy różne obowiązki. Unikaj obiektów, które będą reprezentować „prawdziwą” kwaczącą kaczkę, żółtą, plastykową kaczuszkę do kąpieli oraz osobę, która schyla głowę, by uniknąć trafienia piłką na meczu w baseball. 3. Nieużywane właściwości obiektów są podejrzane. STOP! Spróbuj rozwiązać tę Jeśli okaże się, że właściwości w obiekcie bardzo często mają wartości zagadkę, zanim zaczniesz czytać null lub w ogóle nie są używane, to może to oznaczać, że obiekt następną stronę. wykonuje więcej niż jedno zadanie. Skoro jakaś właściwość obiektu bardzo rzadko ma jakieś wartości, to dlaczego stanowi ona część tego obiektu? Czy nie lepiej byłoby ją umieścić w jakimś innym obiekcie, zawierającym tylko podzbiór właściwości oryginalnego obiektu? Jak myślisz, jaki typ obiektu jest nieprawidłowo używany w aplikacji Ryśka? Zapisz odpowiedź poniżej. A jak myślisz, co należy zrobić, by rozwiązać ten problem? Jakie zmiany w aplikacji byś wprowadził? jesteś tutaj  57
  • 43. Powtarzający się kod to coś okropnego No wiecie… Klienci Ryśka tak naprawdę nie przekazują mu obiektów klasy Guitar… Chodzi mi o to, że w istocie nie dają mu gitar, które on następnie porównuje z instrumentami w magazynie. Franek: Fakt — masz rację. Nie pomyślałem o tym wcześniej. Julka: No i co z tego? Zastosowanie obiektu Guitar znacznie ułatwia wykonywanie porównywania w metodzie search(). Jerzy: Nie bardziej niż zastosowanie jakiegokolwiek innego obiektu. Spójrzcie: if (searchGuitar.getBuilder() != guitar.getBuilder()) { To niewielki fragment continue; metody search() klasy } Inventory. Jerzy: Tak naprawdę nie ma znaczenia, jakiego obiektu tu używamy, o ile tylko będziemy w stanie określić, na jakich cechach gitary zależy klientowi. Franek: Tak… Myślę, że powinniśmy stworzyć nowy typ obiektów, który przechowywałby jedynie takie specyfikacje, jakie klient chce przekazać do metody search(). W takim przypadku do tej metody nie byłyby przekazywane obiekty Guitar, co, swoją drogą, nigdy mi się nie wydawało szczególnie sensowne. Julka: Jednak czy takie rozwiązanie nie spowoduje powielania kodu w aplikacji? Jeśli Hermetyzacja stworzymy obiekt, w którym będzie można zapisać wszystkie specyfikacje podawane przez klienta, a oprócz tego mamy obiekt Guitar ze wszystkimi jego właściwościami pozwala podzielić i metodami, to w efekcie uzyskamy dwie metody getBuilder(), dwie metody getWood() i tak dalej… To chyba niezbyt dobrze… aplikację na Franek: W takim razie dlaczego nie hermetyzować tych właściwości i nie przenieść logiczne części. z klasy Guitar do jakiejś innej, nowej? Jerzy: O rany… Rozumiałem wszystko do chwili, gdy powiedziałeś „hermetyzować”. Myślałem, że hermetyzacja polega na zdefiniowaniu zmiennych jako prywatne, tak by nikt nie mógł ich używać w niewłaściwy sposób. Ale co to ma wspólnego szy Po raz pierw z terminem z właściwościami gitary? spotkałeś się ? Zajrzyj hermetyzacja przeczytaj Franek: Hermetyzacja to także podział aplikacji na logiczne fragmenty oraz do dodatku B, acje zachowanie ich separacji. A zatem, podobnie jak dane w klasie separujemy od działania rm skrócone info i dopiero o Obiektowie lektury pozostałych fragmentów aplikacji, tak i same właściwości gitary możemy oddzielić od potem wróć do samego obiektu Guitar. u. tego rozdział Julka: Czy w takim przypadku w klasie Guitar pozostałaby jedynie zmienna zawierająca referencję do obiektu nowego typu, który gromadziłby wszystkie informacje o gitarze? Franek: Dokładnie! W ten sposób udałoby się nam hermetyzować właściwości gitary od obiektu Guitar i umieścić je w osobnym obiekcie. Spójrzcie, moglibyśmy zrobić coś takiego… 58 Rozdział 1.
  • 44. Dobrze zaprojektowane aplikacje są super Zaostrz ołówek Utworzenie obiektu GuitarSpec Poniżej przedstawiliśmy diagram klasy Guitar oraz nowej klasy o nazwie GuitarSpec, o której przed chwilą rozmawiali Franek, Julka i Jerzy. Twoim zadaniem jest dodanie do klasy GuitarSpec wszystkich właściwości i metod, które według Ciebie będą w niej niezbędne. Następnie wykreśl z klasy Guitar wszystkie właściwości i metody, które nie będą już w niej potrzebne. W końcu w diagramie klasy Guitar pozostawimy Ci nieco wolnego miejsca, na wypadek gdybyś doszedł do wniosku, że będziesz musiał dodać do niej jakieś nowe właściwości lub metody. Guitar GuitarSpec serialNumber: String price: double builder: Builder model: String Usuń z klasy type: Type Guitar wszystko, co według Ciebie backWood: Wood powinno się znaleźć w klasie topWood: Wood GuitarSpec. getSerialNumber(): String getPrice(): double Do klasy Guitar możesz dodać setPrice(float) nowe właściwości i metody, jeśli getBuilder(): Builder uznasz to za właściwe. getModel(): String getType(): Type getBackWood(): Wood getTopWood(): Wood * Jeśli będziesz mieć problemy, to pomyśl o wspólnych elementach, które występują w obiekcie Guitar i które są przekazywane w wywołaniu metody search(). jesteś tutaj  59
  • 45. Hermetyzacja tego, co może być różne Zaostrz ołówek Utworzenie obiektu GuitarSpec Rozwiązanie Poniżej przedstawiliśmy diagram klasy Guitar oraz nowej klasy o nazwie GuitarSpec, o której przed chwilą rozmawiali Franek, Julka i Jerzy. Twoim zadaniem jest dodanie do klasy GuitarSpec wszystkich właściwości i metod, które według Ciebie będą w niej niezbędne. Następnie wykreśl z klasy Guitar wszystkie właściwości i metody, które nie będą już w niej potrzebne. W końcu w diagramie klasy Guitar pozostawimy Ci nieco wolnego miejsca, na wypadek gdybyś doszedł do wniosku, że będziesz musiał dodać do niej jakieś nowe właściwości lub metody. Te dwie właściwości wciąż są unikalne dla Guitar GuitarSpec każdej gitary, a zatem muszą serialNumber: String builder: Builder pozostać bez zmian. price: double model: String builder: Builder type: Type To są właściwości, model: String backWood: Wood które klienci type: Type Powtarzanie się Ryśka topWood: Wood kodu eliminujemy, przekazują do backWood: Wood przenosząc metody search(); wszystkie wspólne a zatem możemy topWood: Wood właściwości je przenieść i związane do klasy spec: GuitarSpec z nimi metody GuitarSpec. do obiektu, którego będziemy używali zarówno Będzie nam także potrzebna getSerialNumber(): String getBuilder(): Builder do określania getPrice(): double właściwości gitar, referencja getModel(): String jak i kryteriów do obiektu GuitarSpec setPrice(float) getType(): Type wyszukiwania. opisującego daną gitarę. getBuilder(): Builder getBackWood(): Wood getModel(): String getTopWood(): Wood getType(): Type getBackWood(): Wood getTopWood(): Wood getSpec(): GuitarSpec my dokładnie Z metodami postępujewościami — tak samo jak z właści my wszelkie z klasy Guitar usuwa ą się w niej metod y, które powtarzaj A teraz uaktualnij swój kod itarSpec. oraz w nowej klasie Gu Dysponując powyższym diagramem, powinieneś być w stanie dodać do aplikacji nową klasę GuitarSpec i zaktualizować kod klasy Guitar. Od razu wprowadź niezbędne modyfikacje także w klasie Inventory, tak by całą aplikację można było poprawnie skompilować. 60 Rozdział 1.
  • 46. Dobrze zaprojektowane aplikacje są super Nie ma niemądrych pytań Zobaczmy, jak postępują prace nad naszymi trzema krokami, które mają P Rozumiem, dlaczego potrzebujemy obiektu, by przekazywać .: nam pozwolić na stworzenie doskonałej aplikacji. wymagania klienta do metody search()… ale dlaczego używamy go w obiekcie Guitar do przechowywania informacji o właściwościach gitary? O.: Załóżmy, że zastosowalibyśmy obiekt GuitarSpec jedynie do 1. Upewnij się, że przechowywania wymagań klienta i przekazywania ich do metody search(), oprogramowanie robi to, natomiast klasa Guitar pozostałaby niezmieniona. W takim przypadku, gdyby Rysiek zaczął handlować gitarami 12-strunowymi i chciał dodać właściwość czego oczekuje klient. numStrings, to musiałbyś dodać taką właściwość — oraz towarzyszący jej kod metody getNumStrings() — zarówno w klasie GuitarSpec, jak i Guitar. Zajmujemy się teraz Rozumiesz zapewne, że to prowadziłoby do powtarzania się tego samego kodu. krokiem 2. — czyli pracujemy nad Zamiast tego cały (potencjalnie) powtarzający się kod możemy umieścić w klasie projektem aplikacji. GuitarSpec, a do klasy Guitar dodać referencję do obiektu GuitarSpec. W ten sposób unikniemy powtarzania się kodu. 2. Zastosuj proste Za każdym razem gdy zauważysz zasady projektowania powtarzający się kod, poszukaj okazji obiektowego, by do zastosowania hermetyzacji. poprawić elastyczność P Wciąż nie do końca rozumiem, dlaczego takie rozwiązanie jest .: oprogramowania. traktowane jako hermetyzacja. Czy możecie mi to jeszcze raz wyjaśnić? z To właśnie w tym miejscu zaczynas a szuk ać poważnych problemów, zwłaszcz ch z takimi zagadnieniami jak O.: Ideą hermetyzacji jest zabezpieczenie informacji gromadzonych w jednym związany powtarzający się kod lub nieprawi dłowo miejscu aplikacji przed jej pozostałymi fragmentami. W najprostszym przypadku zaprojektowane klasy. pewną informację przechowywaną w klasie można chronić przed pozostałym kodem aplikacji poprzez zdefiniowanie jej jako składowej prywatnej. Jednak 3. Staraj się, by projekt czasami będziemy chcieli w taki sposób zabezpieczyć nie jedną, lecz całą grupę właściwości — takich jak szczegółowe informacje o cechach gitary — a nawet oprogramowania zapewniał zachowania, na przykład sposób, w jaki latają poszczególne gatunki kaczek. Jeśli usuniemy zachowanie poza klasę, będziemy mogli je zmieniać bez łatwość jego utrzymania konieczności jednoczesnej modyfikacji samej klasy. A zatem, gdybyś zmienił sposób przechowywania właściwości, to nie musiałbyś wprowadzać i pozwalał na jego jakichkolwiek modyfikacji w klasie Guitar, gdyż właściwości zostały z niej usunięte i przeniesione do innej klasy. wielokrotne stosowanie. Właśnie na tym polega potęga hermetyzacji: poprzez podzielenie aplikacji na części uzyskujemy możliwość modyfikowania jednej z nich, bez konieczności Pamiętaj, że w tym kroku też będz mieli sporo pracy związanej z proj iemy wprowadzania zmian w innych. Ogólnie rzecz biorąc, powinieneś hermetyzować aplikacji; a zatem, zanim ukończysektem te części aplikacji, które mogą się zmieniać, oddzielając je od fragmentów aplikacji, prace, Twój kod będzie naprawd z ę łatw które zmieniać się nie będą. rozbudowy i wielokrotnego zastosow y do ania. jesteś tutaj  61
  • 47. Aktualizacja magazynu Aktualizacja klasy Inventory Teraz, kiedy już hermetyzowaliśmy specyfikację gitary, musimy wprowadzić w naszym kodzie kilka następnych zmian. Inventory guitar: List addGuitar(String, double, Builder, String, Type, Wood, Wood) getGuitar(String): Guitar search(GuitarSpec): List Teraz do metody search() public class Inventory { przekazywany jest obiekt GuitarSpec, a nie Guitar. // właściwości, konstruktor, inne metody public List search(GuitarSpec searchSpec) { List matchingGuitars = new LinkedList(); Obecnie wszystkie for (Iterator i = guitars.iterator(); i.hasNext(); ) { informacje, jakich Guitar guitar = (Guitar)i.next(); używamy podczas porównywania, GuitarSpec guitarSpec = guitar.getSpec(); pochodzą z obiektów if (searchSpec.getBuilder() != guitarSpec.getBuilder()) GuitarSpec, a nie Guitar. continue; String model = searchSpec.getModel().toLowerCase(); if ((model != null) (!model.equals(“”)) (!model.equals(guitarSpec.getModel().toLowerCase()))) continue; Ten kod jest niemal identyczny jak if (searchSpec.getType() != guitarSpec.getType()) wcześniej, a jedyna continue; różnica polega na tym, że porównujemy if (searchSpec.getBackWood() != guitarSpec.getBackWood()) informacje continue; przechowywane w obiekcie if (searchSpec.getTopWood() != guitarSpec.getTopWood()) GuitarSpec. continue; matchingGuitars.add(guitar); class Inven- } tory { return matchingGuitars; Search()} } } y nieznaczne Inventory.java oć wprowadziliśm Ch dzie klasy, to ta modyfikacje w ko raca listę gitar metoda wciąż zw ne kryteria. da spełniających za 62 Rozdział 1.
  • 48. Dobrze zaprojektowane aplikacje są super Przygotuj się na kolejny test Aby przetestować te wszystkie modyfikacje, będziesz musiał wprowadzić kilka zmian w klasie FindGuitarTester: public class FindGuitarTester { public static void main(String[] args) { // Inicjalizacja zawartości magazynu gitar Ryśka Inventory inventory = new Inventory(); initializeInventory(inventory); Tym razem klient przekazuje do metody search() GuitarSpec whatEveLikes = obiekt klasy GuitarSpec. new GuitarSpec(Builder.FENDER, “Stratocastor”, Type.ELECTRIC, Wood.ALDER, Wood.ALDER); List matchingGuitars = inventory.search(whatEveLikes); if (!matchingGuitars.isEmpty()) { System.out.println(“Ewo, może spodobają Ci się następujące gitary:”; for (Iterator i = matchingGuitars.iterator(); i.hasNext(); ) { Guitar guitar = (Guitar)i.next(); GuitarSpec spec = guitar.getSpec(); Także tutaj System.out.println(“ Mamy w magazynie gitarę “ + używamy spec.getBuilder() + “ model “ + spec.getModel() + “, nowej klasy GuitarSpec. jest “ + “to gitara” + spec.getType() + “ :n “ + spec.getBackWood() + “ - tył i boki,n “ + spec.getTopWood() + “ - góra.n Możesz ją mieć za “ + spec.getPrice() + “PLN!n ----”); } } else { System.out.println(“Przykro mi, Ewo, nie znalazłem nic dla Ciebie.”); } } private static void initializeInventory(Inventory inventory) { // dodanie gitar do magazynu } } class FindGui- tar { main() } FindGuitarTester.java jesteś tutaj  63
  • 49. Zastosowanie zasad projektowania obiektowego ?   dlaczEgo mam na To  zWracać uWagĘ ?  Dowiedziałeś się już całkiem dużo na temat pisania doskonałego oprogramowania, jednak wciąż jeszcze musisz się wiele nauczyć. Weź głęboki oddech i zastanów się nad niektórymi terminami i zasadami, które przedstawiliśmy w tym rozdziale. Połącz słowa umieszczone w lewej kolumnie z wyjaśnieniami celów stosowania danych technik lub zasad, znajdującymi się w kolumnie prawej. Beze mnie Twój klient nigdy nie będzie zadowolony. Bez względu na to, jak wspaniale byłaby Elastyczność zaprojektowana i napisana aplikacja, to właśnie ja sprawiam, że na twarzy klienta pojawia się uśmiech. Ja wkraczam do akcji tam, gdzie chodzi o możliwości wielokrotnego wykorzystania tego samego kodu Hermetyzacja i uzyskanie pewności, że nie trzeba będzie rozwiązywać problemów, które ktoś inny rozwiązał już wcześniej. Programiści używają mnie po to, by oddzielić od siebie fragmenty kodu, które ulegają zmianom, Funkcjonalność od tych, które pozostają takie same; dzięki temu łatwo można wprowadzać w kodzie modyfikacje — bez obawy, że cała aplikacja przestanie działać. Programiści używają mnie po to, by oprogramowanie można było rozwijać i zmieniać bez konieczności Wzorce projektowe ciągłego pisania go od samego początku. To ja sprawiam, że aplikacje stają się solidne i odporne. Odpowiedzi znajdziesz na stronie 81 Nie ma niemądrych pytań P Hermetyzacja nie jest jedyną zasadą projektowania .: P Ale nie do końca rozumiem, w jaki sposób ta cała .: obiektowego, jakiej używamy na tym etapie prac nad hermetyzacja poprawia elastyczność mojego kodu. Możecie aplikacją, prawda? to jeszcze raz wyjaśnić? O.: Nie. Kolejnymi z zasad, o których warto pamiętać, są O.: Kiedy już poprawiłeś swój kod i zapewniłeś, że działa on dziedziczenie i polimorfizm. Jednak także i one łączą się tak, jak sobie tego życzył klient, największym problemem staje się z powtarzalnością kodu oraz hermetyzacją; dlatego rozpoczynanie elastyczność. Co się stanie, jeśli klient poprosi o dodanie do aplikacji pracy od poszukiwania miejsc, w jakich można zastosować kilku nowych właściwości lub możliwości funkcjonalnych? Jeśli hermetyzację, zawsze będzie dobrym rozwiązaniem. w aplikacji będzie wiele powtarzających się fragmentów kodu bądź W dalszej części książki przedstawimy znacznie więcej informacji jeśli jej struktura dziedziczenia będzie skomplikowana i niejasna, o zasadach projektowania obiektowego (a w rozdziale 8. to wprowadzanie modyfikacji w takim programie może stać się zaprezentujemy nawet kilka przykładów); zatem nie przejmuj się, prawdziwym koszmarem. jeśli teraz jeszcze nie wszystko dokładnie rozumiesz. Zanim skończysz Jednak dzięki zastosowaniu takich zasad jak hermetyzacja oraz lekturę tej książki, dowiesz się znacznie więcej o hermetyzacji, poprawne projektowanie klas wprowadzanie zmian może być projektowaniu klas oraz o wielu innych zagadnieniach. znacznie łatwiejsze, a sama aplikacja stanie się bardziej elastyczna. 64 Rozdział 1.
  • 50. Dobrze zaprojektowane aplikacje są super Wróćmy do aplikacji Ryśka… Sprawdźmy teraz, czy wszystkie wprowadzone zmiany nie wpłynęły negatywnie na poprawność działania aplikacji Ryśka. Skompiluj wszystkie klasy i jeszcze raz uruchom program FindGuitarTester: Tym razem wyniki od niczym się nie różnią o, uzyskiwanych poprzedni jednak sama aplikacja ana jest lepiej zaprojektow i znacznie bardziej elastyczna. WYTĘŻ umYsł Czy jesteś w stanie podać trzy konkretne czynniki, które sprawiają, że w dobrze zaprojektowanym oprogramowaniu wprowadzanie zmian jest łatwiejsze niż w oprogramowaniu, w którym fragmenty kodu powielają się? jesteś tutaj  65
  • 51. Czas na poważne projektowanie Projekt po raz pierwszy, projekt po raz drugi Skoro już raz przeanalizowałeś aplikację i zastosowałeś w niej podstawowe techniki projektowania obiektowego, czas przyjrzeć się jej ponownie i upewnić, że jest ona nie tylko elastyczna, lecz także zapewnia łatwość wielokrotnego stosowania fragmentów kodu oraz ich rozszerzania. 1. Upewnij się, że oprogramowanie robi to, czego oczekuje klient. Nadszedł czas, by poważnie rozważyć możliwości wielokrotnego wykorzystania kodu aplikacji oraz łatwość wprowadzania w nim zmian. To właśnie teraz możesz zmienić poprawnie zaprojektowane klasy na rozszerzalne, nadające się do wielokrotnego 2. Zastosuj proste zasady stosowania komponenty. projektowania obiektowego, by poprawić elastyczność oprogramowania. Skoro już za zasady proj stosowałeś podstaw nadszedł cz ektowania obiektowegowe wzorce proj as, by wykorzystać o, się na moż ektowe i skoncentrow stosowania liwościach wielokrotneać kodu. go 3. Staraj się, by projekt oprogramowania zapewniał łatwość jego utrzymania i pozwalał na jego wielokrotne stosowanie. 66 Rozdział 1.
  • 52. Dobrze zaprojektowane aplikacje są super (naprawdę) Sprawdźmy, czy klasa Inventory.java jest dobrze zaprojektowana W poprzedniej części rozdziału zastosowaliśmy hermetyzację, by poprawić projekt Teraz, kiedy już oddałe narzędzia do wyszukiwania gitar w magazynie Ryśka, niemniej jednak w naszym sprawne narzędzie wy ś Ryśkowi szu kodzie wciąż można znaleźć miejsca, które należałoby poprawić w celu pozbycia się w magazynie gitary pas kujące wymagań określanych ujące do potencjalnych problemów. Dzięki temu nasz kod będzie można łatwiej rozszerzać, masz pewność, że Rysprzez klientów, iek do Ciebie zadzwoni, jeś ponownie kiedy Rysiek wymyśli kolejne możliwości, które chciałby dodać do aplikacji, oraz zmienić w swojej aplikali zechce coś łatwiej wykorzystać, jeśli zechcemy zastosować jego fragmenty w innej aplikacji. cji. public List search(GuitarSpec searchSpec) { List matchingGuitars = new LinkedList(); for (Iterator i = guitars.iterator(); i.hasNext(); ) { Guitar guitar = (Guitar)i.next(); GuitarSpec guitarSpec = guitar.getSpec(); Oto kod metody if (searchSpec.getBuilder() != guitarSpec.getBuilder()) search() klasy continue; Inventory. String model = searchSpec.getModel().toLowerCase(); Przyjrzyj mu się if ((model != null) (!model.equals(””)) dokładniej. (!model.equals(guitarSpec.getModel().toLowerCase()))) continue; if (searchSpec.getType() != guitarSpec.getType()) continue; if (searchSpec.getBackWood() != guitarSpec.getBackWood()) continue; if (searchSpec.getTopWood() != guitarSpec.getTopWood()) continue; matchingGuitars.add(guitar); } return matchingGuitars; class } Inven- tory { Search()} Inventory.java Zaostrz ołówek Zobacz, co napisaliśmy na stronie 82 Co zmieniłbyś w tym fragmencie kodu? W przedstawionym kodzie występuje poważny problem, Twoim zadaniem jest go odnaleźć. W poniższych pustych wierszach zapisz, na czym według Ciebie polega ten problem oraz w jaki sposób można by go rozwiązać. jesteś tutaj  67
  • 53. Czy proste zmiany naprawdę są proste? No wiesz… Zawsze uwielbiałem grać na 12-strunowych gitarach. Jak trudne byłoby zmodyfikowanie aplikacji w taki sposób, bym mógł sprzedawać takie gitary i aby moi klienci mogli je wyszukiwać? Jak łatwo będzie wprowadzić taką zmianę do aplikacji Ryśka? Przeanalizuj diagram klas tworzących aplikację Ryśka i zastanów się, co należałoby zrobić, aby wyposażyć ją w możliwość obsługi gitar 12-strunowych. Jakie właściwości i metody musiałbyś dodać, w jakich klasach należałoby je umieścić? Jakie zmiany musiałbyś wprowadzić w kodzie aplikacji, by zapewnić klientom Ryśka możliwość wyszukiwania 12-strunowych gitar? Ile klas musiałeś zmienić, by wprowadzić powyższą modyfikację? Czy dalej uważasz, że aplikacja Ryśka jest dobrze zaprojektowana? Guitar serialNumber: String price: double spec: GuitarSpec getSerialNumber(): String getPrice(): double setPrice(float) getSpec(): GuitarSpec 68 Rozdział 1.
  • 54. Dobrze zaprojektowane aplikacje są super Przypiski do diagramu klas aplikacji Ryśka Zaostrz ołówek GuitarSpec Rysiek chciałby sprzedawać 12-strunowe gitary. Przygotuj zatem ołówek i umieść na diagramie klas notatki zawierające następujące informacje: builder: Builder 1. Do jakiej klasy dodałbyś nową właściwość o nazwie numStrings, która będzie model: String służyć do przechowywania informacji o liczbie strun w danej gitarze? type: Type 2. Gdzie dodałabyś nową metodę o nazwie getNumStrings(), która zwraca liczbę strun w gitarze? backWood: Wood 3. W jakich innych miejscach aplikacji powinieneś wprowadzić zmiany w kodzie, topWood: Wood tak by podczas wyszukiwania gitar klienci Ryśka mogli określać, że interesują ich gitary 12-strunowe? getBuilder(): Builder W końcu, w umieszczonych poniżej pustych wierszach zapisz wszelkie problemy getModel(): String związane z projektem aplikacji, które odnalazłeś podczas dodawania obsługi 12-strunowych gitar. getType(): Type getBackWood(): Wood getTopWood(): Wood Oto podpowiedź: zapisana tutaj odpowiedź powinna być Inventory związana z informacjami, które zapisałeś w pustych wierszach na stronie 67. guitars: List addGuitar(String, double, Builder, String, Type, Wood, Wood) getGuitar(String): Guitar search(GuitarSpec): List Builder WYTĘŻ toString(): String Type umYsł Na czym polega przewaga utworzenia toString(): String Wood nowej właściwości o nazwie numStrings nad dodaniem właściwości logicznej toString(): String określającej, czy dana gitara ma 12 strun? jesteś tutaj  69
  • 55. Mamy problem z hermetyzacją Zaostrz ołówek Przypiski do diagramu klas aplikacji Ryśka Rozwiązanie Rysiek chciałby sprzedawać 12-strunowe gitary. Przygotuj zatem ołówek i umieść na diagramie klas notatki zawierające następujące informacje: 1. Do jakiej klasy dodałbyś nową właściwość o nazwie numStrings, która będzie służyć do przechowywania informacji o liczbie strun w danej gitarze? 2. Gdzie dodałabyś nową metodę o nazwie getNumStrings(), która zwraca liczbę strun w gitarze? 3. W jakich innych miejscach aplikacji powinieneś wprowadzić zmiany w kodzie, tak by podczas wyszukiwania gitar klienci Ryśka mogli określać, że interesują ich gitary 12-strunowe? W końcu, w umieszczonych poniżej pustych wierszach zapisz wszelkie problemy związane z projektem aplikacji, które odnalazłeś podczas dodawania obsługi 12-strunowych gitar. Musimy zmienić konstruktor tej Dodajemy właściwość do klasy GuitarSpec, klasy, gdyż w jego wywołaniu lecz jednocześnie musimy zmienić kod metody są przekazywane wszystkie informacje, search() w klasie Inventory oraz konstruktor Guitar które następnie zapisujemy w obiekcie klasy Guitar. serialNumber: String GuitarSpec. price: double spec: GuitarSpec Metoda addGuitar() Oto co my wymyśliliśmy. zdefiniowana w tej klasie Czy Ty zapisałeś coś getSerialNumber(): String także operuje na wszystkich podobnego? getPrice(): double właściwościach gitary. Dodanie nowej właściwości setPrice(float) oznacza zatem konieczność Do klasy GuitarSpec getSpec(): GuitarSpec wprowadzenia modyfikacji musimy dodać w tej metodzie — a to jest właściwość numStrings. problem. GuitarSpec Inventory builder: Builder guitar: List model: String addGuitar(String, double, Builder, String, Type, type: Type Wood, Wood) backWood: Wood getGuitar(String): Guitar topWood: Wood search(GuitarSpec): List getBuilder(): Builder getModel(): String Do tej klasy musimy getType(): Type Kolejny problem: także dodać metodę getBackWood(): Wood musimy zmienić getNumStrings(), która Builder metodę search() będzie zwracać liczbę getTopWood(): Wood klasy Inventory, strun, jaką posiada toString(): String Type aby uwzględnić dana gitara. nowe właściwości toString(): String Wood dodane do klasy GuitarSpec. toString(): String 70 Rozdział 1.
  • 56. Dobrze zaprojektowane aplikacje są super Czyli problem polega właśnie na tym? Dodawanie nowych właściwości do klasy GuitarSpec nie powinno zmuszać nas do wprowadzania modyfikacji w klasach Guitar oraz Inventory. Czy także ten problem możemy rozwiązać, wykorzystując hermetyzację? Owszem — musimy hermetyzować specyfikacje gitary i lepiej izolować je od pozostałych fragmentów aplikacji Ryśka. Choć nowe właściwości dodajemy jedynie do klasy GuitarSpec, to jednak przy tej okazji musimy zmodyfikować dwie inne klasy: Guitar oraz Inventory. W konstruktorze klasy Guitar należy przekazywać dodatkowy argument, a w metodzie search() klasy Inventory trzeba dodać jedno porównanie. m Zastosowanie tego kodu w inny programie nie będzie prostym acji Ten konstruktor tworzy obiekt zadaniem. Wszystkie klasy aplik GuitarSpec, zatem każda Ryśk a są od siebie wzajemnie ać zmiana specyfikacji gitary uzależnione i nie można zastosow będzie powodować konieczność jednej z nich bez jednoczesnego zmiany samego konstruktora. wykorzystania wszystkich pozostałych. public Guitar (String serialNumber, public List search (GuitarSpec searchSpec) { double price, List matchingGuitars = new LinkedList(); Builder builder, for (Iterator i = guitars.iterator(); i.hasNext(); ) { String model, Type type, Guitar guitar = (Guitar)i.next(); Wood backWood, Wood topWood ) { GuitarSpec guitarSpec = guitar.getSpec(); this.serialNumber = serialNumber; if (searchSpec.getBuilder() != guitarSpec.getBuilder()) this.price = price; continue; this.spec = new GuitarSpec (builder, model, String model = searchSpec.getModel().toLowerCase(); type, backWood, topWood); if ((model != null) (!model.equals(„”)) (!model.equals(guitarSpec.getModel().toLowerCase()))) class continue; Guitar if (searchSpec.getType() != guitarSpec.getType()) Gui- tar() continue; } if (searchSpec.getBackWood() != guitarSpec.getBackWood()) continue; Guitar.java if (searchSpec.getTopWood() != guitarSpec.getTopWood()) continue; matchingGuitars.add(guitar); } class return matchingGuitars; Inven- tory { } Search()} Inventory.java jesteś tutaj  71
  • 57. Staraj się, by fragmenty aplikacji można było wielokrotnie używać Zagadka projektowa Sama wiedza, że w aplikacji Ryśka coś szwankuje, to zdecydowanie za mało. Nie wystarczy także świadomość, że konieczne jest ponowne zastosowanie hermetyzacji. Teraz musisz wymyślić, jak należy poprawić tę aplikację, by łatwiej można było wielokrotnie stosować jej fragmenty oraz by jej rozszerzanie nie przysparzało tylu problemów. Problem: Dodanie nowej właściwości do klasy GuitarSpec zmusza nas do wprowadzenia zmian także w kodzie klas Guitar oraz Inventory. Strukturę aplikacji należy zmienić w taki sposób, by dodawanie nowych właściwości do klasy GuitarSpec nie powodowało konieczności modyfikacji innych klas. Twoje zadanie: 1 Dodaj właściwość numStrings oraz metodę getNumStrings() do klasy GuitarSpec. 2 Zmodyfikuj kod klasy Guitar w taki sposób, by właściwości obiektu GuitarSpec zostały Nie wiesz, co — w tym kontekście usunięte z konstruktora tej klasy. — oznacza „delegowanie” 3 Zmień metodę search() w klasie Inventory w taki sposób, by porównywanie dwóch — sprawdź… obiektów GuitarSpec zostało delegowane do klasy GuitarSpec, a nie było realizowane bezpośrednio w tej metodzie. 4 Zmodyfikuj kod klasy FindGuitarTester, by działała ona poprawnie ze zmienionymi klasami Guitar, GuitarSpec i Inventory, a następnie upewnij się, że wyszukiwanie gitar działa poprawnie. 5 Porównaj odpowiedzi, które podałeś, z naszymi odpowiedziami zamieszczonymi na stronie 74; następnie przygotuj się na kolejny test, który pozwoli Ci przekonać się, czy wreszcie zakończyłeś prace nad aplikacją Ryśka. Jedyna zmiana wprowadzić w , jaką będziesz musiał kodu tworzące tym miejscu, dotyczy magazynu i pogo testową zawartość nowego konstr lega na zastosowaniu uktora klasy Guitar. 72 Rozdział 1.
  • 58. Dobrze zaprojektowane aplikacje są super Nie ma niemądrych pytań P Napisaliście, że powinienem „delegować” .: P A niby w jaki sposób to delegowanie ma ułatwiać .: porównywanie do klasy GuitarSpec. Co to jest możliwości wielokrotnego wykonywania kodu? delegowanie? O.: Dzięki delegowaniu każdy obiekt może sam zajmować O.: O delegowaniu mówimy w sytuacji, gdy obiekt, który musi się porównywaniem z innymi obiektami (lub wykonywaniem wykonać pewną czynność, zamiast wykonać ją samemu, prosi o jej jakiejkolwiek innej operacji). To z kolei oznacza, że obiekty mogą wykonanie (w całości lub częściowo) inny obiekt. być bardziej niezależne albo luźniej powiązane. Takie luźniej A zatem, w swojej zagadce projektowej nie chcesz, by metoda powiązane obiekty łatwiej jest zastosować w innej aplikacji, gdyż search() klasy Inventory porównywała dwie specyfikacje nie są one ściśle uzależnione od kodu innych obiektów. GuitarSpec bezpośrednio w swoim kodzie. Zamiast tego chcesz, P Jeszcze raz — co oznacza „luźne powiązanie”? .: by poprosiła obiekt GuitarSpec o określenie, czy specyfikacje te odpowiadają sobie. A zatem metoda search() deleguje porównanie do obiektu GuitarSpec. O.: Luźne powiązanie oznacza, że każdy z obiektów używanych P A dlaczego używamy takiego rozwiązania? .: w aplikacji wykonuje tylko i wyłączenie swoje zadania. A zatem cała funkcjonalność aplikacji jest rozdzielona i zaimplementowana O.: Delegowanie ułatwia wielokrotne wykorzystywanie kodu w dobrze zdefiniowanych obiektach, z których każdy w doskonały sposób realizuje przypisane mu zadania. aplikacji. Oprócz tego pozwala ono, by każdy obiekt koncentrował się wyłącznie na swoich możliwościach funkcjonalnych i chroni przed umieszczaniem zachowań związanych z jednym obiektem P.: A dlaczego to jest dobre rozwiązanie? w różnych miejscach kodu aplikacji. Jednym z najczęściej spotykanych przykładów delegowania w języku Java jest metoda equals(). Metoda ta nie stara O.: Aplikacje tworzone przez luźno powiązane obiekty zazwyczaj się samodzielnie sprawdzać, czy dwa obiekty są sobie równe, są bardziej elastyczne i łatwiej można wprowadzać w nich lecz zamiast tego wywołuje metodę equals() jednego modyfikacje. Ponieważ poszczególne obiekty nie są zwykle w żaden z porównywanych obiektów i przekazuje w jej wywołaniu drugi sposób zależne od innych obiektów, zatem możemy zmienić obiekt. Następnie pobiera jedynie wartość true lub false zwróconą zachowanie jednego z obiektów bez konieczności wprowadzania przez wywołanie metody equals(). modyfikacji w innych. Dzięki temu dodawanie nowych właściwości lub możliwości funkcjonalnych staje się znacznie prostsze. Kącik naukowy delegowanie — proces polegający na tym, iż jeden obiekt zleca wykonanie pewnej operacji innemu, który wykonuje ją w imieniu pierwszego obiektu. jesteś tutaj  73
  • 59. Jeszcze więcej hermetyzacji Zagadka projektowa — Rozwiązanie Sama wiedza, że w aplikacji Ryśka coś szwankuje, to zdecydowanie za mało. Nie wystarczy także świadomość, że konieczne jest ponowne zastosowanie hermetyzacji. Teraz musisz wymyślić, jak należy poprawić tę aplikację, by łatwiej można było wielokrotnie stosować jej fragmenty oraz by jej rozszerzanie nie przysparzało tylu problemów. Problem: Dodanie nowej właściwości do klasy GuitarSpec zmusza nas do wprowadzenia zmian także w kodzie klas Guitar oraz Inventory. Strukturę aplikacji należy zmienić w taki sposób, by dodawanie nowych właściwości do klasy GuitarSpec nie powodowało konieczności modyfikacji innych klas. Twoje zadanie: 1 Dodaj właściwość numStrings oraz metodę getNumStrings() do klasy GuitarSpec. public class GuitarSpec { Nie zapomnij zmodyfiko konstruktora klasy wać // inne właściwości GuitarSpec. private int numStrings; To było naprawdę public GuitarSpec(Builder builder, String model, Type type, łatwe… int numStrings, Wood backWood, Wood topWood) { this.builder = builder; this.model = model; this.type = type; this.numStrings = numStrings; this.backWood = backWood; this.topWood = topWood; } // inne metody public int getNumStrings() { return numStrings; } } class Guitar Spec { get- Num- Strings() GuitarSpec.java 74 Rozdział 1.
  • 60. Dobrze zaprojektowane aplikacje są super 2 Zmodyfikuj kod klasy Guitar w taki sposób, by właściwości obiektu GuitarSpec zostały usunięte z konstruktora tej klasy. public Guitar(String serialNumber, double price, GuitarSpec spec) { this.serialNumber = serialNumber; this.price = price; this.spec = spec; } class Guitar { Gui- arSpec tar() Teraz nie tworzymy obiektu Guit } w konstruktorze klasy Guitar, lecz ołania wyw przekazujemy go jako argument Guitar.java konstruktora. 3 Zmień metodę search() w klasie Inventory w taki sposób, by porównywanie dwóch obiektów GuitarSpec zostało delegowane do klasy GuitarSpec, a nie było realizowane bezpośrednio w tej metodzie. Metoda search() { itarSpec searchSpec) stała się znacznie public List search(Gu = new LinkedList(); prostsza. List matchingGuitars asNext(); ) { tars.iterator(); i.h for (Iterator i = gui itar)i.next(); Guitar guitar = (Gu s(searchSpec)) if (guitar.getSpec().matche class matchingGuitars.add(guitar); Inven- tory { } ; return matchingGuitars search() } Inventory.java Znaczna część public boolean matches(GuitarSpec otherSpec) { kodu metody if (builder != otherSpec.builder) search() została return false; z niej usunięta if ((model != null) (!model.equals(””)) i przeniesiona (!model.toLowerCase().equals(otherSpec.model.toLowerCase()))) do metody return false; matches() klasy if (type != otherSpec.type) GuitarSpec. return false; if (numStrings != otherSpec.numStrings) return false; if (backWood != otherSpec.backWood) return false; if (topWood != otherSpec.topWood) return false; return true; class Teraz dodawanie nowych } Guitar właściwości do klasy Spec { GuitarSpec wymaga jedynie get- wprowadzania zmian w kodzie Num- Strings() tej kasy — klasy Guitar oraz Inventory nie muszą być GuitarSpec.java modyfikowane. jesteś tutaj  75
  • 61. Test aplikacji Ostatni test aplikacji (przygotowanej do wielokrotnego używania kodu) O rany… naprawdę wykonaliśmy kawał dobrej roboty od momentu, gdy Rysiek pokazał nam pierwszą wersję swojej aplikacji. Upewnijmy się, czy jej ostatnia wersja wciąż poprawnie działa — zarówno z punktu widzenia Ryśka, jak i jego klientów — i czy spełnia nasze wymagania dotyczące poprawnego projektu, prostego utrzymania oraz kodu, którego z łatwością będzie można wielokrotnie używać. Oto co powinieneś zobaczyć po wykonaniu nowej wersji programu FindGuitarTester. Ewa może obejrzeć kilka wyszuka dla niej gitar, a Rysiek — znów nych sprzedawać instrumenty swojej wyszukanej klienteli. Gratulujemy! Udało Ci się zmodyfikować niedziałającą aplikację przeszukującą magazyn Ryśka i zmienić ją w poprawnie zaprojektowane, doskonałe oprogramowanie. 76 Rozdział 1.
  • 62. Dobrze zaprojektowane aplikacje są super Oto co zrobiliśmy Przyjrzyjmy się pokrótce, w jaki sposób udało nam się sprawić, że obecnie aplikacja przeszukująca magazyn Ryśka działa tak dobrze: Czy pamiętasz nasze Wykonaliśmy je kolejn trzy kroki? o, przekształcić niedziała by jąc przeszukujące magazyn e narzędzie we w pełni funkcjona Ryśka lną zaprojektowaną aplika , dobrze cję. Rozpoczęliśmy od poprawienia niektórych problemów związanych 1. Upewnij się, że z działaniem aplikacji. oprogramowanie robi to, Następnie dodaliśmy do niej kilka nowych możliwości czego oczekuje klient. funkcjonalnych, dzięki czemu wyszukiwanie zwracało nie jedną, lecz całą listę gitar. żliwości Rozbudowując mo acji, fu nkcjonalne aplik , że podejmowan upewniliśmy się z jej strukturą e 2. Zastosuj proste zasady decyzje związane są właściwe i do bre. projektowania obiektowego, by poprawić elastyczność oprogramowania. Oprócz tego hermetyz owaliśmy właściwości gitary i zapewniliśmy, że dodawanie nowych nie stanowić większego pro będzie blemu. 3. Staraj się, by projekt oprogramowania zapewniał łatwość jego utrzymania Udało się nam nawet , i pozwalał na jego nie zastosować delegowa wane dzi ęki czemu obiekty uży j w apl ikacji stały się luźnie iło popraw powiązane; to z kolei o możliwości wielokrotneg cji. wielokrotne stosowanie. używania kodu aplika jesteś tutaj  77
  • 63. Analiza i projektowanie obiektowe pomagają w tworzeniu doskonałego oprogramowania Czy pamiętasz tego biednego gościa? On chciał jedynie pisać wspaniałe oprogramowanie. Jaka jest zatem odpowiedź? W jaki sposób można konsekwentnie pisać wspaniałe oprogramowanie? Potrzebujesz w tym celu sekwencji czynności, które pozwolą Ci upewnić się, że Twoje oprogramowanie działa i jest dobrze zaprojektowane. Nie musi ona być ani długa, ani skomplikowana — wystarczy prosta, trójetapowa sekwencja, której użyliśmy do poprawienia aplikacji Ryśka i której z powodzeniem będziesz mógł używać we wszystkich swoich projektach. Analiza i projektowanie obiektowe pomoże Ci pisać wspaniałe programy, i to za każdym razem.  Za każdym razem gdy w tym rozdziale wspominaliśmy o trzech krokach pozwalających na pisanie wspaniałego oprogramowania, tak Wyrażenie to będziemy określali naprawdę mieliśmy na myśli OOAD — analizę skrótowo jako i projektowanie obiektowe. OOAD, od angielskich słów Object-Oriented W istocie analiza i projektowanie obiektowe jest Analysis Design. sposobem pisania oprogramowania. Jej celem jest to, by oprogramowanie robiło, co do niego należy, i było dobrze zaprojektowane. To z kolei oznacza, że kod jest elastyczny, łatwo można wprowadzać w nim zmiany, jest prosty w utrzymaniu i nadaje się do wielokrotnego używania. 78 Rozdział 1.
  • 64. Dobrze zaprojektowane aplikacje są super OOAD ma na celu tworzenie wspaniałego oprogramowania, a nie dodanie Ci papierkowej roboty! Klienci są zadowoleni, kiedy ich aplikacje DZIAŁAJĄ. Możemy uzyskać od klienta wymagania, które pozwolą nam upewnić się, że tworzona O wymaganiach napiszemy aplikacja będzie robić to, o co klient prosił. Z powodzeniem można do tego celu zastosować w rozdziale 2. tak zwane przypadki użycia oraz diagramy, niemniej jednak wszystko sprowadza się do tego, by dowiedzieć się, jakich możliwości klient oczekuje od aplikacji. Klienci są zadowoleni, kiedy ich aplikacje będą DZIAŁAĆ DŁUGO. Nikt nie przepada za sytuacją, gdy aplikacja, która do tej pory działała dobrze, nagle Już nieco się zacznie szwankować. Jeśli dobrze zaprojektujemy nasze aplikacje, to będą one solidne dowiedziałeś o wrażliwych i nie będą przysparzać problemów za każdym razem, gdy użytkownik zacznie ich używać i kiepskich w niezwykły bądź niespodziewany sposób. Klasy oraz diagramy sekwencji mogą pomóc aplikacjach. w wykryciu usterek w projekcie aplikacji, jednak kluczowe znaczenie ma pisanie dobrze zaprojektowanego i elastycznego kodu. Klienci są zadowoleni, kiedy ich aplikacje można UAKTUALNIĆ. Kiedy klient prosi o dodanie kilku nowych, prostych możliwości, to dla niego nie ma nic Chcesz poznać gorszego, niż usłyszeć, że wykonanie poprawek zajmie dwa tygodnie i będzie kosztowało więcej informacji To wszystko o delegowaniu, kilkadziesiąt tysięcy złotych. Dzięki zastosowaniu technik projektowania obiektowego, takich nazywamy złożeniach właśnie analizą jak hermetyzacja, składanie oraz delegowanie, tworzone oprogramowanie będzie proste i projektowaniem i agregacji? Wszystkie te w utrzymaniu i łatwe do rozszerzania oraz aktualizacji. obiektowym. Nie zagadnienia chodzi tu wcale poruszymy o tworzenie Programiści są zadowoleni, kiedy napisanego kodu można głupkowatych szczegółowo najpierw WIELOKROTNIE UŻYWAĆ. diagramów… w rozdziale 5., a o pisanie a następnie Czy kiedyś napisałeś jakiś program dla jednego klienta, a następnie zauważyłeś, że po odlotowych w rozdziale 8. wprowadzeniu bardzo nieznacznych modyfikacji będzie on spełniał wymagania innego aplikacji, które uszczęśliwią klienta? Wystarczy się nieco zastanowić nad tworzoną aplikacją, by uniknąć takich klientów, problemów jak wzajemne uzależnienie klas i niepotrzebne powiązania pomiędzy nimi, a Tobie zapewnią i w efekcie uzyskać kod, który z powodzeniem będzie można wielokrotnie stosować. Takie błogie poczucie wielkości pojęcia jak Zasada Otwarte-Zamknięte (ang. Open-Close Principle, w skrócie OCP) oraz i satysfakcji. Zasada Pojedynczej Odpowiedzialności (ang. Single Responsibility Principle, w skrócie SRP) W rozdziale 8. bardzo mogą w tym pomóc. przekonasz się, jak duże znaczenie i wpływ na Programiści są zadowoleni, kiedy pisane przez nich aplikacje są ELASTYCZNE. powstający kod mają te zasady. Czasami tylko nieznaczne zmiany i refaktoryzacja pozwalają zamienić dobrą aplikację we wspaniały framework, którego z powodzeniem będzie można używać do przeróżnych zadań. To właśnie umiejętność wprowadzania takich zmian sprawi, że powoli przestaniesz być zwyczajnym koderem i zaczniesz myśleć jak prawdziwy architekt oprogramowania (o tak… ci goście zarabiają znacznie więcej). Tu chodzi o ogarnięcie całości zagadnienia. się umiejętnością ogólnego W rozdziałach 6. i 7. zajmiemy spojrzenia na rozwiązywan y problem i tworzeniem dobrej architektury dla pisanych aplikacji. jesteś tutaj  79
  • 65. Powtórzenie i prośba No i fantastycznie! Dzięki temu nowemu narzędziu do wyszukiwania nie mogę się opędzić od klientów. Ale swoją drogą… mam parę pomysłów na kilka nowych możliwości… propozycję Widzisz? Już dostałeś Rysiek dalszej pracy. Jednak poczekać będzie musiał z tym my kilka aż do rozdziału 5… Ma którymi w, ważniejszych problemó tępnym musimy się zająć w nas rozdziale. KlUCZOWe ZAGADNieNiA u Jeśli aplikacja jest wrażliwa, byle co może doprowadzić do u zapewnieniu elastyczności projektu możesz zastosować Po jej awarii. wzorce projektowe, by dodatkowo go poprawić i ułatwić wielokrotne stosowanie kodu aplikacji. u Dzięki zastosowaniu zasad projektowania obiektowego, takich jak hermetyzacja i delegowanie, można tworzyć u Odszukaj te fragmenty aplikacji, które często ulegają znacznie bardziej elastyczne aplikacje. zmianom, i postaraj się oddzielić je od pozostałych fragmentów, które się nie zmieniają. u Hermetyzacja polega na dzieleniu aplikacji na logiczne części. u Tworzenie aplikacji, które działają, lecz są nieprawidłowo zaprojektowane, zadowoli klienta, jednak Tobie przysporzy u delegowaniu mówimy wtedy, gdy jeden obiekt przekazuje O wielu zmartwień, problemów i nieprzespanych nocy, wykonanie pewnego zadania do innego obiektu. zmarnowanych na żmudne poprawianie błędów. u Zawsze zaczynaj prace nad projektem od określenia, u Analiza i projektowanie obiektowe udostępnia metody czego chce klient. pozwalające na tworzenie poprawnie zaprojektowanych u Kiedy uda Ci się już poprawnie zaimplementować aplikacji spełniających wymagania nie tylko klienta, lecz podstawowe możliwości funkcjonalne aplikacji, możesz także programisty. zająć się zmodyfikowaniem jej struktury i zapewnieniem jej elastyczności. 80 Rozdział 1.
  • 66. Dobrze zaprojektowane aplikacje są super rozwiązania ćwiczeń ?   dlaczEgo mam na To  zWracać uWagĘ ?  Dowiedziałeś się już całkiem dużo na temat pisania doskonałego oprogramowania, jednak wciąż jeszcze musisz się wiele nauczyć. Weź głęboki oddech i zastanów się nad niektórymi terminami i zasadami, które przedstawiliśmy w tym rozdziale. Połącz słowa umieszczone w lewej kolumnie z wyjaśnieniami celów stosowania danych technik lub zasad, znajdującymi się w kolumnie prawej. Beze mnie Twój klient nigdy nie będzie zadowolony. Bez względu na to, jak wspaniale byłaby Elastyczność zaprojektowana i napisana aplikacja, to właśnie ja sprawiam, że na twarzy klienta pojawia się uśmiech. Ja wkraczam do akcji tam, gdzie chodzi o możliwości wielokrotnego wykorzystania tego samego kodu Hermetyzacja i uzyskanie pewności, że nie trzeba będzie rozwiązywać problemów, które ktoś inny rozwiązał już wcześniej. Programiści używają mnie po to, by oddzielić od siebie fragmenty kodu, które ulegają zmianom, Funkcjonalność od tych, które pozostają takie same; dzięki temu łatwo można wprowadzać w kodzie modyfikacje — bez obawy, że cała aplikacja przestanie działać. Programiści używają mnie po to, by oprogramowanie można było rozwijać i zmieniać bez konieczności Wzorce projektowe ciągłego pisania go od samego początku. To ja sprawiam, że aplikacje stają się solidne i odporne. jesteś tutaj  81
  • 67. Rozwiązania ćwiczeń Zaostrz ołówek Rozwiązanie Co byś zmienił w tym kodzie? W przedstawionym kodzie występuje poważny problem, Twoim zadaniem jest go odnaleźć. W poniższych pustych wierszach zapisz, na czym według Ciebie polega ten problem oraz w jaki sposób można by go rozwiązać. Za każdym razem, gdy do klasy GuitarSpec zostanie dodana nowa właściwość bądź gdy zmieni się któraś z jej metod, konieczne będzie wprowadzenie zmian także w metodzie search() klasy Inventory. Porównywaniem powinna się zajmować klasa GuitarSpec; do niej powinniśmy także przenieść cały kod operujący na specyfikacji gitary, umieszczony obecnie w klasie Inventory. public List search(GuitarSpec searchSpec) { List matchingGuitars = new LinkedList(); for (Iterator i = guitars.iterator(); i.hasNext(); ) { Guitar guitar = (Guitar)i.next(); To nie jest GuitarSpec guitarSpec = guitar.getSpec(); przykład dobrego if (searchSpec.getBuilder() != guitarSpec.getBuilder()) projektu. Za continue; każdym razem, String model = searchSpec.getModel().toLowerCase(); gdy do klasy GuitarSpec if ((model != null) (!model.equals(””)) zostanie dodana (!model.equals(guitarSpec.getModel().toLowerCase()))) nowa właściwość, continue; konieczne będzie wprowadzenie if (searchSpec.getType() != guitarSpec.getType()) zmian w tym continue; kodzie. if (searchSpec.getBackWood() != guitarSpec.getBackWood()) continue; if (searchSpec.getTopWood() != guitarSpec.getTopWood()) continue; matchingGuitars.add(guitar); } return matchingGuitars; } class Inven- tory { search() Inventory.java Zastanów się: czy klasa Inventor na obsłudze magazynu gitar Ryśk y naprawdę koncentruje się a? tym, co sprawia, że dwie specyfika Czy też koncentruje się na GuitarSpec — są sobie równe? cje gitar — czyli dwa obiekty Chce się na swoich zadaniach. Porówny sz, by Twoje klasy skupiały jest zadaniem, którego wykonani wanie obiektów GuitarSpec em GuitarSpec, a nie klasa Inventor powinna zajmować się klasa y. 82 Rozdział 1.