Seriál: Jak se tvoří čistý kód aneb jak se vyvarovat paskvilům – 8. kapitola: Proč je princip Open Closed důležitý pro agilní techniky

V návaznosti na předešlé články si ukážeme na konkrétním příkladu vztah mezi principem Open Closed a agilními technikami, například SCRUM.

Představme si tuto situaci: Analytik-programátor vytvořil a naprogramoval nějakou metodu objektu, pojmenujme tuto metodu objektu jako A. O něco později musí vyvinout druhou metodu někde jinde, pojmenujme ji jako B. Nechť při studiu nové metody programátor zjistí, že tyto dvě metody A a B jsou téměř identické, liší se pouze ve dvou částech (tj. v odstavcích kódu), označme tyto části jako PartA1 a PartA2 v první metodě A a ve druhé metodě B jako PartB1, PartB2, tj. graficky znázorněno kód vypadá nějak takto:

   

Nabízejí se tři možnosti, jak řešit tuto situaci:

Metoda CTRL C + CTRL V

Programátor propadne nadšení: „Mám tento kód metody B již téměř celý naprogramován, ušetřím si práci, stačí změnit malé části!” … a použije operaci CTRL C a CTRL V, tj. kód zkopíruje a v červených částech jej změní editací.

Tento postup je samozřejmě vřele nedoporučován. Představme si, že tento proces kopírování a vkládání povolíme a navíc není vůbec nikde zdokumentován. Znamená to, že se tento kód opakuje několikrát a my nevíme kolikrát a kde. Úpravy kódu se stávají detektivkou. Hlavním tragickým důsledkem tedy není ani tak nutné opakování změn resp. oprav díky opakujícímu se kódu, ale největším problémem je totální ztráta transparence, protože vždy musíme nejprve najít „bůhvíkde“ všechny body, kterých se změna týká.

Metoda switch

Nabízí se druhé lepší řešení (ale ne to nejlepší), populární u “klasických” programátorů vyznávajících „zastaralé“ strukturované programování. Sloučí se obě metody A a B do jedné metody (nazvěme ji jako operX), následně se zavede nějaká proměnná (např. řetězec nebo celé číslo) a do míst odlišností se nasadí klasický přepínač větvení kódu podle této proměnné.

V pseudokódu zapsáno např. nějak takto:

public void operX(k)
 {
 …
 …
 switch (k)
  {
  case 1: PartA1;
  case 2: PartB1;
  }
 …
 …
 switch (k)
  {
  case 1: PartA2;
  case 2: PartB2;
  }
 …
 …
}

Toto řešení je mnohem lepší než předchozí s kopírováním kódu, ale má stále velké nevýhody. Mechanismus přepínání není totiž příliš transparentní a navíc je neflexibilní. V kódu se vyskytuje několik přepínačů a ty se také musejí vyhledávat. Nedejme se zde zmást přehledností situace na obrázku, u nezdokumentovaného zdrojového kódu plného switchů je situace diametrálně odlišná.

Navíc platí jednoduchá logika, že pokud se něco může opakovat jednou, může se to opakovat samozřejmě několikrát. Takže když přibylo B, tak se časem objeví nové varianty C, D aj., což vede k neustálým zásahům do existujícího kódu. Toto řešení jde přesně proti principu Open Closed, protože takovýto zdrojový kód je neustále otevřen.

Využití polymorfismu (s nasazením Generalizace v UML alias Inheritance v OOP)

Třetí nejlepší řešení využívá polymorfního chování v OOP. Představme si, že naprogramujeme dva různé objekty, nazvěme je A a B, oba se stejným interfacem, ale s různým chováním za ním (tj. implementací). Potom stačí vyměnit jeden objekt za druhý a při nezměněném původním kódu dostaneme požadované změny v chování pro části kódů A i B.

Jako příklad si uveďme následující jednoduchou konstrukci (jedná se o aplikaci vzoru TEMPLATE, viz např. školení https://goo.gl/hqcecS   ).

Nechť máme za úkol v programu simulovat chování Zvířete, např. konkrétně simulace pro Kočku nebo Psa v tomto scénáři:

Útočník útočí…
Zvíře vydá zvuk.
Útočník nereaguje…
Zvíře se brání.

Jako řešení třetího postupu s polymorfním chováním zavedeme abstraktní třídu (resp. interface) Zvíře a dvě třídy jako potomky, třídu Kočka a Pes. Vrchní třída (resp. interface) zavádí dvě operace, nazvěme je například VydejZvuk a Braňse. Implementace pro třídy Kočka a Pes je zřejmá, kočka mňouká, pes štěká (jen pozor, jsme v programování, takže např. v pseudokódu bude implementace metody VydejZvuk pro kočku MsgBox.Show(“Mňau!“) a podobně pro psa MsgBox.Show(“Haf!“).). Implementace pro Braňse je také zřejmá, kočka škrábe, pes kouše (tj. analogicky MsgBox.Show(„Já škrábu!“) a podobně pro psa MsgBox.Show(“Já koušu a trhám!“)).

 

Simulace pak proběhne tak, že „bokem“ tohoto kódu scénáře útoku vytvoříme nový objekt buď Kočka nebo Pes, dosadíme do proměnné Zvíře v tomto kódu útoku a spustíme jej.

Jakou má návaznost tento příklad na předešlý výklad? Představme si, že programátor dostal za úkol naprogramovat chování Kočky ve scénáři útoku. Naprogramuje tedy scénář pro Kočku. Následně dostane požadavek naprogramovat „totéž“, ale pro Psa. A v té chvíli nastala zmíněná situace v úvodu a nabízí se mu tři možná řešení: kopírování, switch a polymorfismus.

Zavrhneme první variantu „copy paste“. Řešení pomocí polymorfismu je bezpochyby lepší než se switchem. Jednak odpovídá logice chování tj. zdravému selskému rozumu: Ve switchi se ptáme „Co jsi zač? Kočka nebo Pes?“ Podle toho běžíme pro funkci mňoukání nebo štěkání. Platí tedy vláštní logika „pokud jsi kočka, tak mňoukáš, pokud jsi pes, tak štěkáš“. V druhém případě se kočka resp. pes narodí již se svým typovým implementovaným uměním a platí tedy logicky správnější úvaha: „protože jsi kočka, tak mňoukáš“ (nikoliv pokud) a podobně pro psa se štěkáním…

Navíc, a to je pro princip Open Closed důležité, v daném kódu scénáře útoku se už daného objektu neptáme, co je zač, zda Kočka nebo Pes. Prostě jako zde dosazený objekt patříš do rodiny Zvířat (podporuješ tento interface), a tedy ty už víš, co dělat. Díky tomu je splněn princip Open Closed: Na jedné straně je kód uzavřen, protože kód simulace útočníka na zvíře se nemění, dokonce v C# nebo JAVA může být i zkompilován, na druhé straně je otevřen pro přidávání dalších subtypů třídy Zvíře.

Konec článku



Categories

About the Author:

správce a majitel Serveru objektových technologíí http://www.objects.cz

0 Comments
0 Pings & Trackbacks

Leave a Reply