poznáme.it Programovanie Používate dedičnosť v objektovom svete správne?

Používate dedičnosť v objektovom svete správne?

Dedičnosť v objektovom svete býva častokrát používaná nevhodne. Keďže návodov na správne použitie dedičnosti je veľa, tento článok uvádza návody na nesprávne použitie dedičnosti (antipatterny). Zároveň vysvetlí, prečo je použitie dedičnosti nesprávne a ako by sa v danom prípade malo postupovať.

Dedičnosť do istej miery porušuje základnú črtu objektovo orientovaného prístupu – zapúzdrenosť. Predstavuje preto najsilnejší typ väzby medzi dvomi triedami. Používa sa na modelovanie špecializácie alebo generalizácie. Ak trieda B dedí od triedy A, znamená to, že B je špeciálnym prípadom triedy A. Alebo že A je zovšeobecnením B. Trieda A sa potom nazýva „bázová trieda“.

V UML sa dedičnosť zakresľuje dutou šípkou, ktorá vedie od špecializovanej triedy k bázovej triede.

Antipattern 1: vynímanie pred zátvorku

Trieda „Bod“ reprezentuje bod v dvojrozmernom priestore s atribútmi x a y. Kružnica je dvojrozmerný útvar so stredom v bode [x,y] a s polomerom. Atribúty x a y sú teda spoločné, kružnica k nim pridáva svoje vlastné rozšírenie v podobe atribútu polomer. Spoločné atribúty zvádzajú k nesprávnemu použitiu dedičnosti.

Uvedený model tried tvrdí, že „Kružnica je špeciálnym prípadom bodu“, resp. že „Zovšeobecnením pojmu kružnica vznikne pojem bod“. Ani jedno tvrdenie nemá zmysel a preto je dedičnosť v tomto prípade nesprávna.

Správny model je asociácia:

Používanie dedičnosti na „vyňatie pred zátvorku“ je najčastejším spôsobom chybného použitia dedičnosti. Preto je vhodné pri každom použití dedičnosti zvážiť, či nejde len o asociáciu tried.

Antipattern 2: zovšeobecnené pojmy mimo domény

Podobná chyba ako v predošlom príklade nastáva aj v tomto:

Je pravda, že žiak aj učiteľ sú špeciálne prípady človeka a zároveň človek je zovšeobecnením pojmov žiak a učiteľ. Pojmy žiak a učiteľ môžu pochádzať napríklad z domény evidencie ľudí v škole. Atribúty triedy Človek hovoria o potrebe evidovať osobné údaje žiakov a učiteľov. V tomto prípade nastala chyba v analýze. Namiesto toho, aby analytik správne odhalil a pomenoval pojem „osobné údaje“, pomohol si nevhodným zovšeobecnením „človek“, ktoré stojí mimo analyzovanej domény. Ide o rovnaký prípad ako v predošlom antipatterne. Úlohou triedy Človek je iba „vyňať pred zátvorku“ spoločné atribúty tried Žiak a Učiteľ.

Správnym riešením je opäť asociácia, možno dokonca kompozícia:

Antipattern 3: porušenie „Liskov substitution principle“

„Liskov substitution principle“ je jeden zo základných princípov objektovo orientovaného prístupu a súčasťou akronymu „SOLID“. Hovorí o možnosti nahradenia bázovej triedy svojou špecializáciou. Porušenie tohto kritéria demonštruje vzťah dedičnosti medzi obdĺžnikom a štvorcom:

Uvedený model tvrdí, že „Štvorec je špeciálnym prípadom obdĺžnika“, alebo že „Obdĺžnik je zovšeobecnením pre štvorec“. Obe tvrdenia sú matematicky správne, napriek tomu je použitie dedičnosti aj v tomto prípade nesprávne. Dôvod je, že trieda Obdĺžnik nie je zameniteľná za triedu Štvorec, čo je podmienka dedičnosti vyžadovaná substitučným princípom. Túto skutočnosť odhalí implementácia.

/** Trieda reprezentuje obdĺžnik definovaný dĺžkami svojich strán. */
public class Obdlznik {
	private final int stranaA;
	private final int stranaB;
	
	public Obdlznik(int stranaA, int stranaB) {
		this.stranaA = stranaA;
		this.stranaB = stranaB;
	}

	/** Metóda vráti obsah obdĺžnika. */
	public int obsah() {
		return stranaA * stranaB;
	}

}

/** Trieda reprezentuje štvorec ako špeciálny prípad obdĺžnika. */
public class Stvorec extends Obdlznik {

	// CHYBA: Porušenie "Liskov substitution principle"
	public Stvorec( int stranaA ) {
		super( stranaA, stranaA );
	}
}

Unit test pre triedu Obdĺžnik testuje fungovanie metódy “obsah”:

@Test
public void testObsahObdlznikaSoStranami3a4je12() {
	Obdlznik obdlznik = new Obdlznik(3, 4);
	int obsah = obdlznik.obsah();
	
	assertEquals(12, obsah);
}

Liskov substitution principle hovorí o tom, že ak sa bázová trieda nahradí špecializáciou, chovanie musí ostať zachované. Avšak v uvedenom príklade začne unit test hlásiť chybu:

@Test
public void testObsahObdlznikaSoStranami3a4je12() {
	Obdlznik obdlznik = new Stvorec( 3 );
	int obsah = obdlznik.obsah();
	
	assertEquals(12, obsah); // chyba: obsah stvorca je 9
}

