Seriál: Jak se tvoří čistý kód aneb jak se vyvarovat paskvilům – 7. kapitola: Znáte zkratky DRY, ADP, OCP, ISP a PINI?

Při studiu problematiky tvorby tzv. „čistého kódu“ můžete narazit na tzv. principy. Jedná se vlastně o doporučení týkající se návrhu čistého kódu, která by se měla dodržovat. Pro lepší zapamatování a rychlejší komunikaci mezi vývojáři se tyto principy označují zkratkami. Mezi nejznámější (kromě notoricky známé zkratky SOLID) patří také v nadpisu zmíněné zkratky DRY, ADP, OCP, ISP a INI. Co tyto zkratky znamenají?

Poznámky:

  • Některé principy se v různých zkratkách vyskytují opakovaně, tj. principy se překrývají. Například zde zmíněný princip OCP (Open Closed Princip) se vyskytuje jako jeden z principů také u zkratky SOLID. 
  • Tvorbě a návrhu čistého kódu je věnováno 3denní školení Čistý kód v OOP & Design Patterns, nabízené jednak jako in-house pro firmy, tak i jako veřejné prezenční pro jednotlivce, přednáší autor tohoto článku.

Princip DRY – Don’t Repeat Yourself

Tato zásada znamená, že každý prvek musí mít v rámci systému jedinou, jednoznačnou a spolehlivou reprezentaci (tj. jednoznačnou identitu), například by se neměly opakovat datové struktury, neměl by se opakovat kód apod. V podstatě to znamená dodržovat princip opětovné použitelnosti v maximální možné míře.

Tento princip by se měl dodržovat nejen vůči jednomu vývojáři (jak by mohlo z názvu mylně vyplývat), ale měl by se dodržovat v rámci práce v týmu a nakonec i v celé ve firmě (což není vůbec jednoduché). Například týmová práce spolu s tímto principem vede k nutnosti zavádění tzv. analytického modelování (viz kniha Analytické modelování volně ke stažení zde).

Tento princip doplněný o požadavek „čistoty opětovné použitelnosti“ (tj. požadavek na použití jednoho prvku druhým prvkem bez žádného balastu navíc) považuji za axiomatický z hlediska všech principů, tj. lze z něj ostatní zásady logicky odvodit (podobně jako v matematice).

Je zřejmé, že na logické úrovni aplikace (tj. v analytickém modelování) musí být princip DRY splněn automaticky, jinak bychom byli zralí pro psychiatra. Hovoří-li se totiž v logice aplikace „o tomto prvku“, tak máme na mysli „tento jeden prvek“ (a ne dva), a naopak, jsou-li v logice identifikovány „dva prvky“, máme na mysli dva prvky a ne jeden. Na úrovni technologického návrhu se však tento jeden „logický prvek“ může opakovat v realizaci několika „technologickými prvky“. Typickým příkladem je „de-normalizace“ databáze rozpuštěním jedné tabulky do jiných tabulek.

Hlavním důsledkem nedodržování tohoto principu je totální ztráta transparence systému, protože prvky ztrácejí v kódu svou jednoznačnou identitu, tj. není logicky jasné, kde všude se nacházejí, změnové řízení a opravy chyb se stávají detektivkou, mnohdy až po úplnou neudržitelnost systému.

ADP – Acyclic Dependency Principle

Tato zásada znamená, že struktura závislostí mezi balíčky kódu musí být zavedena jako orientovaný acyklický graf, tj. nesmí existovat kruh v závislosti struktur mezi balíčky kódu.

Tento princip by se dal nazvat jako „dodržujte čistotu z pohledu nižší a vyšší vrstvy kódu“ a vyplývá přímo z čisté opětovné použitelnosti (re-use bez balastu) aplikované na implementační části kódu (moduly apod.). V uvedení knize najdete popis postupu stříhání systému na vyšší a nižší vrstvy (viz kapitola Modulární nůžky).

