Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00088 009785 10452474 na godz. na dobę w sumie
Myślenie obiektowe w programowaniu. Wydanie IV - książka
Myślenie obiektowe w programowaniu. Wydanie IV - książka
Autor: Liczba stron: 304
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-8120-4 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> inne - programowanie
Porównaj ceny (książka, ebook, audiobook).

Obiektowe podejście do programowania pojawiło się w latach 60. ubiegłego wieku. Simula 67 był pierwszym językiem, w którym je zastosowano. Dzięki temu życie programistów stało się zdecydowanie prostsze, a odwzorowanie świata rzeczywistego — możliwe. Jednak żeby skorzystać z zalet podejścia obiektowego, należy najpierw opanować nowy sposób myślenia.

Kolejne wydanie tej docenionej przez profesjonalistów książki szybko Ci w tym pomoże! W trakcie lektury poznasz podstawowe pojęcia oraz założenia programowania obiektowego. Dowiesz się, co to hermetyzacja, polimorfizm oraz dziedziczenie. Zobaczysz, jak obiekty powoływane są do życia oraz jak komunikują się między sobą. Ponadto nauczysz się korzystać z interfejsów, modelować klasy z wykorzystaniem diagramów UML oraz utrwalać stan obiektów. To wydanie zostało uzupełnione o mnóstwo nowych informacji, dotyczących między innymi wykorzystania obiektów w usługach sieciowych oraz aplikacjach mobilnych. Książka ta jest obowiązkową lekturą dla każdego programisty chcącego w 100% wykorzystać potencjał programowania obiektowego.

Sięgnij po tę książkę i:

Twoje kompendium wiedzy o programowaniu obiektowym!

 


 

Znajdź podobne książki Ostatnio czytane w tej kategorii

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)

Gdzie kupić całą publikację:

Myślenie obiektowe w programowaniu. Wydanie IV
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: