Rudolf Pecinovský rudolf@pecinovsky.cz Dědění implementace Rudolf Pecinovský rudolf@pecinovsky.cz.

Slides:



Advertisements
Podobné prezentace
A1PRG - Programování – Seminář Ing. Michal Typová konverze, oblast platnosti, paměťové třídy 9 Verze
Advertisements

Stodůlky 1977 a 2007 foto Václav Vančura, 1977 foto Jan Vančura, 2007.
Množiny Přirozená čísla Celá čísla Racionální čísla Komplexní čísla
(instance konkrétní třídy)
Vlastní skript může být umístěn: v hlavičce stránky v těle stránky
Seminář C++ 5. cvičení Dědičnost Ing. Jan Mikulka.
HYPERTEXT PREPROCESSOR. PROGRAMOVÁNÍ. DEFINICE POJMŮ Problém Problém nevyřešený, nežádoucí stav obvykle vyžaduje nějaké řešení Neřešitelný problém Neřešitelný.
Vaše jistota na trhu IT Quo vadis, programování? Rudolf PECINOVSKÝ 2012 – e-bezpečnost v Kraji Vysočina 1.
ÚVOD DO CPP 7 Dědičnost - pokračování
PJV151 Vnořené a vnitřní členy mohou být členy tříd a interfejsů. Je-li X obalem Y a Y je obalem Z, pak Z získá jméno X$Y$Z - kompilací vzniknou classy.
Polymorfismus Dědičnost
Téma 3 ODM, analýza prutové soustavy, řešení nosníků
J a v a Začínáme programovat Lucie Žoltá metody, objekty, konstruktor.
Objekty v CLIPSu RNDr. Jiří Dvořák, CSc.
Výzkumy volebních preferencí za ČR a kraje od
NÁSOBENÍ ČÍSLEM 10 ZÁVĚREČNÉ SHRNUTÍ
Téma: SČÍTÁNÍ A ODČÍTÁNÍ CELÝCH ČÍSEL 2
VY_32_INOVACE_INF_RO_12 Digitální učební materiál
VY_32_INOVACE_ 14_ sčítání a odčítání do 100 (SADA ČÍSLO 5)
Rudolf Pecinovský Dědění implementace Rudolf Pecinovský
Základní číselné množiny
Soustava lineárních nerovnic
Zábavná matematika.
Dělení se zbytkem 6 MODERNÍ A KONKURENCESCHOPNÁ ŠKOLA
Dělení se zbytkem 5 MODERNÍ A KONKURENCESCHOPNÁ ŠKOLA
State. State – kontext a problém Kontext  chování objektu má záviset na jeho stavu, který se typicky mění za běhu Neflexibilní řešení  metody obsahují.
Jazyk vývojových diagramů
Čtení myšlenek Je to až neuvěřitelné, ale skutečně je to tak. Dokážu číst myšlenky.Pokud mne chceš vyzkoušet – prosím.
Páté cvičení Dědičnost Interface Abstarktní třídy a metody
Posloupnosti, řady Posloupnost je každá funkce daná nějakým předpisem, jejímž definičním oborem je množina všech přirozených čísel n=1,2,3,… Zapisujeme.
Dělení se zbytkem 8 MODERNÍ A KONKURENCESCHOPNÁ ŠKOLA
Náhoda, generátory náhodných čísel
Zásady pozorování a vyjednávání Soustředění – zaznamenat (podívat se) – udržet (zobrazit) v povědomí – představit si – (opakovat, pokud se nezdaří /doma/)
TI 7.1 NEJKRATŠÍ CESTY Nejkratší cesty - kap. 6. TI 7.2 Nejkratší cesty z jednoho uzlu Seznámíme se s následujícími pojmy: w-vzdálenost (vzdálenost na.
DĚLENÍ ČÍSLEM 7 HLAVOLAM DOPLŇOVAČKA PROCVIČOVÁNÍ
Objektové programování
Dostupné z Metodického portálu ISSN: , financovaného z ESF a státního rozpočtu ČR. Provozováno Výzkumným ústavem pedagogickým v Praze.
Sémantická analýza Jakub Yaghob
Jedenácté cvičení Vlákna. Java cv112 Vlákna Operační systém Mutitasking – více úloh se v operačním programu vykonává „současně“ Java Multithreading -
Strategy. Strategy – „All-in-1“ na začátek class AStrategy { public: virtual void Algorithm()=0; protected: AStrategy(); }; class SpecificStrategy: public.
6. cvičení Polymorfismus
Počítače a programování 1
PB161 Právo friend, přetěžování operátorů, přetypování PB161 | Friend, operátory PB161 – Programování v jazyce C++ Objektově Orientované Programování.
JavaScript Podmínky, cykly a pole.
Přednost početních operací
KIV/PPA1 cvičení 8 Cvičící: Pavel Bžoch. Osnova cvičení Objekty v Javě Třída Konstruktor Metody Metody a proměnné třídy x instance Program sestávající.
OSNOVA: a) Úvod do OOPb) Třídy bez metod c) Třídy s metodamid) Konstruktory a destruktory e) Metody constf) Knihovní třídy g) Třídy ve tříděh) Přetížení.
KASKÁDOVÉ STYLY 1. 2 PRVNÍ STANDARD (CSS1) BYL PŘEDSTAVEN V ROCE 1996, PROTO STARŠÍ PROHLÍŽEČE ("ČTYŘKOVÉ" VERZE) IE A NN KASKÁDOVÉ STYLY NEPODPORUJÍ.
Algoritmizace a programování Objektově orientované programování - 16 Mgr. Josef Nožička IKT Algoritmizace a programování
KONTROLNÍ PRÁCE.
IB111 Programování a algoritmizace
Návrh a tvorba WWW Přednáška 5 Úvod do jazyka PHP.
Dědičnost - inheritance dědičnost je jednou z forem znovupoužitelnosti dědičnost je jednou z forem znovupoužitelnosti B A Třída A je předkem třídy B Třída.
Metodika objektového přístupu při tvorbě překladačů. Marek Běhálek Informatika a aplikovaná matematika FEI VŠB-TU Ostrava.
Šesté cvičení Výjimky Balíky.
Principy OOP Objektově orientované programování vychá-zí ze třech základních principů (rysů): zapouzdření (encapsulation) dědičnost (inheritance) polymorfismus.
Počítače a programování 1 7.přednáška. Základy Pole ve třídách a metodách Pole Arrays.
Vaše jistota na trhu IT Rozhraní a implementace Rudolf PECINOVSKÝ 2012 – Vývoj bezpečných aplikací 1.
Template Method. Motivační příklad – reálný svět Čaj 1) Uvař vodu 2) Dej do hrnku sáček čaje 3) Zalij hrnek 4) Přisyp cukr a vymačkej citrón Káva 1) Uvař.
PROGRAMOVÁNÍ 3ITA,3ITB Jaroslav Burdys Hlavní zdroj:
Úvod do C# - OOP Jaroslav BURDYS 4IT.
Vícerozměrná pole (1) Jazyk C povoluje, aby pole mělo více rozměrů (dimenzí) než jeden Z vícerozměrných polí bývá nejčastěji použí-váno pole dvourozměrné.
Definiční obor a obor hodnot
ZAL – 3. cvičení 2016.
Návrhový vzor Flyweight
Polymorfismus = Mnohotvarost
Příkazy cyklu (1) Umožňují vícekrát (nebo ani jednou) pro-vést určitý příkaz Jazyk C rozlišuje příkaz cyklu: s podmínkou na začátku: obecný tvar: while.
Soustava lineárních nerovnic
C# přehled vlastností.
Definiční obory. Množiny řešení. Intervaly.
Transkript prezentace:

