Nedávno jsem zažil jednu klasickou programátorskou situaci. Potřeboval jsem ke své analytické práci naprogramovat drobnou utilitku v C#. Základní zadání bylo jednoduché: Vytvořit Addin do Wordu, který by umožňoval psát velmi efektivně analytické dokumenty za přímé podpory CASE nástroje Enterprise Architect metodou drag and drop.
Věděl jsem přesně, co potřebuji, ale vzhledem k nedostatku času jsem kód nechal vytvořit jednomu svému kolegovi, tehdy studentovi a začínajícímu programátorovi. Odevzdal jsem mu tedy zadání, po určité době jsem dostal vcelku dobře chodící kód. Když se v utilitce objevily první chyby, nepovažoval jsem to ještě za něco katastrofického, každý přece dělá chyby a žádný kód není dokonalý. Tak jsem nechal chyby odstranit a kód byl brzy vrácen od autora jako opravený… ale objevily se další bugy a po následných opravách další a další…
Byla to moje chyba, že jsem neprovedl analýzu odevzdaného kódu dříve, nejlépe hned. Myslel jsem, že když se jedná o „malou utilitku“, tak kód asi nebude třeba kontrolovat. S určitým podezřením jsem tedy o něco později otevřel kód a hned mi bylo jasné, proč je program až tak chybový a hlavně, proč je tak nestabilní, tj. proč se s každým zásahem objevují další chyby.
Při tvorbě softwaru existují dvě krajní označení pro jeho kvalitu:
Na jedné straně hovoříme o tzv. elegantním kódu, což je dost zajímavé označení, které se používá například v módě. Takový kód by se dal nazvat také jako „líbivý kód“, samozřejmě nikoliv v hanlivém slova smyslu, prostě jako kód, který se líbí.
Druhý protipól kvality kódu SW má různá označení, mnohdy dokonce i lehce vulgární, většinou hovoříme o tzv. softwarovém paskvilu. V tomto případě kód (i když navenek zdánlivě chodí tak, jak má) obsahuje nesprávné konstrukce, je potenciálně chybový, nestabilní, nepřehledný, těžce udržovatelný a neflexibilní.
Při pohledu na dodaný kód bylo hned zřejmé, že tento kód patří spíše do té druhé skupiny než do té první…
Otázka:
U některých SW se pozná paskvil na první pohled (pozor, ne však u všech!).
Podle čeho se takovýto „na první pohled očividný paskvil“ pozná?
Odpověď:
Nejprve si musíme pro potřebu dalšího výkladu definovat zvláštní pojem, který úzce souvisí s objektovým paradigmatem (ten si vysvětlíme v kontextu čistoty kódu v tomto seriálu později). Budeme často hovořit o dané vrstvě kódu třídy, metody resp. funkce. V kontextu výkladu tím budeme rozumět tu část kódu třídy, metody, příp. funkce, kterou jako programátoři vidíme při psaní kódu v editoru přímo před sebou a kterou můžete v danou chvíli editací měnit. Nevidíme tedy této vrstvě ten kód, který je schován za další volání metod objektů resp. funkcí anebo přes dědičnost např. rezervovaným slovem base v C# resp. super v Javě, a také nevidíme ten vnitřní kód, který náš kód volá a používá.
První možný postup pro určení míry čistoty kódu je následující:
Projděte si vrstvy kódu tříd, metod resp. funkcí (viz předešlá definice). V duchu si vyjmenujte nad těmito částmi kódu: Patří to opravdu sem? Nepatří to jinam? Nejjednodušeji si na tuto otázku odpovíte tak, že si položíte navíc ještě jednu pomocnou ekvivalentní otázku: Když by byla v této části kódu chyba, budu ji automaticky a intuitivně hledat v této vrstvě kódu anebo někde jinde? Jinak řečeno, je to logické ji tady hledat anebo si to musím pamatovat, že je to zrovna tady?
Je zřejmé, že pokud platí to druhé, není to dobře. Jednak s narůstajícím kódem paměť slábne a za druhé, kolega, který převezme náš kód, tak ten na nás nebude vzpomínat v dobrém a označení pro náš kód jako paskvil pro něj bude málo vulgární, protože bude pracně jak v detektivce nacházet něco někde jinde, než kde by to čekal.
Jako příklad bych uvedl část z dané utility, ve které byla zavedena třída Addin. Tato třída obsluhuje připojení zásuvného modulu typu Addin do Wordu. Kód této třídy byl autorem rozšířen o další skupinu statických metod. Jednou z těchto nových metod třídy Addin byla metoda s názvem SetUpLogger, která iniciovala Log knihovnu. V této metodě se nastavovalo vše potřebné (a není toho málo), aby knihovna Log následně fungovala.
Takže první otázka zněla:
Patří tato metoda, která obsahuje kód inicializace Log knihovny, sem, do vrstvy třídy Addin? Budu ji zde automaticky hledat?
Odpověď:
Samozřejmě, že ne. Třída Addin slouží k připojení addinu do Wordu a nikoliv k inicializaci Log knihovny.
Samozřejmě nedejme se zmást tím, že některá z metod nakódovaných ve vrstvě třídy Addin (např. metoda OnConnection volaná na událost připojení addinu) bude tuto funkčnost volat, to nás nyní nezajímá. My nyní zkoumáme tu vrstvu kódu, kde se naprogramovala inicializace.
Závěr: Kód obsluhující inicializaci Log knihovny bude sice z kódu Addin třídy volán, ale nebude v ní naprogramován, musí být naprogramován jinde.
Je zřejmé, že odpověď na otázku „patří to sem nebo ne“ nemusí znít vždy tak explicitně jasně jako zde v tomto příkladu a dokonce dva tvůrci systému by se mohli hádat, zda to patří nebo nepatří právě sem, do této třídy, tj. zda je daná implementace v kompetenci tohoto prvku nebo ne. Zmíním se proto ještě o druhém, řekl bych důležitějším signálu svědčícím o paskvilnosti, který je také velice pěkně vidět na tomto příkladu.
Poznámka: Tento druhý velmi silný signál paskvilnosti je natolik průkazný a důležitý, že jej probereme ještě v dalších kapitolách důkladněji. Mimochodem u něj se také projeví výhody těch postupů, kdy si model SW namalujeme pomocí jazyka UML v diagramu tříd a vidíme tak zřetelně vazby a závislosti mezi třídami (o modelování a čistém návrhu SW viz autorův web Server objektových technologií https://www.objects.cz )
Při posuzování daných částí kódu si kromě předešlé otázky položme ještě druhou otázku: Představme si, že by někdo jiný (tj. jiný programátor) potřeboval použít tuto námi naprogramovanou funkcionalitu dané vrstvy kódu, kterou zkoumáme. Nebude s tím mít problém? Nebude muset udělat zbytečné a tedy z pohledu chyb a transparence potencionálně nebezpečné kroky? Nedostane něco, co nechce, a mohl mít z tohoto důvodu problém?
Poznámka: Při nákupu některého zboží nebo služeb můžete dostat tzv. bonus, tj. něco, co je pro vás jako plus (například při koupi za X korun dostanete jako bonus slevu na čerpání paliva apod.). Existuje také záporný bonus, kterému se říká malus, například se vám zdraží pojistné automobilu, pokud často bouráte apod. Otázka by tedy zněla: Neobsahuje váš kód při použití jiným programátorem malus, tj. záporný bonus?
Takže představme si, že někdo jiný vyvíjí úplně jiný projekt, například projekt s výsledným spustitelným EXE souborem a v jeho projektu nebude použit žádný Word natož jeho addin. Tomuto tvůrci kódu se bude líbit náš naprogramovaný kód s knihovnou Log v našem projektu a bude jej chtít použít včetně dané funkcionality nakódované v metodě SetUpLogger (u nás umístěné ve vrstvě kódu třídy Addin). Když bude chtít použít náš kód, dostane malus alias záporný bonus anebo nikoliv?
A problém je na první pohled zřejmý, protože odpověď zní: Ano, dostane malus a ne zrovna malý. Daný programátor by si musel přilinkovat celou třídu Addin z našeho projektu a to (proboha) se vším všudy, co naše třída Addin dále potřebuje pro svou funkčnost (tj. odpovídající Word knihovny, NetOffce knihovny aj.) … a bůhví, zda by mu nakonec to vše s naší třídou Addin vůbec chodilo…
Pomocí tohoto druhého postupu při zkoumání malusu zřetelně dostáváme stejný závěr: Tento kód inicializace Logu sem nepatří, musí být jinde, aby jiný programátor neměl problém s přebytečným balastem kódu třídy Addin.
Tento druhý signál je velmi silný, je již neoddiskutovatelný (protože zřetelně vidíme záporné důsledky) a úzce souvisí s pojmy jako „čistý re-use“, zašpinění vrstvy, objektové paradigma, modely v UML aj., což je námětem dalších kapitol tohoto seriálu.
Pokračování následuje…
Napsat komentář