Musím zde v souvislosti s tímto principem ještě upozornit na nutnost nasazení vzoru OBSERVER z GOF (alias LISTENER v JAVĚ resp. události v C# resp. tzv. callback v C aj.) při snaze o volání „proti srsti“, tj. proti směru použití (z nižší vrstvy do vyšší vrstvy).

Nedodržení tohoto principu vede k zavedení tzv. molochálnímu systému, kde vše souvisí se vším. Pro takto navržený systém je charakteristikou tzv., „zašmodrchaný špagetový (makaron) kód“. Konečným důsledkem je, že každá byť malá změna znamená zásah do velké části systému.

OCP – Open Closed Principle

Tato zásada znamená, že kód by měl být napsán tak, aby přidání funkcionality k již uzavřenému kódu vedlo pokud možno pouze k přidání kódu a nikoliv k jeho změně. Symbolicky a s nadsázkou řečeno: „jak jen to jde, tak aditivnost funkcionality musí vést k aditivnosti kódu a ne k update stávajícího kódu“.  Původní kód je tedy uzavřen (dokonce v některém prostředí jako je JAVA nebo .NET je i zkompilován), ale současně je otevřen pro přidávání nových funkcionalit. Znamená to, že návrh by měl být implementován takovým způsobem, který umožňuje přidání nových funkcí (jako jsou nové třídy) a současně by měl zachovat co nejvíce stávajícího kódu bez změn.

Efektu OCP se dosahuje v klasickém objektovém prostředí pomocí polymorfního chování s využitím dědičnosti. Použití tohoto principu vede následně k žádanému „silně flexibilnímu kódu“. Valná většina vzorů GOF Design Patterns řeší právě problémy spojené s „ne-flexibilitou“, tj. GOF nabízí řešení, jak získat maximálně flexibilní kód (OCP).

Flexibilita obecně znamená (nejenom v návrhu kódu): „Jak moc mne bude bolet, když se něco změní.“  Z tohoto pohledu nedodržení zásady OCP vede nakonec k tomu, že se kód s každou další aditivní změnou funkcionalit musí překopat, což bolí hodně, a to nejenom programátora, ale i testera.

Nedodržení tohoto principu spolu s nedodržením ADP (tj. nedodržení zásady „zavádějte vrstvy systému jako acyklický graf závislostí“) je spolehlivým zabijákem agilních technik, například SCRUMu. Pokud se OCP nedodržuje, tak poté žádná práce není nikdy hotova a je neustále rozdělána, s každou aditivní změnou (další task nebo sprint) se neustále překopává hotový kód, i když logicky k tomu důvod vlastně není. Jenže díky „nečistému“ kódu se musí kód neustále znovu otevírat a překopat (například díky větvení pomocí switch apod.).

ISP – Interface Segregation Principle

Tato zásada znamená, že klient by neměl implementovat metody rozhraní, které nepotřebují a které tedy nepoužívají. V důsledku se jedná o doporučení, že namísto jednoho „širokého“ rozhraní je lepší zavést více malých rozhraní rozdělených na základě skupin metod, z nichž každá slouží určité skupině klientů.

Tento princip je logickým vyústěním zásady „re-use bez balastu“, tj. pro určitou skupinu klientů nebudeme nabízet velký interface s pro někoho zbytečnými metodami. V důsledku to znamená, že by neměla vzniknout taková situace, aby potomek byl nucen implementovat metodu interfacu, kterou nepotřebuje a tedy nepoužívá.

Jako příklad je uveden klasický případ stroje, který by měl umět (tj. implementovat) několikero metod (tisk, faxování, skenování atd.). Pokud bychom tyto nezávislé funkčnosti umístili do jednoho interfacu, potom by tyto funkčnosti nutně musely žít vždy spolu a vzniká tímto velký moloch kódu (opět nečistota balastu). Pokud někdo chce použít pouze určitou funkčnost, potom se nutně (a tedy zbytečně) musí implementovat všechny funkčnosti.

Tato zásada doporučuje rozdělit velký interface na menší s každou nezávislou funkčností.

ISP rozděluje rozhraní, která jsou velmi rozsáhlá do menších a konkrétnějších, takže klienti budou muset vědět pouze o metodách, které jsou pro ně zajímavé. Taková zkrácená rozhraní jsou také nazývána „role interface“. ISP je určen k tomu, aby udržoval kód od sebe oddělený a tím se v případě potřeby lépe přeskupoval, skládal, měnil apod.

Je třeba ale upozornit na určité úskalí tohoto vzoru: Přílišné nadužití tohoto vzoru nakonec může vést k velmi velkému počtu interfaců implementovaných do jedné třídy a také k interfacům „na jedno použití“ (majících pouze jednu implementaci). Z toho důvodu bývá plnění této zásady mnohdy diskutabilní. Osobně raději vidím řešení ve formě dobrého logického poskládání vyšších objektů z daných prvků, které představují vrcholy stromů dědičnosti (viz další princip PINI níže), tj. v našem příkladu by se uvedený stroj skládal z prvků pro tisk, fax, sken atd. a tyto funkčnosti by nabízel nikoliv přímo implementaci těchto interfaců, ale pomocí těchto součástek, ze kterých by byl poskládán a tyto součástky by byly reprezentovány vloženým interfacem anebo vložením abstraktní třídy.

PINI – Program to an Interface Principle

Tato zásada znamená, že bychom měli preferovat používání interfacu resp. abstraktní třídu namísto konkrétní třídy.

Ohledně tohoto principu vznikají časté diskuse. Je třeba si uvědomit, že následně, když ukončíme kódování a program v OOP už běží, potom se bude vykonávat pomocí instancí narozených z konkrétních tříd. Znamená to, že nakonec někde někdo někdy ke konkrétním třídám a konstrukci z nich přistoupit musí, aby se instance zrodily.

Zásada PINI se dá vyslovit jinak a pragmatičtěji: Vyvarujte se chyby tzv. „předčasná konkretizace“. Pokud přistupujete k nějakému prvku, který chcete použít a předčasně jej konkretizujete, stává se váš kód závislým na dané zvolené konkretizaci, což může být (a většinou také je) nežádoucí. Pokud přistoupíte k danému prvku nikoliv konkrétně (tj. nikoliv přes konkrétní třídu), ale jako k interfacu resp. abstraktní třídě, potom se váš kód stává na další konkretizaci tohoto prvku nezávislým. Poté může někdo (tj. vy anebo někdo jiný) mimo tento váš kód zvolit různé konkretizace a dosazovat dle libovůle konkrétní prvky do pozice vašeho abstraktně zavedeného prvku.

Tato problematika úzce souvisí s kapitolou Obecný BRIDGE (Přemostění) v knize Analytické modelování v praxi a také s technologií zvanou Inversion of Control.

Nedodržování tohoto principu (podotknu – tam, kde je třeba jej dodržet) vede k netransparentnímu, tvrdému a neflexibilnímu kódu (princip OCP není dodržen), mnohdy s nutnými redundancemi a „kříženci“ ve třídách s následnou nepříjemnou kombinatorikou při rozmnožení potomků konkrétních tříd.

Závěr článku


Uveřejněno

v

,

od

Značky:

Komentáře

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *