Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

Vaše jistota na trhu IT Dědění implementace Rudolf Pecinovský

Podobné prezentace


Prezentace na téma: "Vaše jistota na trhu IT Dědění implementace Rudolf Pecinovský"— Transkript prezentace:

1 Vaše jistota na trhu IT Dědění implementace Rudolf Pecinovský

2 Vaše jistota na trhu IT Obsah s odkazy ►Rozhraní a abstraktní třídyRozhraní a abstraktní třídy ►Principy dědění třídPrincipy dědění tříd ►přebíjení metodpřebíjení metod ►Podrobnosti o konstruktorechPodrobnosti o konstruktorech ►Společný rodič skupiny třídSpolečný rodič skupiny tříd ►Kdy dědění ano a kdy neKdy dědění ano a kdy ne

3 Vaše jistota na trhu IT Opakování: Rozhraní a dědění ►Rozhraní a jeho implementace (opakování) ►Implementace více rozhraní ►Dědění rozhraní ►Přetypování na rodiče a potomka ►Ukázka programu s přetypováním

4 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 4 Opakování Rozhraní a jeho implementace (opakování) ►V definici datového typu rozlišujeme rozhraní a implementaci ►V rozhraní rozlišujeme dvě složky: ● Signaturu definující část rozhraní, kterou může překladač ověřit ● Kontrakt, definující požadavky, které musí programátor doplnit, ale které není možno postihnout v syntaxi ►Konstrukce interface slouží jako specifikace požadované signatury třídy umožňující popsat i kontrakt ►Všechny metody deklarované v rozhraní jsou abstraktní, tj. bez jakékoliv implementace ►Třída implementující interfejs musí implementovat všechny jím deklarované metody nebo se prohlásit za abstraktní ►Instance třídy implementující rozhraní se mohou vydávat za instance daného rozhraní ►Třída může implementovat libovolný počet rozhraní

5 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 5 Implementace více rozhraní ►Implementované interfejsy uvádí třída ve své hlavičce za klíčovým slovem implements ; při současné implementaci více interfejsů oddělujeme jednotlivá rozhraní čárkou, např. public clas Třída implements Rozhraní1, Rozhraní1 ►UML zobrazuje implementaci rozhraní čárkovanou šipkou s trojúhelníkovou hlavičkou ►Implementovaná rozhraní počítáme mezi předky implementující třídy ►Implementující třídy vystupují naopak jako potomci svých implementovaných rozhraní ►Instance potomka se může kdykoliv vydávat za instanci předka Opakování

6 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 6 Dědění rozhraní ►Rozhraní může být potomkem jiného rozhraní; svého rodiče pak uvede v hlavičce za slovem extends interface IHýbací extends IPosuvný, INafukovací ►V diagramu tříd znázorňujeme vztah předek–potomek šipkou s trojúhelníkovým koncem ukazujícím k předku ►Dceřiné rozhraní přebírá (dědí) všechny metody deklarované rodičem ►Třídy implementující dceřiné rozhraní musí implementovat všechny jeho metody včetně zděděných Opakování

7 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 7 Opakování Přetypování instancí rozhraní ►Prostřednictvím implementace rozhraní s více předky můžeme vyřešit problém parametrů metod vystupujících jako instance několika typů současně ►Víme-li, že instance nějakého typu je současně instancí jiného typu, můžeme ji přetypovat ►Přetypování potomka na předka provede překladač automaticky ● Instance třídy implementující rozhraní se může kdykoliv vydávat za instanci kteréhokoliv z implementovaných rozhraní ● Instance potomka rozhraní se může kdykoliv vydávat za instanci předka tohoto rozhraní ►Přetypování se uplatní, potřebujeme-li s instancí vydávající se za instanci některého ze svých předků pracovat opět jako s instancí jejího vlastního typu

