Darmowy fragment publikacji:
Tytuł oryginału: The Object-Oriented Thought Process (4th Edition)
Tłumaczenie: Łukasz Piwko
ISBN: 978-83-246-8120-4
Authorized translation from the English language edition, entitled: THE OBJECT-ORIENTED THOUGHT
PROCESS, Fourth Edition; ISBN 0321861272; by Matt Weisfeld; published by Pearson Education, Inc,
publishing as Addison Wesley.
Copyright © 2013 Pearson Education, Inc.
All rights reserved. No part of this book may by reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording or by any information storage retrieval system,
without permission from Pearson Education, Inc.
Polish language edition published by HELION S.A. Copyright © 2014.
Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej
publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną,
fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje
naruszenie praw autorskich niniejszej publikacji.
Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich
właścicieli.
Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były
kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane
z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie
ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji
zawartych w książce.
Wydawnictwo HELION
ul. Kościuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/myobp4
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Printed in Poland.
• Kup książkę
• Poleć książkę
• Oceń książkę
• Księgarnia internetowa
• Lubię to! » Nasza społeczność
Spis treści
O autorze ...........................................................................13
Wst(cid:246)p ...............................................................................15
Tematyka ksi(cid:241)(cid:276)ki ..........................................................................15
Nowo(cid:264)ci w czwartym wydaniu .........................................................17
Adresaci ksi(cid:241)(cid:276)ki ............................................................................17
Metodyka .....................................................................................18
Konwencje ....................................................................................19
Kod (cid:274)ród(cid:228)owy ................................................................................19
Rozdzia(cid:228) 1 Podstawowe poj(cid:246)cia obiektowo(cid:264)ci .....................................21
Podstawowe poj(cid:246)cia ......................................................................22
Obiekty a stare systemy ................................................................22
Programowanie obiektowe a proceduralne .......................................24
Zamiana podej(cid:264)cia proceduralnego na obiektowe ............................27
Programowanie proceduralne ....................................................27
Programowanie obiektowe ........................................................28
Definicja obiektu ...........................................................................28
Dane obiektu ...........................................................................29
Zachowania obiektu .................................................................29
Definicja klasy ..............................................................................33
Tworzenie obiektów ..................................................................34
Atrybuty ...................................................................................35
Metody ...................................................................................36
Komunikaty .............................................................................36
Modelowanie klas przy u(cid:276)yciu diagramów UML ................................36
Hermetyzacja i ukrywanie danych ....................................................37
Interfejsy .................................................................................37
Implementacje .........................................................................38
Realistyczna ilustracja paradygmatu interfejsu i implementacji ....39
Model paradygmatu interfejs – implementacja ............................39
Kup książkęPoleć książkę
6 My(cid:264)lenie obiektowe w programowaniu
Dziedziczenie ................................................................................40
Nadklasy i podklasy .................................................................42
Abstrakcja ...............................................................................42
Zwi(cid:241)zek typu „jest” ..................................................................42
Polimorfizm ..................................................................................44
Kompozycja ..................................................................................47
Abstrakcja ...............................................................................47
Zwi(cid:241)zek typu „ma” ...................................................................47
Podsumowanie .............................................................................48
Listingi .........................................................................................48
TestPerson ..............................................................................48
TestShape ..............................................................................49
Rozdzia(cid:228) 2 My(cid:264)lenie w kategoriach obiektowych .................................51
Ró(cid:276)nica mi(cid:246)dzy interfejsem a implementacj(cid:241) ...................................52
Interfejs ..................................................................................54
Implementacja .........................................................................54
Przyk(cid:228)ad implementacji i interfejsu ............................................55
Zastosowanie my(cid:264)lenia abstrakcyjnego
w projektowaniu interfejsów .........................................................59
Minimalizowanie interfejsu .............................................................61
Okre(cid:264)lanie grupy docelowej ......................................................62
Zachowania obiektu .................................................................63
Ograniczenia (cid:264)rodowiska ..........................................................63
Identyfikowanie publicznych interfejsów .....................................63
Identyfikowanie implementacji ..................................................64
Podsumowanie .............................................................................65
(cid:273)ród(cid:228)a ..........................................................................................65
Rozdzia(cid:228) 3 Zaawansowane poj(cid:246)cia z zakresu obiektowo(cid:264)ci ..................67
Konstruktory .................................................................................67
Kiedy wywo(cid:228)ywany jest konstruktor ............................................68
Zawarto(cid:264)(cid:232) konstruktora ............................................................68
Konstruktor domy(cid:264)lny ...............................................................69
Zastosowanie wielu konstruktorów ............................................70
Projektowanie konstruktorów ....................................................73
Obs(cid:228)uga b(cid:228)(cid:246)dów ............................................................................74
Ignorowanie problemu ..............................................................74
Szukanie b(cid:228)(cid:246)dów i ko(cid:254)czenie dzia(cid:228)ania programu ........................75
Szukanie b(cid:228)(cid:246)dów i próba ich naprawienia ...................................75
Zg(cid:228)aszanie wyj(cid:241)tków .................................................................76
Poj(cid:246)cie zakresu .............................................................................78
Atrybuty lokalne .......................................................................78
Atrybuty obiektowe ...................................................................79
Atrybuty klasowe ......................................................................81
Kup książkęPoleć książkę
Spis tre(cid:264)ci
7
Przeci(cid:241)(cid:276)anie operatorów ................................................................82
Wielokrotne dziedziczenie ..............................................................83
Operacje obiektów .........................................................................84
Podsumowanie .............................................................................85
(cid:273)ród(cid:228)a ..........................................................................................85
Listingi .........................................................................................86
TestNumber ............................................................................86
Rozdzia(cid:228) 4 Anatomia klasy ..................................................................87
Nazwa klasy .................................................................................87
Komentarze ..................................................................................89
Atrybuty ........................................................................................89
Konstruktory .................................................................................91
Metody dost(cid:246)powe ........................................................................93
Metody interfejsu publicznego ........................................................95
Prywatne metody implementacyjne .................................................95
Podsumowanie .............................................................................96
(cid:273)ród(cid:228)a ..........................................................................................96
Listingi .........................................................................................96
TestCab ..................................................................................96
Rozdzia(cid:228) 5 Wytyczne dotycz(cid:241)ce projektowania klas .............................99
Modelowanie systemów (cid:264)wiata rzeczywistego ..................................99
Identyfikowanie interfejsów publicznych ........................................100
Minimalizacja interfejsu publicznego ........................................100
Ukrywanie implementacji ........................................................101
Projektowanie niezawodnych konstruktorów i destruktorów .............102
Projektowanie mechanizmu obs(cid:228)ugi b(cid:228)(cid:246)dów w klasie ......................103
Pisanie dokumentacji i stosowanie komentarzy ........................103
Tworzenie obiektów nadaj(cid:241)cych si(cid:246) do kooperacji .....................104
Wielokrotne u(cid:276)ycie kodu ..............................................................104
Rozszerzalno(cid:264)(cid:232) ...........................................................................105
Tworzenie opisowych nazw ......................................................105
Wyodr(cid:246)bnianie nieprzeno(cid:264)nego kodu .......................................106
Umo(cid:276)liwianie kopiowania i porównywania obiektów ...................107
Ograniczanie zakresu .............................................................107
Klasa powinna odpowiada(cid:232) sama za siebie ..............................108
Konserwacja kodu .......................................................................109
Iteracja .................................................................................110
Testowanie interfejsu .............................................................110
Wykorzystanie trwa(cid:228)o(cid:264)ci obiektów .................................................112
Serializacja i szeregowanie obiektów .......................................113
Podsumowanie ...........................................................................113
(cid:273)ród(cid:228)a ........................................................................................114
Kup książkęPoleć książkę
8 My(cid:264)lenie obiektowe w programowaniu
Listingi .......................................................................................114
TestMath ..............................................................................114
Rozdzia(cid:228) 6 Wytyczne dotycz(cid:241)ce projektowania klas ...........................115
Wytyczne dotycz(cid:241)ce projektowania ................................................115
Wykonanie odpowiedniej analizy ..............................................119
Okre(cid:264)lanie zakresu planowanych prac .....................................119
Gromadzenie wymaga(cid:254) ...........................................................120
Opracowywanie prototypu interfejsu u(cid:276)ytkownika ......................120
Identyfikowanie klas ...............................................................120
Definiowanie wymaga(cid:254) wobec ka(cid:276)dej z klas .............................121
Okre(cid:264)lenie warunków wspó(cid:228)pracy mi(cid:246)dzy klasami .....................121
Tworzenie modelu klas opisuj(cid:241)cego system .............................121
Tworzenie prototypu interfejsu u(cid:276)ytkownika ..............................121
Obiekty opakowuj(cid:241)ce ...................................................................122
Kod strukturalny ....................................................................122
Opakowywanie kodu strukturalnego .........................................124
Opakowywanie nieprzeno(cid:264)nego kodu .......................................125
Opakowywanie istniej(cid:241)cych klas ..............................................126
Podsumowanie ...........................................................................127
(cid:273)ród(cid:228)a ........................................................................................128
Rozdzia(cid:228) 7 Dziedziczenie i kompozycja ...............................................129
Wielokrotne wykorzystywanie obiektów ..........................................129
Dziedziczenie ..............................................................................131
Generalizacja i specjalizacja ...................................................133
Decyzje projektowe ................................................................134
Kompozycja ................................................................................136
Reprezentowanie kompozycji na diagramach UML ....................137
Czemu hermetyzacja jest podstaw(cid:241) technologii obiektowej .............138
Jak dziedziczenie os(cid:228)abia hermetyzacj(cid:246) ....................................139
Szczegó(cid:228)owy przyk(cid:228)ad wykorzystania polimorfizmu .....................141
Odpowiedzialno(cid:264)(cid:232) obiektów ....................................................141
Klasy abstrakcyjne, metody wirtualne i protoko(cid:228)y ......................145
Podsumowanie ...........................................................................146
(cid:273)ród(cid:228)a ........................................................................................147
Listingi .......................................................................................147
TestShape ............................................................................147
Rozdzia(cid:228) 8 Wielokrotne wykorzystanie kodu
— interfejsy i klasy abstrakcyjne ......................................149
Wielokrotne wykorzystanie kodu ...................................................149
Infrastruktura programistyczna .....................................................150
Co to jest kontrakt ......................................................................152
Klasy abstrakcyjne .................................................................153
Interfejsy ...............................................................................156
Kup książkęPoleć książkę
Spis tre(cid:264)ci
9
Wnioski .................................................................................158
Dowód kompilatora ................................................................160
Zawieranie kontraktu ..............................................................161
Punkty dost(cid:246)powe do systemu ................................................163
Przyk(cid:228)ad biznesu elektronicznego ..................................................163
Biznes elektroniczny ...............................................................164
Podej(cid:264)cie niezak(cid:228)adaj(cid:241)ce wielokrotnego wykorzystania kodu ......165
Rozwi(cid:241)zanie dla aplikacji biznesu elektronicznego ....................167
Model obiektowy UML ............................................................167
Podsumowanie ...........................................................................170
(cid:273)ród(cid:228)a ........................................................................................170
Listingi .......................................................................................170
TestShop ..............................................................................171
Rozdzia(cid:228) 9 Tworzenie obiektów .........................................................175
Relacje kompozycji ......................................................................175
Podzia(cid:228) procesu budowy na etapy .................................................177
Rodzaje kompozycji .....................................................................179
Agregacja ..............................................................................179
Asocjacja ..............................................................................180
(cid:227)(cid:241)czne wykorzystanie asocjacji i agregacji ................................181
Unikanie zale(cid:276)no(cid:264)ci .....................................................................182
Liczno(cid:264)(cid:232) .....................................................................................183
Kilka asocjacji .......................................................................184
Asocjacje opcjonalne ..............................................................186
Praktyczny przyk(cid:228)ad .....................................................................186
Podsumowanie ...........................................................................187
(cid:273)ród(cid:228)a ........................................................................................188
Rozdzia(cid:228) 10 Tworzenie modeli obiektowych .........................................189
Co to jest UML ............................................................................189
Struktura diagramu klasy .............................................................190
Atrybuty i metody ........................................................................192
Atrybuty .................................................................................192
Metody .................................................................................192
Okre(cid:264)lanie dost(cid:246)pno(cid:264)ci ...............................................................193
Dziedziczenie ..............................................................................194
Interfejsy ....................................................................................195
Kompozycja ................................................................................196
Agregacja ..............................................................................196
Asocjacja ..............................................................................197
Liczno(cid:264)(cid:232) .....................................................................................199
Podsumowanie ...........................................................................200
(cid:273)ród(cid:228)a ........................................................................................201
Kup książkęPoleć książkę 10 My(cid:264)lenie obiektowe w programowaniu
Rozdzia(cid:228) 11 Obiekty i dane przeno(cid:264)ne — XML .....................................203
Przeno(cid:264)no(cid:264)(cid:232) danych ....................................................................204
Rozszerzalny j(cid:246)zyk znaczników — XML ..........................................205
XML a HTML ...............................................................................206
XML a j(cid:246)zyki obiektowe ................................................................207
Wymiana danych mi(cid:246)dzy firmami ..................................................208
Sprawdzanie poprawno(cid:264)ci dokumentu wzgl(cid:246)dem DTD ....................208
Integrowanie DTD z dokumentem XML ..........................................210
Kaskadowe arkusze stylów ..........................................................216
Notacja obiektowa j(cid:246)zyka JavaScript (JSON) ..................................217
Podsumowanie ...........................................................................222
(cid:273)ród(cid:228)a ........................................................................................223
Rozdzia(cid:228) 12 Obiekty trwa(cid:228)e — serializacja i relacyjne bazy danych .......225
Podstawy trwa(cid:228)o(cid:264)ci obiektów ........................................................225
Zapisywanie obiektu w pliku p(cid:228)askim ............................................226
Serializacja pliku ....................................................................227
Jeszcze raz o implementacji i interfejsach ................................229
Serializacja metod .................................................................231
Serializacja przy u(cid:276)yciu j(cid:246)zyka XML ...............................................231
Zapisywanie danych w relacyjnej bazie danych ...............................233
Dost(cid:246)p do relacyjnej bazy danych ............................................235
Podsumowanie ...........................................................................237
(cid:273)ród(cid:228)a ........................................................................................237
Listingi .......................................................................................238
Klasa Person .........................................................................238
Rozdzia(cid:228) 13 Obiekty w us(cid:228)ugach sieciowych, aplikacjach mobilnych
i aplikacjach hybrydowych ................................................241
Ewolucja technik przetwarzania rozproszonego ..............................241
Obiektowe skryptowe j(cid:246)zyki programowania ...................................242
Weryfikacja danych za pomoc(cid:241) j(cid:246)zyka JavaScript ...........................245
Obiekty na stronach internetowych ...............................................248
Obiekty JavaScript .................................................................248
Kontrolki na stronach internetowych ........................................250
Odtwarzacze d(cid:274)wi(cid:246)ku .............................................................250
Odtwarzacze filmów ................................................................251
Animacje Flash ......................................................................252
Obiekty rozproszone i systemy przedsi(cid:246)biorstw ..............................252
CORBA ..................................................................................254
Definicja us(cid:228)ugi sieciowej ........................................................257
Kod us(cid:228)ug sieciowych .............................................................261
Representational State Transfer (ReST) ...................................263
Podsumowanie ...........................................................................264
(cid:273)ród(cid:228)a ........................................................................................264
Kup książkęPoleć książkę
Spis tre(cid:264)ci
11
Rozdzia(cid:228) 14 Obiekty w aplikacjach typu klient-serwer ..........................265
Model klient-serwer .....................................................................265
Rozwi(cid:241)zanie w(cid:228)asno(cid:264)ciowe ..........................................................266
Kod obiektu do serializacji ......................................................266
Kod klienta ............................................................................267
Kod serwera ..........................................................................269
Uruchamianie aplikacji ...........................................................270
Technika z wykorzystaniem XML ...................................................271
Definicja obiektu ....................................................................272
Kod klienta ............................................................................273
Kod serwera ..........................................................................274
Uruchamianie programu .........................................................276
Podsumowanie ...........................................................................276
(cid:273)ród(cid:228)a ........................................................................................276
Listingi .......................................................................................277
Rozdzia(cid:228) 15 Wzorce projektowe ..........................................................279
Historia wzorców projektowych .....................................................280
Wzorzec MVC j(cid:246)zyka Smalltalk .....................................................280
Rodzaje wzorców projektowych .....................................................283
Wzorce konstrukcyjne .............................................................283
Wzorce strukturalne ...............................................................288
Wzorce czynno(cid:264)ciowe .............................................................290
Antywzorce .................................................................................291
Podsumowanie ...........................................................................292
(cid:273)ród(cid:228)a ........................................................................................292
Listingi .......................................................................................293
Counter.cs ............................................................................293
Singleton.cs ..........................................................................293
MailTool.cs ...........................................................................294
MailInterface.cs .....................................................................294
MyMailTool.cs .......................................................................295
Adapter.cs ............................................................................295
Iterator.cs .............................................................................296
Skorowidz.........................................................................297
Kup książkęPoleć książkę 12 My(cid:264)lenie obiektowe w programowaniu
Kup książkęPoleć książkę7
Dziedziczenie i kompozycja
D
ziedziczenie i kompozycja odgrywają w projektowaniu systemów obiektowych
bardzo ważną rolę. W istocie wiele najtrudniejszych i najciekawszych decyzji można
sprowadzić do wyboru między tymi dwoma.
Decyzje te w miarę ewoluowania technik obiektowych stawały się coraz ciekawsze.
Do najciekawszych dyskusji, jakie toczą się na tematy związane z obiektowością,
należy spór dotyczący dziedziczenia. Mimo że dziedziczenie jest jednym z fundamentów
programowania obiektowego (aby język uznano za obiektowy, musi umożliwiać
korzystanie z tej techniki), wielu programistów unika go, wybierając inne metody
projektowe.
Zarówno dziedziczenie, jak i kompozycja to techniki umożliwiające wielokrotne
wykorzystanie kodu. Dziedziczenie, jak sama nazwa wskazuje, to technika polegająca
na dziedziczeniu atrybutów i zachowań przez klasy po innych klasach. Wytwarzają się
prawdziwe relacje typu rodzic – dziecko. Dziecko (czyli podklasa) dziedziczy bezpośrednio
po swoim rodzicu (czyli nadklasie).
Kompozycja, również jak nazwa wskazuje, oznacza tworzenie obiektów przy użyciu
innych obiektów. W rozdziale tym opiszę wyraźne i bardziej subtelne różnice między
tymi dwiema technikami Zacznę od tego, kiedy w ogóle należy stosować każdą z nich.
Wielokrotne wykorzystywanie obiektów
Najważniejszym powodem, dla którego powstały techniki dziedziczenia i kompozycji,
jest chęć wielokrotnego wykorzystania obiektów. Mówiąc krótko, klasy (które służą do
tworzenia obiektów) można tworzyć, wykorzystując inne klasy za pomocą dziedziczenia
lub kompozycji. Oznacza to, że techniki te są jedynymi, za pomocą których można
wielokrotnie wykorzystać kod istniejących klas.
Dziedziczenie reprezentuje związek typu „jest”, którego definicję przedstawiłem
w rozdziale 1. „Wstęp do obiektowości”. Na przykład pies jest ssakiem.
Kompozycja to technika polegająca na budowaniu skomplikowanych klas przy użyciu
innych klas — to tworzenie pewnego rodzaju kolekcji. Nie implikuje relacji typu
Kup książkęPoleć książkę130
Rozdzia(cid:228) 7. Dziedziczenie i kompozycja
rodzic – dziecko. Zasadniczo obiekty złożone składają się z innych obiektów. Kompozycja
reprezentuje związek typu „ma”. Na przykład samochód ma silnik. Zarówno samochód,
jak i silnik są odrębnymi obiektami. Jednak ten pierwszy jest obiektem złożonym, który
zawiera (ma) obiekt silnika. W istocie nawet obiekt potomny może być złożony z innych
obiektów. Na przykład silnik może zawierać cylindry. Wówczas można powiedzieć,
że samochód ma cylinder, a nawet kilka.
Gdy technologia obiektowa trafiła do szerokiego grona odbiorców, dziedziczenie stało
się wielkim hitem. Możliwość pisania klas, których funkcjonalność można było później
wykorzystywać w wielu innych klasach, uznawano za najważniejszą zaletę obiektowości.
Nazwano to wielokrotnym wykorzystaniem kodu — wyrazem tej techniki było właśnie
dziedziczenie.
Po pewnym jednak czasie blask dziedziczenia nieco przygasł. Niektórzy nawet
kwestionują zasadność stosowania tej techniki. Peter Coad i Mark Mayfield w swojej
książce Java Design zamieścili nawet cały rozdział zatytułowany „Design with Composition
Rather Than Inheritance” (Wykorzystywanie w projektowaniu kompozycji zamiast
dziedziczenia). Wiele wczesnych platform obiektowych nie obsługiwało nawet tej techniki
w czystej postaci. W języku Visual Basic rzeczywistą obsługę dziedziczenia wprowadzono
dopiero w wersji .NET. Platformy takie jak Microsoft COM zostały oparte na dziedziczeniu
interfejsów. Temat ten szczegółowo opisuję w rozdziale 8. „Modele i wielokrotne
wykorzystanie kodu: projektowanie z wykorzystaniem interfejsów i klas abstrakcyjnych”.
Dziś zastosowanie dziedziczenia wciąż jest przedmiotem ożywionych dyskusji. Będące
częścią technik dziedziczenia klasy abstrakcyjne w niektórych językach programowania,
takich jak Objective-C, nie są bezpośrednio dostępne. Używa się interfejsów, mimo
że nie mają one wszystkich cech dostępnych dzięki klasom abstrakcyjnym.
Dobrze zdać sobie sprawę z tego, że wynikiem całej tej dyskusji na temat używania
lub nieużywania dziedziczenia i kompozycji będzie w końcu opracowanie jakiegoś
kompromisowego stanowiska. Tak jak we wszystkich debatach natury filozoficznej
i tu po obu stronach stoją fanatyczni zwolennicy. Na szczęście, jak zawsze w takich
sytuacjach, gorąca dyskusja doprowadziła do lepszego zrozumienia sposobów
wykorzystania technologii, które jej podlegają.
W dalszej części rozdziału wyjaśnię, dlaczego niektórzy uważają, iż dziedziczenia
należy unikać, a w jego miejsce stosować kompozycję. Jest to delikatny i skomplikowany
problem. W rzeczywistości przy projektowaniu klas można wykorzystywać zarówno
dziedziczenie, jak i kompozycję, które mają swoje ustalone miejsca w obiektowości.
Trzeba przynajmniej rozumieć obie te techniki, aby móc dokonać rozsądnego wyboru
jednej z nich.
To, że dziedziczenie jest często źle stosowane i nadużywane, jest wynikiem braku
zrozumienia tej techniki, a nie jakąś jej fundamentalną usterką.
Należy pamiętać, że zarówno dziedziczenie, jak i kompozycja to ważne techniki służące
do budowy systemów obiektowych. Projektanci i programiści powinni odpowiednio się
przygotować od strony merytorycznej, aby zrozumieć ich mocne i słabe strony. To pozwoli
im stosować obie te techniki w odpowiedni sposób.
Kup książkęPoleć książkęDziedziczenie
131
Dziedziczenie
W rozdziale 1. dziedziczenie zdefiniowane zostało jako system, w którym klasy potomne
dziedziczą atrybuty i zachowania po klasach rodzicach. Jednak na temat dziedziczenia
można powiedzieć znacznie więcej, co zrobię w tym rozdziale.
W rozdziale 1. napisałem, że dziedziczenie można rozpoznać bardzo prostą metodą.
Jeśli stwierdzenie „Klasa B jest rodzajem klasy A” jest prawdziwe, związek taki może być
określony jako dziedziczenie.
Zwi(cid:241)zek typu „jest”
Jedna z najwa(cid:276)niejszych zasad projektowania obiektowego g(cid:228)osi, (cid:276)e interfejs
publiczny powinien by(cid:232) reprezentowany przez zwi(cid:241)zek typu „jest”.
Skorzystam z przedstawionego w rozdziale 1. przykładu rodziny ssaków. Załóżmy,
że utworzono klasę Dog. Psy mają kilka charakterystycznych dla siebie zachowań,
które odróżniają je od np. kotów. Na potrzeby tego przykładu niech będą to szczekanie
(ang. bark) i sapanie (ang. pant). Na tej podstawie można utworzyć klasę Dog z dwoma
zachowaniami i dwoma atrybutami (rysunek 7.1).
Rysunek 7.1. Diagram klasy Dog
Teraz utworzymy klasę GoldenRetriever. Można by było utworzyć całkiem nową klasę
z takimi samymi zachowaniami jak w klasie Dog. Można jednak też dojść do następującego
rozsądnego wniosku: golden retriever to jest pies. Można zatem wykorzystać dziedziczenie
i utworzyć klasę GoldenRetriever jako podklasę klasy Dog, z której zostaną odziedziczone
wszystkie atrybuty i zachowania (rysunek 7.2).
Rysunek 7.2. Klasa GoldenRetriever dziedziczy po Dog
Kup książkęPoleć książkę132
Rozdzia(cid:228) 7. Dziedziczenie i kompozycja
Dzięki temu klasa GoldenRetriever poza własnymi zachowaniami będzie zawierała
wszystkie te, które są charakterystyczne ogólnie dla psów. Jest to korzystne z kilku
powodów. Po pierwsze, przy tworzeniu tej klasy nie trzeba było od nowa wynajdywać
koła, pisząc po raz drugi metody bark i pant. To nie tylko pozwala zaoszczędzić na czasie,
jeśli chodzi o pisanie kodu, ale również umożliwia ograniczenie czynności związanych
z testowaniem i konserwacją. Metody bark i pant zostały napisane jeden raz, przy założeniu,
że odpowiednio je przetestowano przy okazji pisania klasy Dog, i nie trzeba już ich
szczegółowo testować. Ale pewne testy trzeba przeprowadzić jeszcze raz, bo pojawiły się
nowe interfejsy itd.
Spróbuję w pełni wykorzystać zalety dziedziczenia. W tym celu utworzę drugą podklasę
klasy Dog, o nazwie LhasaApso. Podczas gdy retrievery są hodowane, aby aportowały, lhasa
apso mogą służyć jako strażnicy. Mają wyczulone zmysły i kiedy wyczują coś podejrzanego,
od razu zaczynają szczekać. Nie są jednak agresywne. Można więc utworzyć klasę LhasaApso,
która, podobnie jak GoldenRetriever, będzie dziedziczyła po klasie Dog (rysunek 7.3).
Rysunek 7.3. Klasa LhasaApso dziedziczy po klasie Dog
Testowanie nowego kodu
W opisywanym tu przypadku klasy GoldenRetriever metody bark i pant powinny
zosta(cid:232) napisane, przetestowane i oczyszczone z b(cid:228)(cid:246)dów ju(cid:276) na etapie prac nad
klas(cid:241) Dog. Teoretycznie kod ich powinien by(cid:232) ju(cid:276) niezawodny i gotowy do u(cid:276)ytku
w ró(cid:276)nych sytuacjach. Jednak to, (cid:276)e kod mo(cid:276)na wykorzysta(cid:232) wielokrotnie, nie
oznacza, (cid:276)e nie trzeba go testowa(cid:232). Mimo i(cid:276) wydaje si(cid:246) to ma(cid:228)o prawdopodobne,
rasa golden retriever mo(cid:276)e mie(cid:232) pewne cechy, które w jaki(cid:264) sposób b(cid:246)d(cid:241) zak(cid:228)óca(cid:232)
dzia(cid:228)anie tego kodu. G(cid:228)ówna zasada jest taka, (cid:276)e zawsze nale(cid:276)y testowa(cid:232) nowy
kod. Ka(cid:276)dy zwi(cid:241)zek dziedziczenia wytwarza nowy kontekst, w którym mog(cid:241) by(cid:232)
u(cid:276)ywane odziedziczone metody. W kompletnej strategii testowania wszystkie
takie konteksty powinny by(cid:232) uwzgl(cid:246)dnione.
Kolejną zaletą dziedziczenia jest to, że kod metod bark() i pant() znajduje się tylko
w jednym miejscu. Załóżmy, że trzeba zmodyfikować kod pierwszej z nich. Wówczas
wystarczy tylko zmiana w klasie Dog, a będzie ona od razu przejęta przez klasy LhasaApso
i GoldenRetriever.
Kup książkęPoleć książkęDziedziczenie
133
Czy jest tu jakiś problem? Na tym poziomie wydaje się, że model dziedziczenia sprawdza
się doskonale. Czy można jednak mieć pewność, że wszystkie psy mają takie zachowania,
które zdefiniowano w klasie Dog?
W swojej książce Effective C++ Scott Meyers podał świetny przykład ilustrujący dylemat
dotyczący wykorzystania dziedziczenia w projektowaniu. Weźmy na przykład klasę
reprezentującą ptaka. Jedną z najbardziej charakterystycznych cech ptaków jest to,
że potrafią latać. Tworzymy zatem klasę Bird z metodą fly. Od razu nasuwa się pytanie,
co zrobić z pingwinami i strusiami? Są ptakami, a nie potrafią fruwać. Można by było to
zachowanie lokalnie przesłonić, ale nazwa metody pozostałaby taka sama. A przecież nie
byłoby sensu tworzyć metody fly dla ptaka, który nie umie latać, a tylko chodzić jak kaczka.
To może prowadzić do powstawania potencjalnie groźnych problemów. Gdyby na
przykład pingwin miał metodę fly, zrozumiałe, że zechciałby ją kiedyś wypróbować.
Gdyby jednak metoda ta została przesłonięta i odpowiadające jej zachowanie
w rzeczywistości nie istniałoby, pingwin byłby bardzo zdziwiony po wywołaniu metody
fly. Wyobraź sobie rozgoryczenie pingwina, który wywołał metodę fly, a uzyskał jedynie
kaczy chód.
W przypadku klasy psów założono, że wszystkie psy szczekają. Są jednak takie rasy,
które tego nie robią, np. basenji. Psy tej rasy nie szczekają, tylko jodłują. Czy należy zatem
przeprowadzić rewizję pierwotnego projektu? Jak by po niej wyglądał? Na rysunku 7.4
został przedstawiony lepszy model hierarchii klasy Dog.
Rysunek 7.4. Hierarchia klasy Dog
Generalizacja i specjalizacja
Jako przykładem posłużę się hierarchią klasy Dog. Na początku utworzono jedną klasę
o nazwie Dog, w której zdefiniowano pewne cechy wspólne wszystkich psów. Koncepcja
ta jest czasami nazywana generalizacją-specjalizacją i stanowi kolejną rzecz, którą należy
wziąć pod uwagę przy wykorzystywaniu dziedziczenia. Chodzi o to, że im niższy poziom
Kup książkęPoleć książkę134
Rozdzia(cid:228) 7. Dziedziczenie i kompozycja
drzewa dziedziczenia, tym bardziej specyficzna klasa. Najbardziej ogólna znajduje się
na samym wierzchołku. W przedstawionym przykładzie jest to klasa Dog. Najbardziej
specyficzne są klasy odpowiadające poszczególnym rasom psów — GoldenRetriever,
LhasaApso i Basenji. Zasadą dziedziczenia jest przechodzenie od najbardziej ogólnego
przypadku do najbardziej specyficznego poprzez wyodrębnianie cech wspólnych.
W modelu dziedziczenia klasy Dog wyszliśmy z założenia, że mimo iż golden retrievery
mają pewne zachowania, których nie mają psy lhasa apso, rasy te mają także pewne cechy
wspólne — na przykład jedne i drugie szczekają i sapią. Następnie zdaliśmy sobie sprawę,
że są takie psy, które nie szczekają, tylko jodłują. Zmusiło nas to do wydzielenia szczekania
do osobnej klasy o nazwie BarkingDog. Jodłowanie znalazło się w klasie YodelingDog. Wiemy
też, że mimo pewnych różnic jedne i drugie psy mają ze sobą coś wspólnego — sapanie.
Dlatego klasy YodelingDog i BarkingDog dziedziczą po klasie Dog. Teraz klasa Basenji może
dziedziczyć po YodelingDog, a klasy GoldenRetriever i LhasaApso po BarkingDog.
Można by było uniknąć tworzenia dwóch osobnych klas dla psów jodłujących
i szczekających. Wówczas szczekanie i jodłowanie można by było zaimplementować
jako część klasy reprezentującej każdą rasę — odgłosy wydawane przez każdego psa mogą
brzmieć inaczej. Jest to tylko jeden przykład decyzji projektowych, jakie czasami trzeba
podejmować. Prawdopodobnie najlepszym rozwiązaniem byłoby zaimplementować
szczekanie i jodłowanie jako interfejsy, które zostaną opisane w rozdziale 8. „Modele
i wielokrotne wykorzystanie kodu: projektowanie z wykorzystaniem interfejsów i klas
abstrakcyjnych”.
Decyzje projektowe
Teoretycznie wyodrębnienie jak największej liczby cech wspólnych jest bardzo dobrym
pomysłem. Jednak jak to zwykle bywa w projektowaniu, czasami można przedobrzyć.
Podczas gdy znajdowanie cech wspólnych kilku klas jest dobrym sposobem na jak
najwierniejsze odwzorowanie świata rzeczywistego, może już nie tak dobrze reprezentować
sam model. Im więcej tego typu działań, tym bardziej skomplikowany robi się system.
Powstaje dylemat: czy bardziej potrzebny jest precyzyjny model, czy mniej skomplikowany
system? Decyzję należy podjąć w zależności od sytuacji. Nie ma sztywnego zestawu
wskazówek, które pomogą w jej podjęciu.
W czym komputery nie s(cid:241) dobre
Oczywi(cid:264)cie system komputerowy mo(cid:276)e tylko w przybli(cid:276)eniu modelowa(cid:232) (cid:264)wiat
rzeczywisty. Komputery doskonale sprawdzaj(cid:241) si(cid:246) w wykonywaniu oblicze(cid:254),
ale gorzej radz(cid:241) sobie z operacjami abstrakcyjnymi.
Na przykład rozbicie klasy Dog na YodelingDog i BarkingDog lepiej pozwala
odwzorować świat rzeczywisty niż przyjęcie założenia, że wszystkie psy szczekają,
ale wiąże się z tym dodatkowa komplikacja systemu.
Kup książkęPoleć książkęDziedziczenie
135
Z(cid:228)o(cid:276)ono(cid:264)(cid:232) modelu
Na takim poziomie jak w przedstawionym przyk(cid:228)adzie dodanie dwóch klas
nie komplikuje systemu a(cid:276) tak, (cid:276)eby go uczyni(cid:232) trudnym do ogarni(cid:246)cia.
Natomiast w du(cid:276)ych systemach tego rodzaju decyzje, je(cid:264)li s(cid:241) podejmowane
wielokrotnie, mog(cid:241) szybko doprowadzi(cid:232) do du(cid:276)ej komplikacji. W takich
systemach najlepsz(cid:241) strategi(cid:241) jest bronienie za wszelk(cid:241) cen(cid:246) prostoty.
Zdarzają się sytuacje, w których doprecyzowanie modelu nie powoduje jego
dodatkowego skomplikowania. Wyobraźmy sobie hodowcę psów, który podpisał
kontrakt na system informacji o wszystkich swoich psach. Model podzielony na psy
szczekające i jodłujące działa bardzo dobrze. Wyobraźmy sobie, że hodowca ten nie
hoduje psów, które jodłują — nigdy tego nie robił i nie planuje zacząć. Wówczas nie ma
raczej sensu komplikować systemu niepotrzebnym podziałem. System będzie prostszy,
a jego funkcjonalność w ogóle nie zmaleje.
Podjęcie decyzji, czy zaprojektować prostszy, czy bardziej funkcjonalny system,
jest bardzo trudne. Główną zasadą jest to, aby tworzyć systemy elastyczne i na tyle mało
skomplikowane, że nie zawalą się pod własnym ciężarem.
Ważnym czynnikiem mającym wpływ na decyzję są bieżące i przyszłe koszty.
Mimo iż czasami może się wydawać, że właściwym podejściem jest zaprojektowanie
kompletnego i elastycznego systemu, dodatkowa funkcjonalność może tylko w niewielkim
stopniu przynosić korzyści — po prostu inwestycja może się nie opłacić. Czy warto
by było rozszerzyć projekt klasy Dog na dodatkowe zwierzęta z tej rodziny, np. hieny
i lisy (rysunek 7.5)?
Rysunek 7.5. Rozszerzony model rodziny psowatych
Projekt taki mógłby być przydatny dla systemu obsługi zoo, ale dla hodowcy psów
udomowionych nie jest już raczej odpowiedni.
Jak widać, każdy projekt wymaga jakichś kompromisów.
Kup książkęPoleć książkę136
Rozdzia(cid:228) 7. Dziedziczenie i kompozycja
Podejmuj decyzje projektowe, my(cid:264)l(cid:241)c przysz(cid:228)o(cid:264)ciowo
W tym momencie mo(cid:276)na by by(cid:228)o powiedzie(cid:232) „Nigdy nie mów nigdy”. Mimo (cid:276)e teraz
hodowca nie ma u siebie jod(cid:228)uj(cid:241)cych psów, w przysz(cid:228)o(cid:264)ci mo(cid:276)e zacz(cid:241)(cid:232) je hodowa(cid:232).
Je(cid:264)li nie przygotuje si(cid:246) projektu na tak(cid:241) ewentualno(cid:264)(cid:232) od razu, zmiana systemu
w przysz(cid:228)o(cid:264)ci mo(cid:276)e by(cid:232) znacznie bardziej kosztowna. Jest to jeszcze jedna z decyzji,
które nale(cid:276)y podj(cid:241)(cid:232). Metod(cid:246) bark() mo(cid:276)na by by(cid:228)o przes(cid:228)oni(cid:232), aby odpowiada(cid:228)a
jod(cid:228)owaniu. Jest to jednak rozwi(cid:241)zanie sprzeczne z intuicj(cid:241), poniewa(cid:276) wi(cid:246)kszo(cid:264)(cid:232)
ludzi spodziewa si(cid:246), (cid:276)e metoda o nazwie bark() oznacza szczekanie.
Kompozycja
Traktowanie obiektów jako zbiorów innych obiektów jest naturalnym tokiem rozumowania.
Odbiornik telewizyjny zawiera urządzenie odbiorcze i ekran. Komputer zawiera kartę
graficzną, klawiaturę i napędy. Komputer można traktować jako jeden duży obiekt,
ale podłączana do niego pamięć Flash również jest obiektem. Komputer można otworzyć
i wyjąć z niego dysk twardy. W zasadzie dysk ten można by było nawet przenieść do innego
komputera. O tym, że dysk jest samodzielnym obiektem, przekonuje fakt, że można go użyć
w wielu różnych komputerach.
Klasycznym przykładem kompozycji obiektów jest samochód. Przykład ten jest
powtarzany jak mantra w wielu książkach, na kursach i na wykładach. Mimo że części
zamienne wcześniej stosowano do naprawy karabinów, dla większości ludzi kwintesencją
montażu z gotowych komponentów jest wynaleziona przez Henry’ego Forda linia
montażowa. Dlatego wydaje się naturalną koleją rzeczy, że samochód stał się najważniejszym
punktem odniesienia w projektowaniu systemów obiektowych.
Dla większości ludzi oczywiste jest, że samochód ma silnik. Oczywiście poza nim
w samochodzie można znaleźć wiele innych rzeczy, np. koła, kierownicę i radio
samochodowe. Każdy obiekt składający się z innych obiektów, które są jego polami,
nazywa się agregatem lub obiektem złożonym (rysunek 7.6).
Rysunek 7.6. Przyk(cid:228)ad kompozycji
Agregacja, asocjacja i kompozycja
Moim zdaniem s(cid:241) tylko dwa sposoby na wielokrotne wykorzystanie kodu klasy
— dziedziczenie i kompozycja. Szczegó(cid:228)owy opis tej drugiej metody znajduje si(cid:246)
w rozdziale 9. „Tworzenie obiektów” — a mówi(cid:241)c dok(cid:228)adniej: agregacji i asocjacji.
W ksi(cid:241)(cid:276)ce tej przyj(cid:241)(cid:228)em, (cid:276)e agregacja i asocjacja to rodzaje kompozycji, chocia(cid:276)
opinie na ten temat s(cid:241) podzielone.
Kup książkęPoleć książkęReprezentowanie kompozycji na diagramach UML
Do zaznaczenia na diagramie UML, że samochód ma kierownicę, służy notacja
przedstawiona na rysunku 7.7.
Kompozycja
137
Rysunek 7.7. Reprezentacja kompozycji w UML
Agregacja, asocjacja i UML
W tej ksi(cid:241)(cid:276)ce agregacje (np. silnik jest cz(cid:246)(cid:264)ci(cid:241) samochodu) s(cid:241) na diagramach UML
oznaczane lini(cid:241) ci(cid:241)g(cid:228)(cid:241) zako(cid:254)czon(cid:241) rombem. Asocjacj(cid:246) oznacza sama linia bez
rombu na ko(cid:254)cu (np. samodzielna klawiatura obs(cid:228)uguj(cid:241)ca samodzielny komputer).
Zależy zauważyć, że romb na linii łączącej klasę Car z klasą SteeringWheel znajduje
się po stronie tej pierwszej. Oznacza to, że samochód (Car) zawiera (ma) kierownicę
(SteeringWheel).
Rozwinę ten przykład. Załóżmy, że żaden z użytych w tym projekcie obiektów nie
wykorzystuje dziedziczenia. Wszystkie relacje między obiektami ograniczają się wyłącznie
do kompozycji, która jest wielopoziomowa. Jest to oczywiście uproszczony przykład,
ponieważ w każdym samochodzie jest nieporównywalnie więcej relacji zachodzących
między różnymi obiektami. Jednak tutaj chodzi tylko o zilustrowanie, na czym polega
kompozycja.
Załóżmy, że samochód składa się z silnika, radia i drzwi.
Przyjąć do wiadomości fakt, że samochód składa się z silnika, radia i drzwi, jest łatwo,
ponieważ większość osób tak właśnie wyobraża sobie samochody. Jednak przy projektowaniu
systemów oprogramowania należy pamiętać, że podobnie jak samochody, tak i pozostałe
obiekty składają się z innych obiektów. W zasadzie liczba węzłów i gałęzi, które można
dodać do drzewa klas, jest nieograniczona.
Ile drzwi i odbiorników radiowych
Nale(cid:276)y zauwa(cid:276)y(cid:232), (cid:276)e ka(cid:276)dy normalny samochód ma wi(cid:246)cej ni(cid:276) jedne drzwi.
Niektóre s(cid:241) dwu-, a inne czterodrzwiowe. Czasami wyró(cid:276)nia si(cid:246) nawet pi(cid:241)te
drzwi. Analogicznie wcale nie jest powiedziane, (cid:276)e ka(cid:276)dy samochód ma radio.
Mo(cid:276)e mie(cid:232) jedno lub wcale. Raz nawet widzia(cid:228)em samochód z dwoma osobnymi
zestawami nag(cid:228)o(cid:264)nienia. Tego rodzaju sytuacje szczegó(cid:228)owo opisz(cid:246) w rozdziale 9.
Dla uproszczenia przyjmiemy, (cid:276)e samochód ma tylko jedne drzwi (np. wy(cid:264)cigowy)
i jedno radio.
Kup książkęPoleć książkę138
Rozdzia(cid:228) 7. Dziedziczenie i kompozycja
Na rysunku 7.8 pokazano model obiektowy samochodu z silnikiem, radiem i drzwiami.
Rysunek 7.8. Hierarchia klasy Car
Należy zauważyć, że wszystkie trzy obiekty składające się na ten samochód same
składają się z innych obiektów. Silnik zawiera tłoki i świece zapłonowe. Radio zawiera
odbiornik radiowy i odtwarzacz płyt CD. Drzwi mają klamkę. Ponadto jest jeszcze jeden
dodatkowy poziom. Radio zawiera urządzenie odbiorcze. Można by było jeszcze zaznaczyć
fakt, że klamka ma zamek. CD ma przycisk do szybkiego przewijania w przód itp. Można
by też było wyjść o jeden poziom za urządzenie odbiorcze i utworzyć obiekt reprezentujący
antenę. Poziom złożoności modelu obiektowego całkowicie zależy od projektującego.
Z(cid:228)o(cid:276)ono(cid:264)(cid:232) modelu
Zbyt intensywne wykorzystanie kompozycji w przypadku dziedziczenia — analogicznie
jak w problemie ze szczekaj(cid:241)cymi i jod(cid:228)uj(cid:241)cymi psami — mo(cid:276)e równie(cid:276) doprowadzi(cid:232)
do skomplikowania systemu. Granica mi(cid:246)dzy takim modelem obiektowym,
który jest wystarczaj(cid:241)co precyzyjny, aby wszystko by(cid:228)o jasne, a takim, w którym
zbytnia szczegó(cid:228)owo(cid:264)(cid:232) zaciemnia obraz, jest trudna do zrozumienia i wyczucia.
Czemu hermetyzacja jest podstaw(cid:241)
technologii obiektowej
Hermetyzacja naprawdę jest podstawą obiektowości. Każdy opis interfejsu i implementacji
dotyczy w rzeczywistości hermetyzacji. Podstawowe pytanie dotyczy tego, co w klasie
powinno być udostępnione na zewnątrz, a co ukryte. Kwestia hermetyzacji ma się tak
Kup książkęPoleć książkęCzemu hermetyzacja jest podstaw(cid:241) technologii obiektowej
139
samo do danych jak i zachowań. W przypadku klas najważniejsza decyzja projektowa
dotyczy hermetyzacji zarówno danych, jak i zachowań.
Stephen Gilbert i Bill McCarty definiują hermetyzację jako „pakowanie programu
— dzielenie klas na dwie części: interfejs i implementację”. Jest to przesłanie, które zostało
już wielokrotnie w tej książce powtórzone.
Jak ma się hermetyzacja do dziedziczenia i jaki ma to związek z tematem tego rozdziału?
Chodzi o pewien paradoks paradygmatu obiektowego. Hermetyzacja jest tak ważna
w obiektowości, że stanowi jedną z jej kardynalnych zasad. Dziedziczenie także jest uznawane
za jeden z trzech filarów obiektowości. Jednak dziedziczenie w pewnym sensie łamie
zasadę hermetyzacji! Jak to możliwe? Czy dwie najważniejsze koncepcje obiektowości
mogą stać ze sobą w sprzeczności? Sprawdźmy to.
Jak dziedziczenie os(cid:228)abia hermetyzacj(cid:246)
Jak wiadomo, hermetyzacja polega na dzieleniu pakietów, jakimi są klasy, na publiczny
interfejs i prywatną implementację. Zasadniczo wszystko, co nie jest potrzebne innym
klasom, powinno być ukryte.
Peter Coad i Mark Mayfield dowodzą, że dziedziczenie z natury rzeczy osłabia
hermetyzację w obrębie hierarchii klas. Zwracają uwagę na specyficzne ryzyko: dziedziczenie
implikuje silną hermetyzację między niezwiązanymi klasami, ale słabą między podklasami
i nadklasami.
Problem polega na tym, że jeśli podklasy odziedziczą po nadklasie implementację,
która zostanie później zmieniona, zmiany te będą widoczne także we wszystkich podklasach.
Ten efekt odbicia może potencjalnie mieć wpływ na wszystkie podklasy. Na pierwszy rzut
oka może się wydawać, że to niewielki problem. Ale wiadomo jednak, że czasami efekty
tego mogą być trudne do przewidzenia. Dobrym przykładem jest testowanie, które z tego
powodu może zamienić się w koszmar. W rozdziale 6. „Projektowanie z wykorzystaniem
obiektów” objaśniłem, w jaki sposób hermetyzacja ułatwia testowanie systemów.
Teoretycznie, jeśli klasa Cabbie (rysunek 7.9) będzie miała poprawnie zaprojektowane
interfejsy publiczne, żadne zmiany ich implementacji nie powinny być widoczne dla innych
klas. Jednak zmiana w nadklasie nigdy nie spowoduje takich samych zmian w podklasach.
Czy już widać, na czym polega problem?
Rysunek 7.9. Diagram UML klasy Cabbie
Kup książkęPoleć książkę140
Rozdzia(cid:228) 7. Dziedziczenie i kompozycja
Gdyby inne klasy były bezpośrednio zależne od implementacji klasy Cabbie, testowanie
stałoby się znacznie trudniejsze, a może nawet niemożliwe.
Pami(cid:246)taj o testowaniu
Nawet mimo wykorzystywania hermetyzacji nadal nale(cid:276)y testowa(cid:232) klasy
korzystaj(cid:241)ce z klasy Cabbie, aby sprawdzi(cid:232), czy jej modyfikacje nie wywo(cid:228)a(cid:228)y
jakich(cid:264) niepo(cid:276)(cid:241)danych skutków.
Jeśli później zostanie utworzona podklasa klasy Cabbie o nazwie PartTimeCabbie
(która odziedziczy po niej implementację), zmiana implementacji tej pierwszej będzie
miała bezpośredni wpływ na drugą.
Jako przykład niech posłuży diagram UML widoczny na rysunku 7.10. PartTimeCabbie
to podklasa klasy Cabbie. Zatem dziedziczy ona implementację swojej nadklasy, z metodą
giveDirections()włącznie. Gdyby metoda ta zmieniła się w klasie Cabbie, miałoby
to bezpośredni wpływ na klasę PartTimeCabbie i wszystkie inne jej podklasy. W takim
przypadku zmiany implementacji klasy Cabbie niekoniecznie muszą się ograniczać
tylko do niej.
Rysunek 7.10. Diagram UML klas Cabbie i PartTimeCabbie
Aby zmniejszyć ryzyko związane z tym zjawiskiem, należy ściśle trzymać się zasady
relacji „jest” w dziedziczeniu. Jeśli podklasa rzeczywiście jest wyspecjalizowaną wersją
swojej nadklasy, zmiany w tej nadklasie powinny mieć taki wpływ na podklasę, jakiego
należałoby się spodziewać i jaki jest naturalny. Rozważmy taki przykład. Jeśli klasa Circle
dziedziczy implementację po Shape i zmiana w implementacji tej drugiej powoduje
uszkodzenia pierwszej, należy uznać, że Circle nie jest właściwą podklasą klasy Shape.
Jak można wykorzystać dziedziczenie w niewłaściwy sposób? Wyobraźmy sobie,
że trzeba utworzyć okno na potrzeby graficznego interfejsu użytkownika (GUI).
Można by było w pierwszej chwili uznać, że dobrym rozwiązaniem będzie utworzenie
takiego okna jako podklasy klasy reprezentującej prostokąt:
Kup książkęPoleć książkęCzemu hermetyzacja jest podstaw(cid:241) technologii obiektowej
141
public class Rectangle {
}
public class Window extends Rectangle {
}
W rzeczywistości jednak okno GUI to coś znacznie więcej niż prostokąt. Nie jest
to tak naprawdę specjalny rodzaj prostokąta, jak np. kwadrat. Prawdziwe okno może
zawierać prostokąt (a nawet wiele prostokątów). Nie jest to jednak prostokąt sam
w sobie. W takim przypadku klasa Window nie powinna dziedziczyć po Rectangle,
tylko zawierać obiekty tej klasy.
public class Window {
Rectangle menubar;
Rectangle statusbar;
Rectangle mainview;
}
Szczegó(cid:228)owy przyk(cid:228)ad wykorzystania polimorfizmu
Dla wielu polimorfizm jest podstawą obiektowości. W projektowaniu systemów obiektowych
chodzi przede wszystkim o utworzenie całkowicie niezależnych obiektów. W dobrze
zaprojektowanym systemie obiekt powinien potrafić odpowiedzieć na wszystkie ważne
pytania, które go dotyczą. Z zasady każdy obiekt powinien odpowiadać sam za siebie.
Niezależność jest jednym z podstawowych warunków, których spełnienie umożliwia
wielokrotne wykorzystanie kodu.
Przypominam z rozdziału 1., że polimorfizm dosłownie oznacza wiele kształtów.
Jeśli obiekt odbiera komunikaty, musi mieć odpowiednie metody, aby na nie odpowiedzieć.
W hierarchii dziedziczenia podklasy dziedziczą po swoich nadklasach interfejsy. Ponieważ
jednak każda klasa jest w zasadzie samodzielną jednostką, może na komunikaty odpowiadać
we właściwy sobie sposób.
Wrócę na chwilę do przykładu z rozdziału 1., w którym opisana została klasa Shape.
Ma ona zachowanie o nazwie Draw. Jeśli poprosi się kogoś o narysowanie figury
geometrycznej, najprawdopodobniej padnie pytanie, jakiej. Instrukcja, aby narysować
figurę geometryczną, jest zbyt abstrakcyjna (w istocie metoda Draw w klasie Shape nie
ma implementacji). Należy więc sprecyzować, o jaką figurę chodzi. W tym celu w klasie
Circle i innych podobnych powinna znaleźć się odpowiednia implementacja metody
Draw. Mimo że klasa Shape zawiera metodę Draw, klasa Circle przesłania ją własną
wersją. Przesłanianie to, mówiąc najkrócej, zastępowanie implementacji odziedziczonej
po nadklasie własnym kodem.
Odpowiedzialno(cid:264)(cid:232) obiektów
Jeszcze raz wrócę do przykładu klasy Shape z rozdziału 1. (rysunek 7.11).
Kup książkęPoleć książkę142
Rozdzia(cid:228) 7. Dziedziczenie i kompozycja
Rysunek 7.11. Hierarchia klasy Shape
Polimorfizm jest jednym z najbardziej eleganckich sposobów wykorzystania
dziedziczenia. Przypomnę, że nie można utworzyć egzemplarza klasy Shape. Jest to klasa
abstrakcyjna, co można stwierdzić dzięki obecności w niej abstrakcyjnej metody
getArea(). Szczegółowy opis klas abstrakcyjnych znajduje się w rozdziale 8.
Natomiast w przypadku klas Rectangle i Circle tworzenie egzemplarzy jest możliwe,
ponieważ są to klasy konkretne. Mimo że klasy Rectangle i Circle dziedziczą po tej samej
klasie Shape, są między nimi pewne oczywiste różnice. Ponieważ są to figury geometryczne,
można obliczyć ich pole powierzchni. Ale wzór na to pole dla każdej wygląda inaczej.
Dlatego implementacja tej metody nie może znajdować się w klasie Shape.
Jest to idealna sytuacja, w której należy wykorzystać polimorfizm. Polimorfizm polega
na tym, że taki sam komunikat można wysłać do wielu różnych obiektów, a każdy z nich
odpowie we właściwy sobie sposób. Jeśli na przykład zostanie wysłany komunikat getArea
do obiektu klasy Circle, wykonane obliczenia będą znacznie się różniły od tych, które
w odpowiedzi na ten sam komunikat wykonałby obiekt klasy Rectangle. Jest to możliwe
dzięki temu, że obiekty te same odpowiadają za siebie. Jeśli zażąda się od obiektu Circle,
aby obliczył swoje pole powierzchni, wykona polecenie. Jeśli zażąda się od tego obiektu,
aby się narysował, również to nastąpi. Obiekt klasy Shape nie mógłby wykonać żadnej
z tych czynności, nawet gdyby dało się go w ogóle utworzyć, ponieważ miałby zbyt mało
informacji o sobie. Należy zauważyć, że nazwa metody getArea na diagramie klasy Shape
(rysunek 7.11) jest napisana kursywą. To oznacza, że metoda ta jest abstrakcyjna.
Przedstawię bardzo prosty przykład. Są cztery klasy: jedna abstrakcyjna o nazwie Shape
i trzy konkretne o nazwach Circle, Rectangle oraz Star. Oto ich kod źródłowy:
public abstract class Shape{
public abstract void draw();
}
public class Circle extends Shape{
public void draw() {
System.out.println( Rysuj(cid:218) ko(cid:239)o. );
}
}
public class Rectangle extends Shape{
public void draw() {
System.out.println( Rysuj(cid:218) prostok(cid:200)t. );
Kup książkęPoleć książkęCzemu hermetyzacja jest podstaw(cid:241) technologii obiektowej
143
}
}
public class Star extends Shape{
public void draw() {
System.out.println( Rysuj(cid:218) gwiazd(cid:218). );
}
}
Zwracam uwagę, że w każdej z tych klas jest tylko jedna metoda — draw. Ilustruje
to ważną cechę polimorfizmu: przedstawione wyżej konkretne klasy same odpowiadają
za rysowanie swoich obiektów. Klasa Shape nie dostarcza żadnego kodu rysującego.
Klasy Circle, Rectangle i Star robią to we własnym zakresie. Oto przykładowy fragment
kodu, który posłuży za dowód:
public class TestShape {
public static void main(String args[]) {
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
Star star = new Star();
circle.draw();
rectangle.draw();
star.draw();
}
}
Aplikacja testowa TestShape utworzy trzy klasy: Circle, Rectangle oraz Star.
Aby narysować obiekty tych klas, TestShape wysyła do nich żądanie:
circle.draw();
rectangle.draw();
star.draw();
Wynik działania programu TestShape będzie następujący:
C:\ java TestShape
Rysuj(cid:218) ko(cid:239)o.
Rysuj(cid:218) prostok(cid:200)t.
Rysuj(cid:218) gwiazd(cid:218).
Tak działa polimorfizm. Co trzeba by było zrobić, gdyby wystąpiła konieczność
utworzenia nowej klasy, np. Triangle? Wystarczy ją napisać, skompilować, przetestować
i używać. W klasie bazowej Shape ani innych klasach nie trzeba niczego zmieniać:
public class Triangle extends Shape{
public void draw() {
System.out.println( Rysuj(cid:218) trójk(cid:200)t. );
}
}
Od tej pory można wysyłać komunikaty do obiektów klasy Triangle. Mimo że klasa
Shape nie jest w stanie narysować trójkąta, Triangle zrobi to doskonale:
Kup książkęPoleć książkę144
Rozdzia(cid:228) 7. Dziedziczenie i kompozycja
public class TestShape {
public static void main(String args[]) {
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
Star star = new Star();
Triangle triangle = new Triangle ();
circle.draw();
rectangle.draw();
star.draw();
triangle.draw();
}
}
C:\ java TestShape
Rysuj(cid:218) ko(cid:239)o.
Rysuj(cid:218) prostok(cid:200)t.
Rysuj(cid:218) gwiazd(cid:218).
Rysuj(cid:218) trójk(cid:200)t.
Aby przekonać się o prawdziwej mocy polimorfizmu, można obiekt figury przekazać
do metody, która absolutnie „nie ma pojęcia”, co to jest. Spójrzmy na poniższy kod
zawierający informacje o konkretnych kształtach w parametrach.
public class TestShape {
public static void main(String args[]) {
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
Star star = new Star();
drawMe(circle);
drawMe(rectangle);
drawMe(star);
}
static void drawMe(Shape s) {
s.draw();
}
}
W tym przypadku obiekt klasy Shape jest przekazywany metodzie drawMe, która potrafi
obsłużyć każdy poprawny obiekt tego typu — nawet taki, który zostanie dodany później.
Tę wersję programu TestShape można uruchomić w taki sam sposób jak p
Pobierz darmowy fragment (pdf)