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

Podobné dokumenty
PB161 Programování v jazyce C++ Přednáška 11

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

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

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

Programování II. Polymorfismus

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

Chování konstruktorů a destruktorů při dědění

Dědění, polymorfismus

Generické programování

Programování v C++ 1, 6. cvičení

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

Programování II. Abstraktní třída Vícenásobná dědičnost 2018/19

1. Dědičnost a polymorfismus

Návrhové vzory. Jakub Klemsa, Jan Legerský. 30. října Objektově orientované programování.

NPRG031 Programování II 1 / :25:46

NMIN201 Objektově orientované programování 1 / :36:09

24. listopadu 2013, Brno Připravil: David Procházka

Jazyk C++ I. Polymorfismus

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

Třídy. Instance. Pokud tento program spustíme, vypíše následující. car1 má barvu Red. car2 má barvu Red. car1 má barvu Blue.

Obsah přednášky 9. Skrývání informací. Skrývání informací. Zapouzdření. Skrývání informací. Základy programování (IZAPR, IZKPR) Přednáška 9

PB161 Základy OOP. Tomáš Brukner

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

PB přednáška (21. září 2015)

TŘÍDY POKRAČOVÁNÍ. Události pokračování. Příklad. public delegate void ZmenaSouradnicEventHandler (object sender, EventArgs e);

Úvod Třídy Rozhraní Pole Konec. Programování v C# Hodnotové datové typy, řídící struktury. Petr Vaněček 1 / 39

Jazyk C# (seminář 3)

Polymorfismus. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 30.března

PB přednáška (5. října 2015)

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

Návrhové vzory OMO, LS 2014/2015

Programování II. Návrh programu II

Bridge. Známý jako. Účel. Použitelnost. Handle/Body

Dynamicky vázané metody. Pozdní vazba, virtuální metody

Programování II. Úvod do dědičnosti 2018/19

IoC/DI. Tomáš Herceg Microsoft MVP (ASP.NET)

Virtuální metody - polymorfizmus

Vývoj informačních systémů. Architektura, návrh Vzory: Doménová logika

PB přednáška (12. října 2015)

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

Programování v C++ 3, 3. cvičení

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Dědičnost. Karel Richta a kol. katedra počítačů FEL ČVUT v Praze. Karel Richta, Aleš Hrabalík a Martin Hořeňovský, 2016

3. Je defenzivní programování technikou skrývání implementace? Vyberte jednu z nabízených možností: Pravda Nepravda

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

Programování v C++ 2, 8. cvičení

Programování v C++ 1, 5. cvičení

PB161 programování v C++ Výjimky Bezpečné programování

typová konverze typová inference

Vývoj informačních systémů. Architektura, návrh Vzory: Doménová logika

Vyřešené teoretické otázky do OOP ( )

Programování v C++ 2, 4. cvičení

Dědičnost (inheritance)

Objekty v PHP 5.x. This is an object-oriented system. If we change anything, the users object.

Pokročilé programování v jazyce C pro chemiky (C3220) Dědičnost tříd v C++

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

Pokročilé programování v jazyce C pro chemiky (C3220) Pokročilá témata jazyka C++

7. OBJEKTOVĚ ORIENTOVANÉ PROGRAMOVÁNÍ

C# - OOP (object oriented programming)

Programování II. Dědičnost změna chování 2018/19

IB111 Programování a algoritmizace. Objektově orientované programování (OOP)

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

Dědičnost. Časová náročnost lekce: 3 hodiny Datum ukončení a splnění lekce: 23.března

11 Diagram tříd, asociace, dědičnost, abstraktní třídy

20. Projekt Domácí mediotéka

, Brno Připravil: David Procházka Návrhové vzory

Programování v C++ VI

MATURITNÍ OTÁZKY ELEKTROTECHNIKA - POČÍTAČOVÉ SYSTÉMY 2003/2004 PROGRAMOVÉ VYBAVENÍ POČÍTAČŮ

Dědičnost. seskupování tříd do hierarchie. potomek získá všechny vlastnosti a metody. provádí se pomocí dvojtečky za názvem třídy.

Programování II. Návrh programu I 2018/19

Principy OOP - rozhraní, dědičnost PB161

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

Abstraktní třída a rozhraní

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

Programování II. Třídy a objekty (objektová orientovanost) 2018/19

Viditelnost (práva přístupu) Tomáš Pitner, upravil Marek Šabo

Objektové programování

Základy objektové orientace I. Únor 2010

Semin aˇr Java N avrhov e vzory Radek Ko ˇc ı Fakulta informaˇcn ıch technologi ı VUT Duben 2009 Radek Koˇc ı Semin aˇr Java N avrhov e vzory 1/ 25

PŘETĚŽOVÁNÍ OPERÁTORŮ

IRAE 07/08 Přednáška č. 1

UJO Framework. revoluční architektura beans. verze

PRINCIPY OOP, DĚDIČNOST. PB161 Principy OOP - Zapouzdření

State. Známý jako. Účel. Použitelnost. Stav, Object for States. umožňuje objektu měnit svoje chování v závislosti na stavu objekt mění svou třídu

Seminář Java IV p.1/38

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

Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody

Semin aˇr Java N avrhov e vzory Radek Ko ˇc ı Fakulta informaˇcn ıch technologi ı VUT Duben 2008 Radek Koˇc ı Semin aˇr Java N avrhov e vzory 1/ 24

Nepravidlové a hybridní znalostní systémy