8 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 8 Ukázka programu s přetypováním na potomka public class Pozice { public final int x, y; //... Vynechané public boolean equals( Object o ) { if( !(o instanceof Pozice) ) return false; Pozice p = (Pozice) o; return (x == p.x) && (y == p.y); } Opakování

9 Vaše jistota na trhu IT Principy dědění ►Specializace × Zobecnění ►Terminologie a základní vztahy ►3 druhy dědění

10 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 10 Specializace ►Typ: třída ( class ) / rozhraní ( interface ) / výčtový typ ( enum ) ►Při práci s objekty daného typu často odhalíme skupiny instancí se speciálními, avšak pro celou skupinu společnými vlastnostmi ►Příklady: ● Auta můžeme dělit na osobní, nákladní, autobusy a speciální ● Vesmírná tělesa dělíme na hvězdy, planety a ostatní smetí ● Osoby dělíme na muže a ženy ● Zpomezi geometrických tvarů můžeme vydělit skupiny elips, n-úhelníků, … ● Mezi čtyřúhelníky můžeme vydělit obdélníky, mezi nimi pak čtverce ►To, že je daný objekt členem speciální podskupiny nijak neovlivňuje jeho členství v původní skupině ● Proto se může potomek kdykoliv vydávat za předka

11 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 11 Zobecnění ►Často provádíme obrácený myšlenkový postup: u řady různých druhů objektů nacházíme společné vlastnosti a definujeme pak společné skupiny ►Příklady: ● Lidé spolu s řadou zvířecích druhů tvoří skupiny savců ● Auta, kola, povozy, vlaky, letadla, lodě & spol. jsou dopravní prostředky ● Kybernetika je věda o řízení a sdělování v živých organizmech, společenstvích a strojích => jako kybernetický systém lze považovat každý člen každé ze skupin ● Přirozené číslo je speciálním případem celého čísla, které je speciálním případem racionálního čísla, které je speciálním případem reálného čísla, které je speciálním případem komplexního čísla ● V objektově orientovaných programech je vše považováno za objekt

12 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 12 Terminologie a základní vztahy ►Typ: třída ( class ) / rozhraní ( interface ) / výčtový typ ( enum ) ►Pro podmnožinu instancí se speciálními vlastnostmi můžeme definovat jejich vlastní typ ►Pro označení „obecnějšího“ a „specializovaného“ typu se používají různé termíny: ZobecněnýSpecializovaný Rodičovský typDceřiný typ Bázový typOdvozený typ Nadtyp, nadtřídaPodtyp, podtřída PředekPotomek ►Za předky třídy považujeme i všechna implementovaná rozhraní

13 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 13 3 druhy dědění ►Potomci mohou od svých rodičů leccos převzít; toto převzetí označujeme termínem dědění ►V OOP existují 3 druhy dědění: ● Dědění rozhraní ● Dědění implementace ● Dědění podstaty (potomek je speciálním případem předka) ►Má-li program spolehlivě fungovat, musejí být všechny tři druhy dědění v souladu ● Vyžaduje-li konkrétně řešený problém tento soulad narušit, je třeba tuto skutečnost důkladně zdokumentovat

14 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 14 Dědění rozhraní ►O dědění rozhraní předka hovoříme nezávisle na tom, je-li předkem třída nebo interface ►Dědění rozhraní vyžaduje, aby potomek dodržel veškerou signaturu a kontrakt rozhraní předka (Liskov Substitution Principle – LSP) ►Korektně implementované zděděné rozhraní umožňuje, aby se instance potomka mohla vydávat za instanci předka ►Narušení dědění rozhraní (většinou tak, že potomek nedodrží kontrakt předka) snižuje stabilitu programu ►Příklady řádného dědění: ● HTML spojení je potomek obecného spojení ● Třída ArrayList je potomkem rozhraní List ● Třída FileInputStream je potomkem třídy InputStream ● Čtverec lze v jistých situacích považovat za speciální případe obdélníka

15 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 15 Dědění implementace ►Dědění implementace je speciální případ skládání, při němž potomek převezme instanci předka jako svůj podobjekt a současně převezme i jeho rozhraní ►Používá se především proto, aby potomek získal „zdarma“ implementaci některých potřebných atributů, metod a typů ►Překladač zabezpečí automatické převzetí signatury, dodržení příslušného kontraktu má na starosti programátor ►Autoři potomků dědících implementaci se často soustředí pouze na to, aby získali implementaci „zdarma“, a zcela ignorují nutnost dodržení kontraktu a invariantů předka ►Důsledek: instance potomka se nemůže plnohodnotně vydávat za instanci předka; porušuje tak LSP a konzistenci programu

16 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 16 Nevhodné použití dědění implementace ►Obdélník je potomek čtverce ● Obdélník je implementován jako čtverec, který nemusí mít všechny strany stejně dlouhé ►Obdélník je potomek bodu ● Obdélník zdědí souřadnice jednoho rohu a přidá souřadnice dalšího ►Kruh je potomek bodu ● Kruh je implementován jako bod doplněný o poloměr ►Kruhová výseč je potomek kruhu ● Výseč je implementována jako kruh doplněný o úhel výseče ►Cyklista je potomek žáby ● Cyklista neumí přeskákat potok po kamenech. Po jeho předefinování jako potomka žáby tuto schopnost získá. Současně ale zdědí schopnost kvákat a rodit pulce. Výsledný objekt proto není cyklista, který umí skákat po kamenech, ale žába, která umí jezdit na kole.

17 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 17 Dědění podstaty ►O dědění podstaty hovoříme tehdy, můžeme-li považovat instance potomka za speciální případy instancí předka ►O dědění podstaty lze uvažovat nezávisle na implementaci ►Příklady: ● Mapa je speciální typ množiny – je to množina dvojic (klíč,hodnota). Ve skutečnosti (standardní knihovna Javy) je však množina implementována jako mapa ● Kruh chápeme jako speciální případ elipsy a obdobně chápeme čtverec jako speciální případ obdélníku. Tuto dědění však nemůžeme použít v aplikacích, v nichž potřebujeme realizovat operace, které svobodně mění oba rozměry daného grafického objektu ►Při návrhu architektury stavějící na dědění podstaty můžeme občas narazit na problémy se správnou implementací takto pojatého dědění

18 Vaše jistota na trhu IT Návrhový vzor Dekorátor

19 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 19 Předběžné úvahy ►Máme auta, která se umějí přesunout na zadanou pozici, ale nejsou připravena na přímé ovládání z klávesnice ►Ovládání z klávesnice může zprostředkovat instance třídy Řadič, která zprostředkuje ovládání instancím typu IOvládaný ►Znalci dědění navrhnou definovat ovládaná auta jako potomky dodaných, ale tak v projektu vznikne řada nových tříd ►Výhodnější je použít vzor Dekorátor a schopnost „být ovládán“, tj. implementaci rozhraní IOvládaný přidat jako „dekoraci“

20 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 20 Návrhový vzor Dekorátor ►Název Dekorátor obdržel vzor proto, že dekoruje (ozdobí) dodaný (dekorovaný) objekt novou (nebo upravenou) funkčností ►Dekorátor přebírá rozhraní předka včetně kontraktu, takže může kdykoliv vystupovat v roli dekorovaného „předka“ ►Mohli bychom jej označit jako explicitně definovanou dědění: dekorující objekt (dekorátor) vystupuje v roli potomka, dekorovaný objekt v roli předka ►Dekorátor explicitně implementuje všechny „nedotčené“ metody „předka“ s tím, že zodpovědnost za jejich korektní provedení přenechává na podobjektu „předka“ ►Je obzvláště výhodný v situacích, kdy mechanické použití dědění vede k dramatickému nárůstu počtu tříd a citelnému snížení modifikovatelnosti programu

21 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 21 Definice dekorátoru public class Ovládaný implements IPosuvný, IOvládaný { private final IPosuvný posuvný; public Ovládaný (IPosuvný posuvný) { this.posuvný = posuvný; } public Pozice getPozice() { return posuvný.getPozice(); } public void setPozice(Pozice p) { posuvný.setPozice(p); } //... a další } Zprávy, jejichž realizaci dekorátor deleguje na dekorovaný objekt Dekorující objekt rozhodne, pro které činnosti využije metod dekorovaného objektu a pro které definuje metody vlastní Konstruktor dekorátoru převezme dekorovaný objekt jako parametr

22 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 22 x Adaptér pro skupinu dekorátorů ►Dekorátor „přehrává“ volání „nezdobících“ metod na metody ozdobeného objektu ►Očekáváme-li vytváření více dekorujících objektů, má smysl pro ně definovat společného rodiče, který se postará o toto „přehrávání“

23 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 23 x Definice společného rodiče public class AdOtočný implements IOtočný { private final IOtočný otočný; public AdOtočný(IOtočný otočný) { this.otočný = otočný; } public Pozice getPozice() { return otočný.getPozice(); } public void setPozice(Pozice p) { otočný.setPozice(p); } //... a další }

24 Vaše jistota na trhu IT Implementace dědění tříd ►Co se dědí ►Rodičovský podobjekt ►Konstrukce objektu

25 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 25 ? Co se vlastně dědí Všechno

26 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 26 Co se vlastně dědí – rodičovský podobjekt ►Aby bylo možno zabezpečit funkci všech (i soukromých) vazeb, obsahuje každý objekt dceřiné třídy jako svoji součást podobjekt své rodičovské třídy, který s sebou přináší požadovanou implementaci ►Dceřiná třída tak dědí od svého rodiče všechny jeho schopnosti; přebírá všechny jeho členy včetně soukromých, o kterých vůbec „neví“ ►Objekt dceřiné třídy se nemůže začít budovat dřív, než bude zcela vybudován jeho rodičovský podobjekt ● U rodičovského podobjektu aplikujeme stejné pravidlo – před ním je tedy třeba vybudovat prarodičovský podobjekt, před ním praprarodičovský atd. Vnučka Dcera Matka Object

27 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 27 Atribut super ►Rodičovský podobjekt je vytvořen jako „soukromý atribut“ nazvaný super, tímto identifikátorem jej lze „oslovovat“ ►Tento atribut ale není plnohodnotný: ● Smí jej používat pouze jeho vlastník, tj. nelze zavolat param.super.toString() ● Nelze jej používat samotný, musím po něm vždy chtít nějaký člen (atribut, metodu, datový typ) ● Potřebujeme-li jej zkonstruovat pomocí parametrického konstruktoru, zavoláme na počátku těla konstruktoru super(/*parametry*/); ● Aby mohla mít třída potomky, nesmí mít konstruktor soukromý

28 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 28 1.Zkontroluje se, že je zavedena třída 1.Při zavádění musí být zaveden rodič => nejprve se zavede rodičovská třída 2.Pak se zavede dceřiná třída 2.Zavolá se operátor new 1.Připraví prostor na haldě a připojí odkaz na VMT 2.Mezi parametry přidá odkaz this a vyhodnotí zbylé parametry Předává-li konstruktor zodpovědnost za inicializaci svému kolegovi, pokračuje se ve vyhodnocování parametrů, dokud se nenarazí na konstruktor, který inicializaci doopravdy provede 3.Zavolá se konstruktor 1.Zavolá se konstruktor rodičovského podobjektu ( super ) 2.Projde se definice třídy, inicializují se atributy a provedou inicializační bloky 3.Vstoupí se do těla konstruktoru a to se provede 4.Odkaz na vytvořenou instanci se předá volající metodě Demo: Matka – Dcera – Vnučka Matka – Dcera – Vnučka Konstrukce objektu

29 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 29 Hierarchie dědění ►Dědění tříd má v jazyku Java stromovou strukturu, v jejímž kořeni je třída Object ● Každá třída s výjimkou třídy Object má právě jednoho předka ● Třída Object je společným (pra)rodičem všech tříd ►Každá uživatelem definovaná třída má právě jednoho předka, tj. smí obsahovat pouze jeden rodičovský podobjekt ►Na rozdíl od některých jiných jazyků (např. C++) jazyk Java nepodporuje násobnou dědění tříd ● Přináší problémy při implementaci a programy jsou méně stabilní ● Přináší problémy při návrhu, protože nabízí návrhářům řešení, z kterého se po odhalení jeho nevýhodnosti špatně couvá ● Přináší problémy při modifikacích programu, protože zbytečně zvyšuje počet vzájemných vazeb, které je při modifikaci nutno respektovat

30 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 30 Deklarace dědění ►Svoje odvození od rodičovské třídy deklaruje dceřiná třída v hlavičce za svým názvem klíčovým slovem extends následovaným úplným názvem rodičovské třídy ►Je-li rodičovská třída ve stejném balíčku nebo je-li importovaná, lze úplný název nahradit jednoduchým ►Třídy z kořenového balíčku nemají úplný název, a proto nemohou mít potomky v jiných balíčcích ►Dědictví od třídy Object se uvádět nemusí a naopak, nemá-li někdo v hlavičce uvedeného předka, je přímým potomkem třídy Object ►Případná deklarace implementace rozhraní se uvádí až za deklarací dědění: public class Potomek extends Předek implements Rozhraní

31 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 31 Příklady dědění z knihovny kolekcí Object public abstract class AbstractCollection implements Collection public abstract class AbsttractList extends AbstractCollection implements List public class ArrayList extends AbstractList implements RandomAccess public class LinkedHashSet extends HashSet

32 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 32 Modifikátor protected ►Definuje přístup, který je rozšířením implicitního přístupu ►Členy (atributy, metody, zanořené typy) označené modifikátorem protected jsou viditelné: ● Pro všechny třídy uvnitř stejného balíčku ● Pro dceřiné třídy v jakémkoliv balíčku ►Členy s modifikátorem protected jsou v překladech označovány jako chráněné ►Má-li třída či instance atribut, který je instancí této třídy nebo její rodičovské třídy, jsou pro ni jeho chráněné členy nepřístupné public class Seznam { protected Uzel první; protected Uzel poslední; } public class Uzel { protected Uzel předchůdce; protected Uzel následník; protected Object hodnota; }

33 Vaše jistota na trhu IT Abstraktní třídy a návrhový vzor Šablonová metoda ►Abstraktní třídyAbstraktní třídy ►Abstraktní metodyAbstraktní metody ►Potomci abstraktní třídyPotomci abstraktní třídy ►Ještě jednou architektura knihovny kolekcíJeště jednou architektura knihovny kolekcí

34 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 34 Motivace ►Občas se hodí, aby část rozhraní byla již implementována a potomci mohli tuto implementaci zdědit ►Příklad: Většina tříd implementujících interface IPosuvný bude mít definovány metodu setPozice(Pozice) stejně: public class Posuvný implements IPosuvný { public Pozice getPozice() { //Definice těla metody } public void setPozice(int x, int y) { //Definice těla metody } public void setPozice(Pozice pozice) { setPozice(pozice.x, pozice.y); } //Další atributy a metody } Metody, které definuje každá třída po svém Metoda, kterou většina tříd definuje stejně

35 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 35 Abstraktní třídy ►Abstraktní třída je hybrid na pomezí mezi standardní třídou a rozhraním ● Na rozdíl od rozhraní může některé metody implementovat ● Na rozdíl od standardní třídy nemusí implementovat všechny metody ►Abstraktní třída se musí ke své abstraktnosti přihlásit uvedením klíčového slova abstract mezi svými modifikátory ● Příklad: public abstract class MojeTřída {} ►Protože abstraktní třídy nemusí mít vše implementované, nemůže mít žádné samostatné instance; instance může vytvářet jen třída s kompletní implementací ►S instancemi je na tom abstraktní třída obdobně jako rozhraní: za instance abstraktní třídy se vydávají instance jejích potomků ►Třídu (metodu), která není abstraktní, označujeme jako konkrétní

36 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 36 Abstraktní metody ►Abstraktní třída může deklarovat vlastní abstraktní metody, musí však mezi jejich modifikátory uvést klíčové slovo abstract např.: abstract public void nakresli(Kreslítko k); ►Abstraktní metody nesmějí být soukromé, potomek by na ně neviděl a nemohl by je implementovat ►Deklaraci neimplementovaných metod deklarovaných v implementovaných rozhraních, není třeba opakovat (ale lze) ►Metody, které nejsou abstraktní, označujeme jako konkrétní ►Konkrétní metody mohou ve svých definicích používat všechny deklarované metody včetně abstraktních, přestože tyto nejsou v době jejich definice ještě implementovány

37 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 37 Možné řešení ►Dovolíme třídě deklarovat abstraktní metody obdobně, jako to dělá interface, a nechat implementaci na potomcích ►Musíme se však smířit s tím, že (stejně jako interface) tato třída nebude moci mít vlastní instance – označíme ji proto jako abstraktní public abstract class Posuvný implements IPosuvný { public abstract Pozice getPozice(); public abstract void setPozice(int x, int y); public void setPozice(Pozice pozice) { setPozice(pozice.x, pozice.y); } //Další atributy a metody } Abstraktní metody Metoda používající abstraktní metody, i když ještě nejsou implementovány

38 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 38 Potomci abstraktní třídy ►Třída se prohlásí za potomka jiné třídy tím, že za název třídy napíše klíčové slovo extends následované názvem předka ►Třída smí mít pouze jediného „třídního“ předka; nezávisle na tomto „třídním“ předku však může implementovat libovolný počet rozhraní ►Potomek abstraktní třídy zdědí od svého rodiče všechny metody, avšak ty abstraktní musí buď implementovat, anebo se také prohlásit za abstraktní třídu ►Potomek abstraktní třídy automaticky dědí i implementaci všech rozhraní, k jejichž implementaci se rodičovská třída přihlásila ►Implementaci rozhraní dědí potomek nezávisle na tom, jestli požadované metody zdědí již implementované, anebo se bude muset postarat o jejich implementaci sám

39 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 39 Knihovna kolekcí – architektura

40 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 40 Návrhový vzor Šablonová metoda ►Motivace: Definujeme metodu obsahující kostru nějakého algoritmu, u nějž však v době konstrukce ještě nejsou všechny kroky známy ►Konkrétní náplň neznámých kroků definují až potomci na základě svých speciálních dodatečných znalostí ►Třída se šablonovou metodou definuje příslušnou kostru a pro dosud neznámé postupy definuje virtuální metody, které potomci přebijí svými vlastními ● Je-li jedno zmožných řešení např. „nedělat nic“, je možno definovat virtuální metodu jako prázdnou ● Neexistuje-li žádné přijatelné implicitní řešení, definuje se metoda jako abstraktní

41 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 41 Příklad: Třída AbstractCollection 1/2 public abstract class AbstractCollection implements Collection { /** Vrací počet prvků v kolekci. */ public abstract int size(); /** Vrací informaci, zda je kolekce prázdná. */ public boolean isEmpty() { return size() == 0; } public abstract Iterator iterator();

42 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 42 Příklad: Třída AbstractCollection 2/2 public boolean contains(Object o) { Iterator ie = iterator(); if (o==null) { while (ie.hasNext()) if (ie.next()==null) return true; } else { while (ie.hasNext()) if (o.equals(ie.next())) return true; } return false; }

43 Vaše jistota na trhu IT Přebíjení metod ►Přebíjení metod ►Virtuální a konečné metody ►Nebezpečné vlastnosti virtuálních metod 354– –118

44 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 44 Přebíjení metod ►Rodičovská třída může dovolit, aby dceřiná třída definovala vlastní verze metod, jejichž zděděné verze ji nevyhovují – tuto operaci označujeme jako přebití metody ►Rodič může zakázat přebíjení svých metod uvedením modifikátoru final ►Metody, které je možno přebít, označujeme jako virtuální ►přebít lze pouze metody, u nichž to není zakázané a na které třída vidí; neviditelné metody jsou nepřebitelné ( private či „ package private “ v jiných balíčcích)

45 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 45 Dosažitelnost virtuálních metod ►Kdykoliv v budoucnu někdo pošle objektu zprávu, jejíž zpracování má na starosti virtuální metoda, bude vždy zavolána verze osloveného objektu, a to nezávisle na tom, za čí instanci se objekt v danou chvíli vydává ►Jakmile potomek přebije rodičovskou verzi metody, stane se přebitá verze pro okolí nedostupná ►O tom, která verze metody se použije, rozhoduje virtuální stroj až za běhu, a proto nezáleží na tom, které verze metod existovaly v době překladu volající metody

46 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 46 Možná implementace přebíjení – VMT ►Součástí objektu třídy je tabulka virtuálních metod (virtual method table – VMT) ►Na počátku tabulky jsou zděděné metody, za nimi metody nové ►Je-li metoda přebitá, nahradí se její adresa adresou metody, která ji přebila ►Pošle-li někdo instanci třídy zprávu, zavolá se metoda na adrese ve VMTVMT ►Ve VMT jsou pouze adresy virtuálních metod

47 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 47 Použití přebité verze metody ►Přebití (overriding) není předefinování (redefining) ani přepsání (rewriting) přebité metody ►Při přebití metody jinou metodou zůstává přebitá metoda nedotčená a můžete ji kdykoliv použít ►Dceřiná třída může použít přebitá verzi metody prostřednictvím kvalifikace klíčovým slovem super, pro ostatní třídy je však přebitá verze nedostupná

48 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 48 Další vlastnosti přebíjení metod ►Přebíjející metoda musí mít stejnou signaturu (název, počet parametrů a jejich typy, typ návratové hodnoty) jako metoda přebíjená ►Od Javy 5 smí přebíjející metoda deklarovat návratový typ jako potomka návratové typu přebité metody ►Nemá-li metoda stejnou signaturu, nejedná se o přebití, ale o přetížení ►Napíšu-li před hlavičku bude překladač zkontrolovat, zda jsem metodu nepřetížil, ale opravdu přebil, ● funguje plně až od verze 6.0 ►Nechci-li umožnit potomkům metodu přebít, označím metodu modifikátorem final ● Při překladu volání konečných metod (stejně jako při překladu soukromých metod) nepoužívá překladač tabulku virtuálních metod a volání je proto o pár nanosekund rychlejší

49 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 49 BlueJ a dědění ►Deklaruje se stejně jako implementace rozhraní ● Natažením šipky dědění od potomka k rodiči  BlueJ pak sám upraví hlavičku ● Zápisem hlavičky  BlueJ pak sám natáhne šipku ►Stejně jako u implementace rozhraní se dědění i ruší ● Smazáním deklarace dědění v hlavičce dosáhneme i odstranění šipky ● Odstraněním šipky dědění dosáhneme i příslušnou změnu v hlavičce ►Definice dědění mezi balíčky/projekty natažením šipky není možná ►BlueJ neumožňuje definovat násobné dědění; definujeme-li natažením šipky dědění od další třídy, BlueJ sám první dědění zruší

50 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 50 Třída Čtverec jako potomek třídy Obdélník

51 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 51 Vše ostatní čtverec zdědí

52 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 52 Definice třídy Čtverec ►Definujeme třídu pouze s bezparametrickým konstruktorem, proto jsme definici konstruktoru vynechali ►Všechny metody můžeme zdědit, pouze metodu setRozměr(int,int) musíme upravit tak, aby i po její aplikaci zůstal čtverec čtvercem ►V předchozím programu je chyba: definice nepočítá s používáním přetěžujících verzí metod public class Čtverec extends Obdélník public void setRozměr( int šířka, int výška ) { super.setRozměr( Math.min( šířka, výška ) ); }

53 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 53 Příčiny chyby v definici třídy Obdélník ►Tvůrce definice si neuvědomil, že: ● Jednoparametrická verze volá dvouparametrickou ● Ta je v potomkovi přebitá, takže se volá přebíjecí, tj. potomkova verze ● Potomkova verze dvouparametrické metody volá rodičovskou jednoparametrickou – cyklus je uzavřen public class Obdélník { // Deklarace ostatních //atributů a metod public void setRozměr(int rozměr) { setRozměr( rozměr, rozměr ); } public class Čtverec extends Obdélník public void setRozměr( int šířka, int výška ) { super.setRozměr( Math.min( šířka, výška ) ); }

54 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 54 Správná definice třídy Čtverec ►Přebíjecí verze potomka volá přebitou verzi rodiče, pouze jí předá upravené hodnoty parametrů, aby i po změně rozměru zůstal čtverec čtvercem ►Mějte na paměti, že podobné podrazy na vás u dědění čekají poměrně často public class Čtverec extends Obdélník public void setRozměr( int šířka, int výška ) { int min = Math.min( šířka, výška ); super.setRozměr( min, min ); }

55 Vaše jistota na trhu IT Podrobnosti o konstruktorech ►Dědění a konstruktory ►Klíčové slovo super ►Na co si dát u konstruktorů pozor ►Obejití virtuální metody v konstruktoru ►Konstruktory × tovární metody ►Konečné třídy 354–429

56 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 56 Dědění a konstruktory ►Dceřiný konstruktor musí vždy volat rodičovský konstruktor, aby vytvořil potřebný rodičovský podobjekt ►Vyhovuje-li volání bezparametrického konstruktoru, nemusí se v definici konstruktoru uvádět ►Potřebujeme-li volat parametrický konstruktor, voláme jej prostřednictvím super následovaného seznamem parametrů ►Dceřiná třída by měla vždy definovat konstruktor maximálně využívající co nejobecnější verzi rodičovského konstruktoru, protože jinak bude pro „vnoučata“ nedostupný public Čtverec( int x, int y, int strana, Barva barva ) { super( x, y, strana, strana, barva ); }

57 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 57 Klíčové slovo super ►Volání prostřednictvím super musí být úplně první akcí v těle konstruktoru; nesmí předcházet ani složená závorka ►Stejný požadavek má i volání prostřednictvím this => volání prostřednictvím super a prostřednictvím this se navzájem vylučují public Čtverec() { this( 0, 0 ); } public Čtverec( int x, int y ) { this( x, y, 50, IMPLICITNÍ_BARVA ); } public Čtverec( int x, int y, int strana ) { this( x, y, strana, strana, IMPLICITNÍ_BARVA ); } public Čtverec( int x, int y, int strana, Barva barva ) { super( x, y, strana, strana, barva ); }

58 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 58 Viditelnost konstruktorů ►Potomek může používat pouze ty verze rodičovských konstruktorů, na které vidí ►Třída, která má pouze soukromé konstruktory, nemůže mít žádné potomky ►Nedefinuje-li rodičovská třída dostupný bezparametrický konstruktor, musíme vždy používat volání přes super ►Je-li rodič a potomek ve stejném balíčku, stačí pro rodičovský konstruktor deklarovat implicitní přístup; jsou-li rodič a potomek v různých balíčcích, musí mít rodičovský konstruktor deklarován alespoň chráněný přístup ►Ukázky: viz modrá učebnice a projekt Matka–Dcera–VnučkaMatka–Dcera–Vnučka

59 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 59 Na co si dát u konstruktorů pozor ►V těle konstruktoru nesmíme volat virtuální metody, a to ani zprostředkovaně (tj. volat metodu, která volá virtuální metodu) ● Není to sice syntaktická chyba (bohužel), ale je to poukázka na budoucí problémy ►Pokud potomek danou metodu přebije, může v přebíjecí verzi používat atributy, které při práci rodičovského konstruktoru ještě neexistují (přesněji nejsou ještě inicializovány) ►V konstruktoru bychom proto měli používat pouze soukromé a konečné metody ►Potřebujeme-li použít virtuální (= přebitelnou) metodu, definujeme její soukromou verzi, kterou bude volat jak konstruktor, tak daná virtuální metoda

60 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 60 Obejití virtuální metody v konstruktoru public class Rodič { private double atribut; public Rodič() { atribut=virtuální(); } public double virtuální() { return currentTimeMillis(); } public class Rodič { private double atribut; public Rodič() { atribut=soukromá(); } private double soukromá() { return currentTimeMillis(); } public double virtuální() { return soukromá(); } public class Potomek extends Rodič { private final double dělitel=7; private double dělenec; public Potomek(double dělenec) { this.dělenec = dělenec; public long virtuální() { return dělenec / dělitel; } ►Potomek přebije virtuální metodu vlastní verzí, která vrací podíl svých atributů ►V okamžiku volání rodičem atributy nejsou inicializovány => metoda vrací 0/0 ►Po úpravě používá rodičovský konstruktor nepřebitelnou, a proto bezpečnou verzi ►Tu vyvolá i nová verze virtuální metody

61 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 61 Akce před voláním rodičovského konstrukt. ►Občas potřebujeme provést nějakou akci ještě před zavoláním rodičovského konstruktoru ►Daný problém je možno řešit dvěma způsoby ● Nečistě: provedením akce v rámci předávání parametrů (stav nejvyššího zoufalství – podrobnosti viz modrá učebnice) ● Lépe: náhradou konstruktoru jednoduchou tovární metodou  Tovární metoda je zcela běžná metoda, a proto na ni nejsou kladena omezení platná pro konstruktory ● Může před vlastním voláním konstruktoru cokoliv připravit ● Může se sama rozhodnout, zda vůbec konstruktor zavolá, a pokud ano, tak který konstruktor (třeba i konstruktor potomka) ● Můžeme mít několik různě pojmenovaných (a tím i různě se chovajících) metod se stejnými sadami parametrů

62 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 62 Konečné třídy ►Občas potřebujeme zakázat vytváření dceřiných tříd dané třídy  toho dosáhneme modifikátorem final ►Druhou možností je definovat všechny konstruktory soukromé, ale někdy potřebujeme konstruktory zveřejnit ►Takto je ošetřena řada tříd ve standardní knihovně; z dosud probraných tříd jsou to: ● Knihovní třídy (mají navíc soukromé konstruktory) ● Obalové třídy primitivních typů ● String ● Class ►Konečná třída nemůže být zároveň abstraktní

63 Vaše jistota na trhu IT Společný rodič skupiny tříd ►Duplicity v kódu ►Proč společný rodič ►Abstraktní třídy ►Jak vytvořit společného rodiče

64 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 64 Duplicity v kódu – zásada DRY ►Princip DRY (suchá zásada) – Don‘t Repeat Yourself ►Jednou z věcí, kterých bychom se měli vyvarovat, jsou duplicity v kódu, tj. stejný kód na více místech ►Duplicity v kódu nemusí představovat pouze stejný kód, ale i kód velmi podobný ►Duplicity v kódu lze odstranit několika způsoby ● Duplicity v rámci třídy: definovat metodu a z příslušných míst ji volat ● U duplicitních metod vyskytujících se v několika třídách lze definovat společný kód ve zvláštní třídě a využít návrhového vzoru Služebník ● Jedná-li se o třídy, které jsou všechny speciálním případem čehosi obecnějšího, lze pro ně definovat společného rodiče

65 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 65 Proč společný rodič ►Umožňuje soustředit na jedno místo kód, který mohou sdílet všichni potomci ►Definuje jednotné rozhraní pro celou skupinu tříd – svých budoucích potomků ►Umožňuje, aby potomci vystupovali jako instance společného rodiče, tj. abychom pro ně mohli definovat společné metody ►Umožňuje deklarovat požadavky (rozhraní) i pro budoucí třídy, které by měly patřit do dané skupiny, tj. pro třídy, které teprve časem vzniknou, ale už nyní víme, jaké by měly mít vlastnosti

66 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 66 Abstraktní třídy – proč ►Zatím jsme se seznámili s abstraktními třídami pouze pasivně jako s hybridy uprostřed mezi klasickými třídami a rozhraními ►Abstraktní třídu můžeme využít tehdy, potřebujeme-li definovat společného předka skupiny potomků avšak některé metody, které by měl tento předek „mít“, v něm nedokážeme implementovat ►Příklad: ● V projektu s plátnem mají všechny obrazce téměř shodné metody => mohli bychom jim tedy definovat společného rodiče ● Některé z těchto metod ale používají metodu nakresli(), pro níž nelze nalézt společnou implementaci – každý ji musí implementovat po svém ● Optimálním řešením je metodu pouze deklarovat (ne definovat), aby ji mohly ostatní metody použít, ale deklarovat ji jako abstraktní a nechat její implementaci na potomcích

67 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 67 Abstraktní třídy – shrnutí ►Jakmile třída nějakou metodu neimplementuje, musí deklarovat, že je abstraktní třídou ►Abstraktní třída se může sama rozhodnout, zda bude metody implementovat, nebo jenom deklarovat ►V definicích implementovaných metod lze použít i volání abstraktních metod, tj. metod, které budou definovat až potomci ►Od abstraktní třídy nelze vytvářet instance ►Třídu lze definovat jako abstraktní, i když vše implementuje, ale my chceme, aby od ní nebylo možno vytvářet instance ►I abstraktní třída musí mít konstruktor, aby mohla vytvářet rodičovský podobjekt v instancích svých potomků ►Abstraktní třída nemůže být konečná

68 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 68 Jak vytvořit společného rodiče 1.Projdeme všechny potenciální potomky vytvářeného rodiče a zjistíme, které metody mají společné (nemusí mít stejný kód, musí mít stejný účel)  to jsou potenciální metody budoucího rodiče 2.Zjistíme, které z nich mají stejný či podobný kód a navrhneme jejich společnou implementaci v rodiči 3.Společné metody, jejichž implementace se ale vzájemně liší, deklarujeme v rodiči jako abstraktní 4.Zjistíme, jaké atributy implementované metody potřebují a deklarujeme je jako atributy rodiče 5.Deklarace a definice všeho, co jsme vystěhovali do rodiče, můžeme v budoucích potomcích smazat

69 Vaše jistota na trhu IT Kdy dědění ano a kdy ne ►Proč nemáme dědění rádi ►Kdy použít a kdy nepoužít dědění ►Příklady špatného použití děděníi

70 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 70 Proč nemáme dědění rádi ►Dědění porušuje zapouzdření a skrývání implementace ● Nutí potomka, aby znal implementaci v předku (viz nekonečná smyčka při návrhu metody setPozice(int,int) ) ● Občas nutí předka, aby znal možné implementace svých potomků ►Dědění zvyšuje vzájemnou provázanost tříd; potřebuji-li modifikovat rodiče (najdu v něm chybu nebo v reakci na změnu zadání), musím zkontrolovat všechny potomky, že tato změna jejich chování nežádoucím způsobem neovlivnila ►Předchozí problém je způsoben tím, že potomek je závislý nejenom na rozhraní rodiče, ale i na jeho implementaci ►Začátečníci jsou možnostmi děděníi unešeni, a často ji proto používají i tam, kam vůbec nepatří

71 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 71 Kdy nepoužít dědění ►Vždy, když umím najít jiný rozumný způsob realizace potřebné funkčnosti ● Berme dědění jako KPZ (krabička poslední záchrany) pro případy, které bez ní neumíme dostatečně elegantně vyřešit ● V řadě případů lze dědění výhodně nahradit skládáním, kdy místo dědictví od předka použiji instanci předka jako atribut ►Když potenciální potomek není speciálním případem předka ►Intenzivně uvažujme o NEpoužití děděníi i v případě, když je potomek naopak příliš speciálním případem předka ►Dědění je naopak vhodné použít, když dopředu víme, že potomek je nejenom speciálním případem předka, ale navíc budeme často potřebovat přetypovávat potomka na předka

72 VŠE – 05 Copyright © 2006, Rudolf Pecinovský 72 Příklady špatného použití dědění ►Obdélník potomek úsečky, úsečka potomek bodu ►Kruhová výseč je potomkem kruhu ►Kvádr je potomek obdélníku ►Letadlo jako potomek křídla ►Cyklista přeskakující potok po kamenech je potomek žáby ► XObdélník a Terč z modré učebnice ● Zde najdete podrobné vysvětlení, proč je v daném případě použití dědění nevhodné včetně ukázek problémů, k nimž požití dědění v tomto případě vede

73 Vaše jistota na trhu IT Děkuji za pozornost Rudolf Pecinovský mail: ICQ:


Stáhnout ppt "Vaše jistota na trhu IT Dědění implementace Rudolf Pecinovský"

Podobné prezentace


Reklamy Google