Rudolf Pecinovský rudolf@pecinovsky.cz Dědění implementace Rudolf Pecinovský rudolf@pecinovsky.cz

Obsah s odkazy Rozhraní a abstraktní třídy Principy dědění tříd přebíjení metod Podrobnosti o konstruktorech Společný rodič skupiny tříd Kdy dědění ano a kdy ne

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

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í Opakování Copyright © 2006, Rudolf Pecinovský VŠE – 05

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í Copyright © 2006, Rudolf Pecinovský VŠE – 05

Opakování 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í Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Opakování Copyright © 2006, Rudolf Pecinovský VŠE – 05

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

Specializace × Zobecnění Terminologie a základní vztahy 3 druhy dědění Principy dědění Specializace × Zobecnění Terminologie a základní vztahy 3 druhy dědění

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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ý typ Dceřiný typ Bázový typ Odvozený typ Nadtyp, nadtřída Podtyp, podtřída Předek Potomek Za předky třídy považujeme i všechna implementovaná rozhraní Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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. Copyright © 2006, Rudolf Pecinovský VŠE – 05

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í Copyright © 2006, Rudolf Pecinovský VŠE – 05

Návrhový vzor Dekorátor

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“ Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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ší Konstruktor dekorátoru převezme dekorovaný objekt jako parametr 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í Copyright © 2006, Rudolf Pecinovský VŠE – 05

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í“ Copyright © 2006, Rudolf Pecinovský VŠE – 05

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ší Copyright © 2006, Rudolf Pecinovský VŠE – 05