K príkladu by sa dalo namietať, že ak by vstupný parameter do konštruktora objektu Stvorec bolo číslo odmocnina z 12, unit test by fungoval. Hlavným prehreškom voči substitučnému princípu je však použitie jednoparametrického konštruktora new Stvorec( 3 ), namiesto požadovaného new Stvorec( 3, 4). Trieda Stvorec nedokáže zmysluplne implementovať konštruktor s dvomi rôznymi vstupnými dĺžkami strán, ktoré sú použité v konštruktore triedy Obdlznik.

Tento typ dedičnosti porušuje Liskov substitution principle. A tak aj napriek intuitívnej správnosti dedičnosti je aj v tomto prípade jej použitie nevhodné. Vhodnejšie by bolo vyviesť metódu „obsah“ do spoločného rozhrania, ktoré je oboma triedami implementované rôznym spôsobom:

Liskov substitution principle sa týka zdedených tried, nie implementovaných rozhraní. Takže v unit teste už nevzniká nárok, aby bola funkčnosť platná pre Obdlznik zachovaná aj pre Stvorec. Z pohľadu dizajnu ide o dve nezávislé a nezameniteľné triedy.

Záver

Začínajúci programátor či dizajnér by mal vždy veľmi dobre zvážiť, či je použitie dedičnosti na mieste a či by v danom prípade nebolo lepšie využiť asociáciu. Určite neurobí chybu, ak sa bude snažiť vyhýbať sa použitiu dedičnosti.

Príklady správneho použitia dedičnosti sú uvedené v návrhových vzoroch (design patterns). Správne použitie dedičnosti je zamerané na chovanie, nie na atribúty. Pri zameraní na chovanie je však obvykle lepšie využívať interface-y namiesto konkrétnych tried. Rozhodujúcou motiváciou pre použitie dedičnosti je snaha využiť polymorfizmus.

Odporúčané čítanie:

http://www.oodesign.com/liskov-s-substitution-principle.html


Dobrý článok? Chceš dostávať ďalšie?

Už viac ako 6 200 ITečkárov dostáva správy e-mailom. Nemusíš sa báť, nie každé ráno. Len občasne.



Súhlasím so spracovaním mojich osobných údajov. ( Viac informácií. )

Tvoj email neposkytneme 3tím stranám. Posielame naňho len informácie z robime.it. Kedykoľvek sa môžeš odhlásiť.

Zdeno Jašek
Zdeno Jašek
Pracujem ako Solution Architect vo firme PosAm a programovaním sa zaoberám takmer 30 rokov. Prešiel som jazykmi Basic, Assembler, Pascal, Object Pascal, Lisp, Prolog, Magic, MUMPS, Clipper, Paradox a Java, z ktorých najmilšia mi je Java. Pracoval som hlavne ako softvérový architekt, ale aj ako programátor, analytik, dizajnér a projektový manažér. Pri vývoji softvéru sa mi najviac páči navrhovanie objektového dizajnu – obzvlášť pre zložité aplikácie. Svoje blogy chcem zamerať na postupy pri vytváraní objektového návrhu aplikácie a ich technologickej realizácii v podobe hexagonálnej architektúry a microservices.

Scala Developer/ka

Máš rád svoju slobodu, pracuješ na projektoch, ideálne remote? Staň sa súčasťou startupu, ktorý buduje platformu na podporu maloobchodu a...

Java Software Architect

Mrháš svojim talentom? Urob prvý krok a zistí čo Ti dnešok ponúka. Medziiným aj prácu v medzinárodnej IT spoločnosti,...

Back-End Developer / REMOTE

Pracuj na svetovom SW produkte, ktorý je používaný miliónmi používateľov! Firma rýchlo rastie a vyvíja nové features. Poznáme ich prostredie...

FullStack PHP Developer

Chcel by si dlhodobú spoluprácu, dobré pracovné podmienky, seriózny prístup? Hľadáme FullStack PHP Developera pre spoločnosť, ktorá sa zaoberá...

Python Medior/Senior Developer

Si Python developer, chceš sa naučiť Go, ideálne remote? Spoločnosť, ktorá sa zaoberá pokročilou analýzou dát a automatizáciou marketingu...

Data Scientist / REMOTE

Pre mladý startup vyvíjajúci softvér, ktorý prispieva hráčom k lepšiemu zážitku z hrania, hľadáme Data Scientist. Založili ho dvaja...

IT Aplikačný Architekt

Chceš stabilné zamestnanie, vychutnávať si skvelý kolektív a mať priestor na realizáciu svojich riešení? Pre spoločnosť pôsobiacu v energetike...

Zapojte sa do prvého NCC hackathonu!

Nenechajte si ujsť prvý hackathon Národného kompetenčného centra pre HPC a využite jedinečnú príležitosť podieľať sa na vývoji aplikácie,...

Zviditeľnite sa v médiách ako odborník v oblasti IT

Zaujímajú vás novinky zo sveta IT a chýbajú vám v médiách? Pomôžte budovať povedomie v oblasti informačných technológií a...

Súťaž Scracth Match 2021 zaznamenala nárast, dominovali jej edukatívne hry

Porota celoslovenskej súťaže Scratch Match 2021 už po piaty raz ocenila nádejné programátorky vo veku 8-15 okov. Medzi ocenenými...

Čítaj ďalej:

Dobrý článok? Chceš dostávať ďalšie?

Už viac ako 6 200 ITečkárov dostáva správy e-mailom. Nemusíš sa báť, nie každé ráno. Len občasne.

Súhlasím so spracovaním mojich osobných údajov. ( Viac informácií. )

Tvoj email neposkytneme 3tím stranám. Posielame naňho len informácie z robime.it. Kedykoľvek sa môžeš odhlásiť.