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ť.

Junior React Developer

Chceš pracovať na dlhodobom, technologicky inovatívnom projekte a mať za to dobre zaplatené? Pre spoločnosť, ktorá používa matematické metódy...

Senior Frontend Developer

Množstvo práce sa zvýšilo, ale plat nie? Pre mladú slovenskú online firmu, ktorá má reálnu ambíciu, aby jej hlavný...

Medior Backend Developer

Chceš sa podieľať na rozvoji digitálnych služieb určených pre zdravotníctvo? Poď robiť veci, ktoré dávajú zmysel! Pátrame po Medior...

Medior/Senior FullStack Developer

Chceš pracovať na dlhodobom, technologicky inovatívnom projekte a mať za to dobre zaplatené? Pre spoločnosť, ktorá používa matematické metódy...

Active Directory Admin

Páčil by sa ti projekt v Prahe so skvelým finančným ratom? Pre globálnu technologickú a obchodnú spoločnosť hľadáme Active...

Engineering Expert Public Cloud Network Engineering

Homeoffice, dobrá odmena a projekt, ktorý sa Ti bude páčiť? Pre globálnu technologickú a obchodnú spoločnosť hľadáme Engineering Expert...

DevOps Enginner / Online Bezpečnosť

Pre IT spoločnosť, ktorá sa venuje online bezpečnosti hľadáme do tímu DevOps Medior Engineer. Spoločnosť má 20-členný tím s...

GIRL ́S DAY 2020: Už po siedmy raz môžu stredoškoláčky nahliadnuť do IT firiem po celom Slovensku.

Občianske združenie Aj Ty v IT už siedmy rok prepája IT firmy a organizácie so stredoškoláčkami po...

DIGITALEUROPE: Na digitalizáciu pôjde z plánu obnovy až pätina prostriedkov. Ako zareaguje Slovensko?

Výzva v podobe zníženej globálnej konkurencieschopnosti, ako aj kríza spojená s pandémiou postavili Európu...

OpenSlava 2020 tentokrát online!

OpenSlava 2020 je 8. ročník úspešnej konferencie pre IT komunitu na Slovensku...

Čí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ť.