Implementace dědění tříd Co se dědí Rodičovský podobjekt Konstrukce objektu

? Všechno Co se vlastně dědí Copyright © 2006, Rudolf Pecinovský

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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ý Copyright © 2006, Rudolf Pecinovský VŠE – 05

Demo: Matka – Dcera – Vnučka Konstrukce objektu Zkontroluje se, že je zavedena třída Při zavádění musí být zaveden rodič => nejprve se zavede rodičovská třída Pak se zavede dceřiná třída Zavolá se operátor new Připraví prostor na haldě a připojí odkaz na VMT 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 Zavolá se konstruktor Zavolá se konstruktor rodičovského podobjektu (super) Projde se definice třídy, inicializují se atributy a provedou inicializační bloky Vstoupí se do těla konstruktoru a to se provede Odkaz na vytvořenou instanci se předá volající metodě Demo: Matka – Dcera – Vnučka Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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í Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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; Copyright © 2006, Rudolf Pecinovský VŠE – 05

Abstraktní třídy a návrhový vzor Šablonová metoda Abstraktní metody Potomci abstraktní třídy Ještě jednou architektura knihovny kolekcí

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) { 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ě Copyright © 2006, Rudolf Pecinovský VŠE – 05

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í Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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

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í Copyright © 2006, Rudolf Pecinovský VŠE – 05

Příklad: Třída AbstractCollection 1/2 public abstract class AbstractCollection<E> implements Collection<E> { /** 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<E> iterator(); Copyright © 2006, Rudolf Pecinovský VŠE – 05

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

Přebíjení metod Přebíjení metod Virtuální a konečné metody Nebezpečné vlastnosti virtuálních metod 103–118 354–429

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) Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 VMT Ve VMT jsou pouze adresy virtuálních metod Copyright © 2006, Rudolf Pecinovský VŠE – 05

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á Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 anotaci @Override, bude překladač zkontrolovat, zda jsem metodu nepřetížil, ale opravdu přebil, Anotace @Override 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ší Copyright © 2006, Rudolf Pecinovský VŠE – 05

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ší Copyright © 2006, Rudolf Pecinovský VŠE – 05

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

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

Definice třídy Čtverec public class Čtverec extends Obdélník { @Override public void setRozměr( int šířka, int výška ) super.setRozměr( Math.min( šířka, výška ) ); } 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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 { @Override public void setRozměr( int šířka, int výška ) super.setRozměr( Math.min( šířka, výška ) ); } Copyright © 2006, Rudolf Pecinovský VŠE – 05

Správná definice třídy Čtverec public class Čtverec extends Obdélník { @Override public void setRozměr( int šířka, int výška ) int min = Math.min( šířka, výška ); super.setRozměr( min, min ); } 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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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

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 ); } Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 ); Copyright © 2006, Rudolf Pecinovský VŠE – 05

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čka Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

Obejití virtuální metody v konstruktoru Po úpravě používá rodičovský konstruktor nepřebitelnou, a proto bezpečnou verzi Tu vyvolá i nová verze virtuální metody 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; } @Override public long virtuální() { return dělenec / dělitel; public class Rodič { private double atribut; public Rodič() { atribut=soukromá(); } private double soukromá() { return currentTimeMillis(); public double virtuální() { return soukromá(); public class Rodič { private double atribut; public Rodič() { atribut=virtuální(); } public double virtuální() { return currentTimeMillis(); 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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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ů Copyright © 2006, Rudolf Pecinovský VŠE – 05

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í Copyright © 2006, Rudolf Pecinovský VŠE – 05

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

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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á Copyright © 2006, Rudolf Pecinovský VŠE – 05

Jak vytvořit společného rodiče 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 Zjistíme, které z nich mají stejný či podobný kód a navrhneme jejich společnou implementaci v rodiči Společné metody, jejichž implementace se ale vzájemně liší, deklarujeme v rodiči jako abstraktní Zjistíme, jaké atributy implementované metody potřebují a deklarujeme je jako atributy rodiče Deklarace a definice všeho, co jsme vystěhovali do rodiče, můžeme v budoucích potomcích smazat Copyright © 2006, Rudolf Pecinovský VŠE – 05

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

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ří Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

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 Copyright © 2006, Rudolf Pecinovský VŠE – 05

Děkuji za pozornost Rudolf Pecinovský http://vyuka.pecinovsky.cz/vse mail: rudolf@pecinovsky.cz ICQ: 158 156 600