Dědičnost (programování)

Dědičnost (angl. inheritance ) - koncept objektově orientovaného programování , podle kterého může abstraktní datový typ zdědit data a funkčnost nějakého existujícího typu, což usnadňuje opětovné použití softwarových komponent .

Terminologie

V objektově orientovaném programování se od Simuly 67 abstraktní datové typy nazývají třídy .

Superclass ( eng.  super class ), parent class ( eng.  parent class ), předek, parent nebo superclass - třída, která produkuje dědičnost v podtřídách, tedy třída, ze které dědí ostatní třídy. Nadtřídou může být podtřída, základní třída, abstraktní třída a rozhraní.

Podtřída ( eng.  subclass ), odvozená třída ( eng.  odvozená třída ), podřízená třída ( eng.  child class ), podřízená třída, následnická třída nebo implementační třída - třída zděděná z nadtřídy nebo rozhraní, tj. třída definovaná prostřednictvím dědičnosti z další třída nebo několik takových tříd. Podtřída může být nadtřídou.

Základní třída  je třída , která je na vrcholu hierarchie dědičnosti tříd a na konci stromu podtříd, to znamená, že není podtřídou a nedědí z jiných nadtříd nebo rozhraní. Základní třídou může být abstraktní třída a rozhraní. Jakákoli nezákladová třída je podtřídou.

Rozhraní je struktura , která definuje čisté  rozhraní třídy sestávající z abstraktních metod. Rozhraní se účastní hierarchie dědičnosti tříd a rozhraní.

Superinterface ( anglicky  super interface ) nebo rozhraní předka je analogií nadtřídy v hierarchii dědičnosti, to znamená, že je to rozhraní, které dědí v podtřídách a podrozhraních.

Rozhraní potomka, odvozené rozhraní nebo odvozené  rozhraní je analogem podtřídy v hierarchii dědičnosti rozhraní, tj. je to rozhraní zděděné z jednoho nebo více superrozhraní.

Základní rozhraní je ekvivalentem základní třídy v hierarchii dědičnosti rozhraní, tj. jedná se o rozhraní na vrcholu hierarchie dědičnosti.

Hierarchie dědičnosti nebo hierarchie tříd je strom, jehož prvky jsou třídy a rozhraní.

Aplikace

Dědičnost je mechanismus pro opětovné použití kódu (anglicky code reuse ) a přispívá k nezávislému rozšiřování softwaru prostřednictvím otevřených tříd (anglické veřejné třídy) a rozhraní (anglická rozhraní). Nastavením vztahu dědičnosti mezi třídami se generuje hierarchie tříd.

Dědičnost a polymorfismus podtypů

Dědičnost je často identifikována s polymorfismem podtypů :

