+ All Categories
Home > Documents > PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce...

PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce...

Date post: 17-Jan-2020
Category:
Upload: others
View: 11 times
Download: 0 times
Share this document with a friend
30
PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu 2018 PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 1 / 30
Transcript
Page 1: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

PB161 Programování v jazyce C++Přednáška 8

DědičnostNávrhové principy v OOP

Nikola Beneš

13. listopadu 2018

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 1 / 30

Page 2: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Dědičnost

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 2 / 30

Page 3: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Dědičnost

Co už víte z minula?

třída může dědit od jiné třídyco všechno se dědí?atributy i metody

dědičnost realizuje podtypyvztah IS-Alučištník je pozemní jednotka (an archer is a landunit)pes je zvíře (a dog is an animal)ukazatel/reference na předka se může odkazovat na potomka

dědičnost může usnadnit znovupoužívání kódunení to ale jediná možnostkdy je to dobrý nápad?

s dědičností souvisí pojmy časná a pozdní vazba

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 3 / 30

Page 4: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Dědičnost v C++

lecture08_01.cpp

class Base { /* ... */ };

class Derived : public Base { /* ... */ };

v tomto předmětu používáme pouze public dědičnostDerived má přístup k:

veřejným a protected atributům a metodám Baseuživatelé Derived mají přístup k:

veřejným atributům a metodám Derived včetně zděděných veřejnýchatributů a metod

v jakém pořadí se volají konstruktory?konstruktor předka se provede před konstruktorem potomkapředek je jakoby prvním atributem potomka

v jakém pořadí se volají destruktory?destruktor potomka se provede před destruktorem předka

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 4 / 30

Page 5: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Časná a pozdní vazba

Mám-li referenci/ukazatel na objekt a zavolám na něm metodu,která metoda se zavolá?

časná vazba (early binding): podle typu ukazatele/referencepozdní vazba (late binding): podle skutečného typu objektu

Který typ vazby je v C++ implicitní a proč?

Vynucení pozdní vazby – klíčové slovo virtual

lecture08_02.cpp

metoda s klíčovým slovem virtual ve třídě předkapřekrývající metody ve třídách potomků můžou a nemusí mít klíčovéslovo virtual (metoda je virtuální každopádně)od C++11: klíčové slovo override

sdělujeme úmysl překrýt (override) metodu z předkapřekladač nás hlídá

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 5 / 30

Page 6: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Časná a pozdní vazba – Destruktory

V čem je problém?

lecture08_03.cpp, lecture08_04.cpp

class Base {public:

~Base() { std::cout << "~Base()\n"; }};class Derived : public Base {public:

~Derived() { std::cout << "~Derived()\n"; }};int main() {

Derived d;std::unique_ptr<Base> uptr = std::make_unique<Derived>();

}

Co s tím? A co když Base ani Derived nemají destruktor?

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 6 / 30

Page 7: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Virtuální destruktory

Herb Sutter: A base class destructor should be either public and virtual,or protected and nonvirtual.

In brief, then, you’re left with one of two situations. Either:1 you want to allow polymorphic deletion through a base pointer, in

which case the destructor must be virtual and public; or2 you don’t, in which case the destructor should be nonvirtual and

protected, the latter to prevent the unwanted usage.

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 7 / 30

Page 8: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Doporučení

Kdy psát virtual?

je daná třída součástí objektové hierarchie?bude se od dané třídy dědit?je metoda určená k tomu, aby ji potomci překrývali vlastníimplementací?

Jak psát virtuální metody?

v předkovi použijte klíčové slovo virtualv potomcích použijte klíčové slovo override

ušetří to spoustu času stráveného hledáním chybv potomcích není potřeba psát virtual

co když nemám v předkovi žádnou vhodnou implementaci?použijte čistě virtuální metodu (syntax = 0)

lecture08_05.cppPB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 8 / 30

Page 9: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Abstraktní třídy, rozhraní

Abstraktní třída

má alespoň jednu čistě virtuální metodunelze od ní vytvářet instance

Čistě abstraktní třída

všechny její metody jsou čistě virtuální (s případnou výjimkoudestruktoru)

Rozhraní (interface)

čistě abstraktní třída bez atributů (s případnou výjimkou statickýchpoložek)nedrží žádná data, deklaruje pouze, které metody bude možno volatv C++ není speciální klíčové slovo, jen dohoda

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 9 / 30

Page 10: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Vícenásobná dědičnost

Je možno dědit od více tříd?

ano

Je to dobrý nápad?

jak kdy; bez problému, pokud dědíme pouze od rozhraní

class IPrinter { /* ... */ };class IScanner { /* ... */ };class Copier: public IPrinter, public IScanner { /* ... */ };

