Dedenie a zloženie (vzťah Is-a vs. Has-a) v Jave

1. Prehľad

Dedičstvo a zloženie - spolu s abstrakciou, zapuzdrením a polymorfizmom - sú základnými kameňmi objektovo orientovaného programovania (OOP).

V tomto tutoriáli sa budeme venovať základom dedičnosti a zloženia a zameriame sa dôrazne na zisťovanie rozdielov medzi týmito dvoma typmi vzťahov.

2. Základy dedičstva

Dedenie je mocný, ale nadmerne používaný a zneužívaný mechanizmus.

Jednoducho povedané, s dedičnosťou základná trieda (aka základný typ) definuje stav a správanie bežné pre daný typ a umožňuje podtriedam (podtypy aka) poskytovať špecializované verzie tohto stavu a správania.

Aby sme mali jasnú predstavu o tom, ako pracovať s dedičstvom, vytvorme si naivný príklad: základná trieda Osoba ktorý definuje spoločné polia a metódy pre človeka, zatiaľ čo podtriedy Servírka a Herečka poskytujú ďalšie, jemnozrnné implementácie metód.

Tu je Osoba trieda:

public class Osoba {private final Názov reťazca; // ďalšie polia, štandardné konštruktory, getre}

A toto sú podtriedy:

public class Waitress extends Person {public String serveStarter (String starter) {return "Serving a" + starter; } // ďalšie metódy / konštruktory} 
public class Actress extends Person {public String readScript (String movie) {return "Reading the script of" + movie; } // ďalšie metódy / konštruktory}

Okrem toho vytvorme jednotkový test na overenie, či sú inštancie súboru Servírka a Herečka triedy sú tiež inštanciami triedy Osoba, čo ukazuje, že podmienka „je – a“ je splnená na úrovni typu:

@Test public void givenWaitressInstance_whenCheckedType_thenIsInstanceOfPerson () {assertThat (nová servírka ("Mary", "[chránená e-mailom]", 22)) .isInstanceOf (Person.class); } @Test public void givenActressInstance_whenCheckedType_thenIsInstanceOfPerson () {assertThat (nová herečka ("Susan", "[chránená e-mailom]", 30)) .isInstanceOf (Person.class); }

Je dôležité zdôrazniť tu sémantický aspekt dedičstva. Okrem opätovného použitia implementácie Trieda osôb, vytvorili sme presne definovaný vzťah „je-a“ medzi základným typom Osoba a podtypy Servírka a Herečka. Servírky a herečky sú v skutočnosti osoby.

Môže to spôsobiť, že sa budeme pýtať: v ktorých prípadoch použitia je správnym prístupom dedičstvo?

Ak podtypy spĺňajú podmienku „je-a“ a hlavne poskytujú aditívne funkcie ďalej v hierarchii tried,potom je dedičstvo tou správnou cestou.

Prepísanie metódy je samozrejme povolené, pokiaľ si prepísané metódy zachovajú zameniteľnosť základného typu / podtypu podporovanú princípom substitúcie Liskov.

Ďalej by sme to mali mať na pamäti podtypy dedia API základného typu, čo môže byť v niektorých prípadoch nadmerné alebo iba nežiaduce.

V opačnom prípade by sme mali namiesto toho použiť zloženie.

3. Dedičnosť v návrhových vzoroch

Aj keď panuje zhoda v tom, že by sme mali uprednostňovať zloženie pred dedičstvom, kedykoľvek je to možné, existuje niekoľko typických prípadov použitia, keď má dedičstvo svoje miesto.

3.1. Vzor supertypu vrstvy

V tomto prípade my použite dedičstvo na presunutie spoločného kódu do základnej triedy (supertypu) na úrovni jednotlivých vrstiev.

Tu je základná implementácia tohto vzoru vo doménovej vrstve:

verejná trieda Entita {chránené dlhé ID; // nastavovatelia} 
verejná trieda Používateľ rozširuje entitu {// ďalšie polia a metódy} 

Rovnaký prístup môžeme uplatniť aj na ďalšie vrstvy v systéme, napríklad na vrstvy služieb a perzistencie.

3.2. Vzor metódy šablón

Vo vzore metódy šablóny môžeme použite základnú triedu na definovanie invariantných častí algoritmu a potom implementujte variantné časti do podtried:

verejná abstraktná trieda ComputerBuilder {verejné konečné Computer buildComputer () {addProcessor (); addMemory (); } verejné abstraktné neplatné addProcessor (); public abstract void addMemory (); } 
public class StandardComputerBuilder rozširuje ComputerBuilder {@Override public void addProcessor () {// implementácia metódy} @Override public void addMemory () {// implementácia metódy}}

4. Základné kompozície

Skladba je ďalším mechanizmom poskytovaným OOP na opätovné použitie implementácie.

Stručne, kompozícia nám umožňuje modelovať objekty, ktoré sú zložené z iných objektov, čím sa definuje vzťah „má-a“ medzi nimi.

Ďalej zloženie je najsilnejšou formou asociácie, čo znamená, že objekty, ktoré tvoria alebo sú obsiahnuté jedným objektom, sa zničia tiež, keď sa tento objekt zničí.

Aby sme lepšie pochopili, ako funguje kompozícia, predpokladajme, že musíme pracovať s objektmi, ktoré predstavujú počítače.

Počítač sa skladá z rôznych častí, vrátane mikroprocesora, pamäte, zvukovej karty atď., Takže môžeme modelovať počítač aj každú z jeho častí ako samostatné triedy.

Tu je príklad jednoduchej implementácie Počítač trieda môže vyzerať:

verejná trieda Počítač {súkromný procesor procesora; súkromná pamäť Memory; súkromná zvuková karta SoundCard; // štandardné getre / setre / konštruktory public Voliteľné getSoundCard () {návrat Voliteľné.ofNullable (soundCard); }}

Nasledujúce triedy modelujú mikroprocesor, pamäť a zvukovú kartu (rozhrania sú kvôli stručnosti vynechané):

verejná trieda StandardProcessor implementuje procesor {private String model; // štandardné getre / settery}
verejná trieda StandardMemory implementuje pamäť {private String brand; veľkosť súkromného reťazca; // štandardné konštruktory, getre, toString} 
verejná trieda StandardSoundCard implementuje SoundCard {súkromná značka String; // štandardné konštruktory, getre, toString} 

Je ľahké pochopiť motiváciu, ktorá stojí za pretláčaním zloženia nad dedičstvom. V každom scenári, kde je možné vytvoriť sémanticky správny vzťah typu „has-a“ medzi danou triedou a ostatnými, je kompozícia tou správnou voľbou.

Vo vyššie uvedenom príklade Počítač spĺňa podmienku „má-a“ s triedami, ktoré modelujú jej časti.

Je tiež potrebné poznamenať, že v tomto prípade obsahujúci Počítač objekt má vlastníctvo obsiahnutých objektov keby a len keby objekty nemôžu byť znovu použité v inom Počítač objekt. Ak môžu, používali by sme skôr agregáciu ako zloženie, kde vlastníctvo nie je naznačené.

5. Zloženie bez abstrakcie

Prípadne sme mohli definovať vzťah kompozície pevným kódovaním závislostí súboru Počítač triedy, namiesto ich deklarovania v konštruktore:

verejná trieda Počítač {súkromný procesor StandardProcessor = nový StandardProcessor ("Intel I3"); súkromná pamäť StandardMemory = nová StandardMemory ("Kingston", "1TB"); // ďalšie polia / metódy}

Samozrejme by to bol tuhý a pevne spojený dizajn, aký by sme vyrábali Počítač výrazne závisí od konkrétnych implementácií procesor a Pamäť.

Nepoužili by sme výhody úrovne abstrakcie poskytovanej rozhraniami a vkladaním závislostí.

S počiatočným dizajnom založeným na rozhraniach dostaneme voľne spojený dizajn, ktorý je tiež ľahšie otestovateľný.

6. Záver

V tomto článku sme sa naučili základy dedičnosti a zloženia v Jave a podrobne sme preskúmali rozdiely medzi týmito dvoma typmi vzťahov („je-a“ vs. „má-a“).

Ako vždy, všetky ukážky kódu zobrazené v tomto tutoriále sú k dispozícii na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found