Navzdory výše uvedené poznámce je dědičnost široce používaným mechanismem pro vytvoření vztahu is -a. Některé programovací jazyky souhlasí s dědičností a polymorfismem podtypů (většinou staticky typované jazyky jako C++C#Java a Scala ), zatímco jiné sdílejí výše uvedené koncepty.

Dědičnost – dokonce ani v programovacích jazycích, které podporují použití dědičnosti jako mechanismu pro polymorfismus podtypů – nezaručuje polymorfismus chování podtypů; viz: „Princip substituce“ od Barbary Liskov .

Typy dědičnosti

"Jednoduché" dědictví

"Jednoduchá" dědičnost, někdy nazývaná jednoduchá dědičnost, popisuje vztah mezi dvěma třídami, z nichž jedna dědí druhou. Mnoho tříd může pocházet z jedné třídy, ale i tak tento druh vztahu zůstává "jednoduchým" dědičností.

Abstraktní třídy a tvorba objektů

Pro některé programovací jazyky platí následující koncept.

Existují „abstraktní“ třídy (deklarované jako takové svévolně nebo kvůli abstraktním metodám , které jim byly přiřazeny ); mohou být popsány jako mající pole a metody . Vytváření objektů (instancí) znamená konkretizaci , použitelnou pouze pro neabstraktní třídy (včetně neabstraktních potomků abstraktních), jejichž představiteli budou ve výsledku vytvořené objekty.

Příklad: Základní třída „Zaměstnanec univerzity “, ze které se dědí třídy „ Doktorand “ a „ Profesor “, nechť je abstraktní. Běžná pole a funkce tříd (například pole „Rok narození“) lze popsat v základní třídě. A program vytvoří objekty pouze odvozených tříd: "Postgraduální student" a "Profesor"; obvykle nemá smysl vytvářet objekty základních tříd.

Vícenásobná dědičnost

Při vícenásobné dědičnosti může mít třída více než jednoho rodiče. V tomto případě třída zdědí metody všech předků. Výhodou tohoto přístupu je větší flexibilita.

Vícenásobná dědičnost je implementována v C++ . Mezi další jazyky, které tuto funkci poskytují, patří Python a Eiffel . V UML je podporována vícenásobná dědičnost .

Vícenásobná dědičnost je potenciálním zdrojem chyb, které mohou vzniknout v důsledku stejných názvů metod v předcích. V jazycích, které jsou umístěny jako nástupci C++ ( Java , C# a další), bylo rozhodnuto opustit vícenásobnou dědičnost ve prospěch rozhraní . Bez použití tohoto mechanismu se téměř vždy obejdete. Pokud však přesto taková potřeba vyvstala, pak pro vyřešení konfliktů při použití zděděných metod se stejnými názvy je možné například použít operaci rozšíření viditelnosti - "::" - zavolat konkrétní metodu a konkrétního rodiče.

Pokus o vyřešení problému se stejnými názvy metod u předků byl učiněn v jazyce Eiffel , ve kterém je při popisu nové třídy nutné explicitně uvést importované členy každé z zděděných tříd a jejich pojmenování v dětská třída.

Většina moderních objektově orientovaných programovacích jazyků ( C# , Java , Delphi a další) podporuje schopnost současně dědit od třídy předků a implementovat metody několika rozhraní stejnou třídou. Tento mechanismus umožňuje do značné míry nahradit vícenásobnou dědičnost – metody rozhraní musí být explicitně předefinovány, což eliminuje chyby při dědění funkčnosti stejných metod různých tříd předků.

Jedna základní třída

V řadě programovacích jazyků všechny třídy, ať už explicitně nebo implicitně, dědí z nějaké základní třídy. Smalltalk byl jedním z prvních jazyků, které tento koncept použily. Mezi tyto jazyky také patří: Objective-C (třída NSObject), Perl ( UNIVERSAL), Eiffel ( ANY), Java ( java.lang.Object), C# ( System.Object), Delphi ( TObject), Scala ( Any).

Dědičnost v programovacích jazycích

C++

Dědičnost v C++ :

třídaA { }; // Základní třída třída B : public A {}; // Veřejná dědičnost třída C : protected A {}; // Třída chráněné dědičnosti Z : private A {}; // Soukromé dědictví

V C++ existují tři typy dědičnosti : veřejné , chráněné , soukromé . Specifikátory přístupu členů základní třídy se v potomcích mění takto:

  • Pokud je třída deklarována jako základní třída jiné třídy se specifikátorem přístupu...
    • ... veřejné :
      • veřejné členy základní třídy – dostupné jako veřejné členy odvozené třídy;
      • chráněné členy základní třídy - dostupné jako chráněné členy odvozené třídy;
    • … chráněno :
      • veřejné a chráněné členy základní třídy jsou dostupné jako chráněné členy odvozené třídy;
    • … soukromé :
      • veřejné a chráněné členy základní třídy jsou dostupné jako soukromé členy odvozené třídy.

Jednou z hlavních výhod veřejné dědičnosti je, že ukazatel na odvozené třídy lze implicitně převést na ukazatel na základní třídu, takže pro výše uvedený příklad můžete napsat:

A * a = novýB ( );

Tato zajímavá funkce otevírá možnost dynamické identifikace typu (RTTI).

Delphi (Object Pascal)

Chcete-li použít mechanismus dědičnosti v Delphiclass , musíte zadat třídu předka v deklaraci třídy v závorkách :

Předek:

TAncestor = class private protected public // Procedura virtuální procedury VirtualProcedure ; virtuální ; abstraktní ; procedura StaticProcedure ; konec ;

Dědic:

TDescendant = class ( TAncestor ) private protected public // Virtual procedure override procedure VirtualProcedure ; přepsat ; procedura StaticProcedure ; konec ;

Absolutně všechny třídy v Delphi jsou potomky třídy TObject. Pokud není uvedena třída předka, předpokládá se, že nová třída je přímým potomkem třídy TObject.

Vícenásobná dědičnost v Delphi není zpočátku v zásadě podporována, ale pro ty, kteří se bez ní neobejdou, stále existují takové příležitosti, například pomocí pomocných tříd (Сlass Helpers).

Python

Python podporuje jednoduché i vícenásobné dědění. Při přístupu k atributu dochází k prohlížení odvozených tříd v pořadí podle pořadí rozlišení metod  (MRO ) [1] .

class Předek1 ( objekt ): # Předek-1 def m1 ( self ): předat třídu Předek2 ( objekt ): # Předek-2 def m1 ( self ): předat třídu Potomek ( Předek1 , Předek2 ): # Potomek def m2 ( self ): složit d = Potomek () # Tisk instance d . __třída__ . __mro__ # Pořadí rozlišení metody: ( < class ' __main__ . Potomek '>, <class ' __main__ . Předek1 '>, <třída ' __main__ . Předek2 '>, <type ' objekt '>)

Od Pythonu 2.2 koexistují v jazyce „klasické“ třídy a „nové“ třídy. Ti poslední jsou dědici object. Třídy „Classic“ budou podporovány až do verze 2.6, ale budou odstraněny z jazyka v Pythonu 3.0.

Vícenásobná dědičnost se v Pythonu používá zejména k zavedení smíšených tříd do hlavní třídy . 

PHP

Pro použití mechanismu dědičnosti v PHP je nutné v deklaraci třídy za jménem deklarované nástupnické třídy uvést slovo extendsa název třídy předka :

class Potomek rozšiřuje předka { }

Pokud se odvozená třída překrývá s metodami předka, lze k metodám předka přistupovat pomocí parent:

class A { function example () { echo "Metoda A::example() volana.<br /> \n " ; } } class B extends A { function example () { echo "Metoda B::example() volana.<br /> \n " ; rodič :: příklad (); } }

Je možné zabránit odvozené třídě v přepsání metod předka; Chcete-li to provést, musíte zadat klíčové slovo final:

class A { finální příklad funkce () { echo "Volána metoda A::example().<br /> \n " ; } } class B extends A { function example () { //vyhodí chybu parent :: example (); //a nikdy se nespustí } }

Aby bylo možné odkazovat na konstruktor nadřazené třídy během dědění, je nutné, aby podřízená třída byla uvedena v konstruktoru parent::__construct();[2]

Objective-C

@interface A  : NSObject - ( void ) příklad ; @konec @implementation - ( void ) příklad { NSLog ( @"ClassA" ); } @konec @rozhraní B  : A - ( neplatný ) příklad ; @konec @implementation - ( void ) příklad { NSLog ( @"ClassB" ); } @konec

Rozhraní deklaruje metody, které budou viditelné mimo třídu (veřejné).

Interní metody lze implementovat bez rozhraní. Chcete-li deklarovat další vlastnosti, použijte interface-extension v implementačním souboru.

Všechny metody v Objective-C jsou virtuální.

Java

Příklad dědičnosti z jedné třídy a dvou rozhraní :

public class A { } public interface I1 { } public interface I2 { } public class B extends A implements I1 , I2 { }

Direktiva finalv deklaraci třídy znemožňuje její dědění.

C#

Příklad dědičnosti z jedné třídy a dvou rozhraní :

public class A { } public interface I1 { } public interface I2 { } public class B : A , I1 , I2 { }

Dědění z typovaných tříd lze provést zadáním pevného typu nebo přenesením proměnné typu do zděděné třídy:

veřejná třída A < T > { } veřejná třída B : A < int > { } veřejná třída B2 < T > : A < T > { }

Je také možné zdědit vnořené třídy z tříd, které je obsahují:

třída A // výchozí třída A je interní, neveřejná třída B nemůže být veřejná { třída B : A { } }

Direktiva sealedv deklaraci třídy znemožňuje její dědění. [3]

Ruby

třídní rodič def public_method "Veřejná metoda" konec soukromé def private_method "Soukromá metoda" konec konec classChild < Rodič _ def public_method "Předefinovaná veřejná metoda" end def call_private_method "Privátní metoda předka: " + private_method end konec

Třída Parentje předkem třídy, Childjejíž metoda je přepsána public_method.

dítě = dítě . novorozenec . _ public_method #=> "Předefinovaná veřejná metoda" potomek . call_private_method #=> "Soukromá metoda předka: Soukromá metoda"

Soukromé metody předka lze volat od potomků.

JavaScript

class Parent { konstruktor ( data ) { this . data = data ; } publicMethod () { return 'Veřejná metoda' ; } } class Child extends Parent { getData () { return `Data: ${ this . data } ` ; } publicMethod () { return 'Předefinovaná veřejná metoda' ; } } const test = newChild ( ' test' ); test . getdata (); // => 'Data: test' test . veřejná metoda (); // => Test 'Předefinovaná veřejná metoda' . údaje ; // => 'test'

Třída Parentje předkem třídy, Childjejíž metoda je přepsána publicMethod.

JavaScript používá prototypovou dědičnost.

Konstruktory a destruktory

V C++ jsou konstruktory volány postupně během dědění od nejstaršího předka po nejnovějšího potomka, a naopak, destruktory jsou volány od nejnovějšího potomka po nejstaršího předka.

classFirst _ { veřejnost : First () { cout << ">>Prvni konstruktor" << endl ; } ~ První () { cout << ">>První destruktor" << endl ; } }; třída Druhá : veřejná První { veřejnost : Druhý () { cout << ">Druhý konstruktor" << endl ; } ~ Druhý () { cout << ">Druhý destruktor" << endl ; } }; třída Třetí : veřejná Druhá { veřejnost : Třetí () { cout << "Třetí konstruktor" << endl ; } ~ Třetí () { cout << "Třetí destruktor" << endl ; } }; // spuštění kódu Third * th = new Third (); odstranit th ; // výsledek výstupu /* >>První konstruktor >Druhý konstruktor Třetí konstruktor Třetí destruktor >Druhý destruktor >>První destruktor */

Odkazy

Poznámky

  1. na pořadí rozlišení metod v Pythonu
  2. Co je objektově orientované programování . wh-db.com (30. června 2015).
  3. Specifikace jazyka C# verze 4.0, Copyright © Microsoft Corporation 1999-2010