Co když dědíme od jiných druhů tříd?

může nastat problém, pokud se tyto třídy nějakým způsobempřekrývají (mají stejnou metodu/atribut, dědí od stejné třídy)problém s děděním od stejné třídy se nazývá diamond problemřeší se virtuální dědičností (pokročilejší téma, v následujícímpředmětu) lecture08_06.cpp

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 10 / 30

Page 11: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Dědičnost a přetypování

Co už víte o přetypování v C++

Céčkové přetypování ve stylu (typ)hodnota není typově bezpečné anemělo by se v C++ používat; proč?

tohle přetypování postupně zkouší všechny možnosti(včetně reinterpret_cast a const_cast)taky není syntakticky moc pěkné (jakou má prioritu?)

pro přetypování mezi primitivními typy můžeme použítstatic_cast<typ>(hodnota)

změna celočíselné hodnoty na enumzměna ukazatele na void * a naopak (pozor na nedef. chování)

http://en.cppreference.com/w/cpp/language/explicit_cast

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 11 / 30

Page 12: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Dědičnost a přetypování

Přetypování v objektové hierarchii

ukazatel na potomka se umí implicitně konvertovat na ukazatel napředka (podobně pro reference)opačný směr?

static_cast<typ>(hodnota)jen když zcela určitě vím, že objekt je skutečně daného typuco když to nevím? nedefinované chování!

dynamic_cast<typ>(hodnota)rozhoduje se za běhu programujen pokud je někde v hierarchii virtuální metoda (proč?)používejte zřídka a jen pokud k tomu máte dobrý důvod, preferujterozumně navržené virtuální metody

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 12 / 30

Page 13: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Dědičnost a přetypování

lecture08_07.cpp

dynamic_cast<typ>(hodnota)k určení, zda je hodnota skutečně daného typu, se používá ukazatel navirtuální tabulku

pokud dynamic_cast neuspěje:pokud je typ ukazatel, vrátí nullptrpokud je typ reference, vyhodí výjimku typu std::bad_cast

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 13 / 30

Page 14: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Kompozice vs. dědičnost

Dědičnost je vztah IS-A

potomek má vlastnosti předkapotomka je možno přetypovat na předka

Kompozice je vztah HAS-A

objekt se skládá z jiných objektůauto se skládá z motoru, kol, dveří, …počítač se skládá z procesoru, paměti, disků, …

reprezentace v OOP jazyce:třída má atributy, které jsou typu jiné třídy

třída tím obsahuje vlastnosti těchto jiných třídnení ale jejich potomkem, nemůže je zastoupit

Proč o tom mluvíme? Časté zneužití dědičnosti tam, kde by byla lepšíkompozice.

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 14 / 30

Page 15: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Příklady špatného použití dědičnosti

class Laptop : public CPU, public RAM { /* ... */ };

class Stack : public Vector { /* ... */ };

class Properties : public HashTable { /* ... */ };

class Square : public Rectangle { /* ... */ }; // ???

class ComputerWithPrinter : public Computer { /* ... */ };

Složitější:

class Teacher : public Person { /* ... */ };class Student : public Person { /* ... */ };

co když je někdo zároveň student a učitel?kompozice místo dědičnosti: role

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 15 / 30

Page 16: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Návrhové principy a vzory

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 16 / 30

Page 17: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Návrhové principy v OOP

Jak správně navrhnout objektovou hierarchii?

co umístit do jedné třídy?jaké vztahy mezi třídami?

Jak se pozná špatný návrh?

malé změny vyžadují velké úpravyzměny způsobí nečekané problémyvelká provázanost (s konkrétní aplikací, s jiným kódem)

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 17 / 30

Page 18: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Návrhové principy v OOP (pokr.)

Základní návrhové principy pro OOP

SRP – The Single Responsiblity PrincipleOCP – The Open/Closed PrincipleLSP – The Liskov Substitution PrincipleISP – The Interface Segregation PrincipleDIP – The Dependency Inversion Principle

https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)(odkazy v části References)

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 18 / 30

Page 19: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Principy SOLID

The Single Responsiblity Principle

(už jsme viděli)„Třída by měla mít jediný důvod ke změně.“obecnější princip: každý se stará o jednu věc

Důsledky porušení

změna jedné z funkcionalit vyžaduje rekompilaciúprava kódu poruší více funkcionalit

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 19 / 30

Page 20: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Principy SOLID

Příklad porušení SRP

třída Rectangle zároveň vykresluje čtverec a počítá jeho obsahněkteří uživatelé potřebují obě tyto funkcionality, někteří jen jednu

Lepší řešení

rozdělit funkcionalitu Rectangle do dvou třído počítání obsahu se postará GeometricRectangle

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 20 / 30

Page 21: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Principy SOLID

The Open/Closed Principle

„Třídy by mělo být možné rozšiřovat, ale bez jejich modifikace.“nejen třídy (moduly, jiné entity)open for extension: možnost přidávat chováníclosed for modification: možnost použití jiným kódem

dosahuje se použitím dědičnosti a abstrakce

Související

nepoužívat globální proměnnésoukromé atributy objektůpoužívat dynamic_cast opatrně (proč?)

Důsledek porušení: kaskáda změn v kódu

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 21 / 30

Page 22: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Principy SOLID

The Liskov Substitution Principle

(už jsme v podstatě viděli)„Potomek může zastoupit předka.“funkce očekávající objekt typu předka musí fungovat s objekty typupotomka, aniž by o tom vědělyúzce souvisí s OCP

Důsledek porušení: neočekávané chování (špatné předpoklady)

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 22 / 30

Page 23: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Principy SOLID

Příklad porušení LSP

mějme třídu Rectangle a třídu Square, která z ní dědíobě mají metody setWidth a setHeight (a odpovídající get)

void f(Rectangle& r) {r.setWidth(5);r.setHeight(6);assert(r.getWidth() * r.getHeight() == 30);

}

kde je problém?

čtverec je pravoúhelník, ale objekt typu Square není objektem typuRectangle – mají jiné chování

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 23 / 30

Page 24: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Principy SOLID

The Interface Segregation Principle

„Vytvářejte specifická rozhraní pro specifické klienty.“více malých rozhraní je lepší než jedno obrovskéklienti by neměli být nuceni záviset na rozhraních, které nepoužívajísouvisí s SRP

Jak dosáhnout?

rozhraní s mnoha metodami: kdo je používá?opravdu používají všichni všechny metody?rozdělit metody do samostatných rozhranípoužití vícenásobné dědičnosti

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 24 / 30

Page 25: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Principy SOLID

The Dependency Inversion Principle

obecné třídy by neměly záviset na specializovaných třídách;všichni by měli záviset na abstrakcíchabstrakce by neměly záviset na detailech, ale naopaksouvisí s OCP a LSP

použití rozhraní (čistě abstraktních tříd)

Důsledky porušení:

přidání podpory nového typu vede ke změně v obecné tříděobecná třída použitelná jen pro co byla implementována

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 25 / 30

Page 26: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Principy SOLID

Příklad porušení:

třída Worker s metodou work() a třída Manager, která ji používázávislost Manager → Workerco když budeme chtít přidat novou třídu SuperWorker?

Řešení:

vytvoření abstrakce (rozhraní) IWorkerzávislosti Manager → IWorker, Worker → IWorker, …

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 26 / 30

Page 27: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Keep It Simple, Stupid!

Princip KISS

jednoduché, postačující řešení může být lepší než komplikované arafinované

snazší pochopení (dalšími vývojáři, námi samotnými)menší riziko chyby

kompromis mezi aktuálními a budoucími požadavkypříliš mnoho budoucích požadavků návrh komplikujeomezení na aktuální požadavky vadí budoucí rozšiřitelnosti

průběžný vývoj, refaktorizace (nebát se!)

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 27 / 30

Page 28: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Návrhové vzory

Motivace

opakující se programátorské problémyopakující se způsoby řešenívhodné konstrukce pro řešení: návrhové vzory

kniha Design Patterns: Elements of Reusable Object-OrientedSoftware (23 vzorů)autoři Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides(tzv. Gang of Four, GoF)

existují i jiné návrhové vzorykde začít? https://en.wikipedia.org/wiki/Software_design_pattern

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 28 / 30

Page 29: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Návrhové vzory

Co je návrhový vzor?

(jednoduchý) způsob, jak řešit skupiny podobných problémůopakovatelný, (víceméně) nezávislý na konkrétní aplikaci či jazyce

Pozitiva

řeší běžné problémymohou zlepšovat kód a jeho udržovatelnost

Kritika

zbytečně složité na jednoduché problémynadužívání může vést ke komplikovanějšímu kódučasto řeší jen nedokonalost používaného jazyka

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 29 / 30

Page 30: PB161 Programování v jazyce C++ Přednáška 8 - Dědičnost ... · PB161 Programování v jazyce C++ Přednáška 8 Dědičnost Návrhové principy v OOP Nikola Beneš 13. listopadu

Závěrečný kvíz

https://kahoot.itclass A {public:

void f() { std::cout << "Af "; g(); }virtual void g() { std::cout << "Ag "; }

};class B : public A {public:

void f() { std::cout << "Bf "; }void g() override { std::cout << "Bg "; }

};

int main() {B b; A* ptr = &b; ptr->f();

}

PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 30 / 30


Recommended