10 Balíčky, grafické znázornění tříd, základy zapozdření

Osnova přednášky. Programové prostředky řízení Úvod do C# II. Přístup ke členům. Členy (Members)

Výčtový typ strana 67

Unity a Objekty (NMIN102) RNDr. Michal Žemlička, Ph.D.

Principy objektově orientovaného programování

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

1. Programování proti rozhraní

Ukazka knihy z internetoveho knihkupectvi

Více o konstruktorech a destruktorech

11. Dědičnost. Dědičnost strana 103

Jazyk C++ I. Polymorfismus

Transkript:

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

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

Dědičnost Co už víte z minula? třída může dědit od jiné třídy co všechno se dědí? atributy i metody dědičnost realizuje podtypy vztah IS-A luč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ódu není to ale jediná možnost kdy 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

Dědičnost v C++ class Base { /*... */ }; class Derived : public Base { /*... */ }; v tomto předmětu používáme pouze public dědičnost Derived má přístup k: veřejným a protected atributům a metodám Base uživatelé Derived mají přístup k: veřejným atributům a metodám Derived včetně zděděných veřejných atributů a metod v jakém pořadí se volají konstruktory? konstruktor předka se provede před konstruktorem potomka předek je jakoby prvním atributem potomka v jakém pořadí se volají destruktory? destruktor potomka se provede před destruktorem předka lecture08_01.cpp PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 4 / 30

Č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/reference pozdní 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 metoda s klíčovým slovem virtual ve třídě předka př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ředka překladač nás hlídá lecture08_02.cpp PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 5 / 30

Časná a pozdní vazba Destruktory V čem je problém? 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? lecture08_03.cpp, lecture08_04.cpp PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 6 / 30

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; or 2 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

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 virtual v potomcích použijte klíčové slovo override ušetří to spoustu času stráveného hledáním chyb v 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.cpp PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 8 / 30

Abstraktní třídy, rozhraní Abstraktní třída má alespoň jednu čistě virtuální metodu nelze od ní vytvářet instance Čistě abstraktní třída všechny její metody jsou čistě virtuální (s případnou výjimkou destruktoru) Rozhraní (interface) čistě abstraktní třída bez atributů (s případnou výjimkou statických položek) nedrží žádná data, deklaruje pouze, které metody bude možno volat v 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

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ůsobem př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ím předmětu) lecture08_06.cpp PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 10 / 30

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é a nemě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žít static_cast<typ>(hodnota) změna celočíselné hodnoty na enum změ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

Dědičnost a přetypování Přetypování v objektové hierarchii ukazatel na potomka se umí implicitně konvertovat na ukazatel na př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 typu co když to nevím? nedefinované chování! dynamic_cast<typ>(hodnota) rozhoduje se za běhu programu jen pokud je někde v hierarchii virtuální metoda (proč?) používejte zřídka a jen pokud k tomu máte dobrý důvod, preferujte rozumně navržené virtuální metody PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 12 / 30

Dědičnost a přetypování dynamic_cast<typ>(hodnota) k určení, zda je hodnota skutečně daného typu, se používá ukazatel na virtuální tabulku pokud dynamic_cast neuspěje: pokud je typ ukazatel, vrátí nullptr pokud je typ reference, vyhodí výjimku typu std::bad_cast lecture08_07.cpp PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 13 / 30

Kompozice vs. dědičnost Dědičnost je vztah IS-A potomek má vlastnosti předka potomka 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říd není 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

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

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

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é úpravy změny způsobí nečekané problémy velká 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

Návrhové principy v OOP (pokr.) Základní návrhové principy pro OOP SRP The Single Responsiblity Principle OCP The Open/Closed Principle LSP The Liskov Substitution Principle ISP The Interface Segregation Principle DIP 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

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

Principy SOLID Příklad porušení SRP třída Rectangle zároveň vykresluje čtverec a počítá jeho obsah někteří uživatelé potřebují obě tyto funkcionality, někteří jen jednu Lepší řešení rozdělit funkcionalitu Rectangle do dvou tříd o počítání obsahu se postará GeometricRectangle PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 20 / 30

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

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 typu potomka, 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

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 typu Rectangle mají jiné chování PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 23 / 30

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

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ích abstrakce by neměly záviset na detailech, ale naopak souvisí 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

Principy SOLID Příklad porušení: Řešení: třída Worker s metodou work() a třída Manager, která ji používá závislost Manager Worker co když budeme chtít přidat novou třídu SuperWorker? vytvoření abstrakce (rozhraní) IWorker závislosti Manager IWorker, Worker IWorker, PB161 přednáška 8: dědičnost, návrhové principy 13. listopadu 2018 26 / 30

Keep It Simple, Stupid! Princip KISS jednoduché, postačující řešení může být lepší než komplikované a rafinované snazší pochopení (dalšími vývojáři, námi samotnými) menší riziko chyby kompromis mezi aktuálními a budoucími požadavky příliš mnoho budoucích požadavků návrh komplikuje omezení 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

Návrhové vzory Motivace opakující se programátorské problémy opakující se způsoby řešení vhodné konstrukce pro řešení: návrhové vzory kniha Design Patterns: Elements of Reusable Object-Oriented Software (23 vzorů) autoři Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (tzv. Gang of Four, GoF) existují i jiné návrhové vzory kde 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

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 Kritika řeší běžné problémy mohou zlepšovat kód a jeho udržovatelnost zbytečně složité na jednoduché problémy naduží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

Závěrečný kvíz https://kahoot.it class 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