Analytikova zkušenost s Ariadninou nití
Svět objektově orientovaného programování je plný různých doporučení, striktních zákazů, nemilosrdně vyžadovaných pravidel i dobře míněných zápisků s petrifikovanými zkušenostmi analytiků-doyenů.
Studiem dokumentace k OOP by mohl nyní jeden člověk strávit celý život a zdá se být kladem, že začínající analytik není odkázán jen na svoji invenci a nemusí stále dokola s Archimédovým výkřikem objevovat dávno známé znalosti, ale zvládne učební křivku na své cestě za téměř dokonalým návrhem (na tomto světě nemůže být nic dokonalé :)) rychleji s využitím přehršle formálních poznatků svých předchůdců. Skoro růžové vyhlídky.:)
Nakonec vždy zjistíme, že mechanická aplikace sebelepších pravidel z nás dobré analytiky neudělá a že ke každému návrhu systému musíme přistupovat s respektem, protože půjde o originál reagující na vždy jiné požadavky zákazníka. Samotný proces návrhu připomíná průchod bludištěm, ve kterém jsou lákavě svůdnou Ariadninou nití zkušenosti jiných i naše vlastní, ale nikdy si nemůžeme být jisti, že postmoderní feministka Ariadna netouží po naší smrti u Minotaura, kterým je formálně dokonalý systém zcela se míjející s reálnými a zákazníkem vyslovenými požadavky.
Po trochu delším úvodu se dostávám k samotnému tématu příspěvku. Jednou z nejdůležitějších a nejvíce akcentovaných charakteristik dobrého návrhu je striktní dodržení principu izolace změn. Většinou se tímo principem míní omezení dopadu změn při rozvoji systému na určitou komponentu nebo třídu bez zásahu do návrhu a kódu ostatních a se změnou nesouvisejících komponent aplikace. Systémy, u nichž každá změna spustí dominový efekt vynucených drobných a těžko dohledatelných úprav, dokáží zákazníkům i dodavatelům připravit těžké noci plné nočních můr o neúspěšném businessu.
Izolaci změn ale nemusíme chápat jen takto omezeně jako prostředek k snadnému rozvoji aplikace. Mnohdy je nutné vyrovnat se ihned v první verzi aplikace s rozdíly při práci s třídami na různé úrovní hierarchie dědičnosti vyplývajícími přímo z aplikace uznávaných objektově orientovaných principů a zamezit šíření zhoubného požáru v podobě duplicitního kontrolního kódu.
Představme si návrh aplikace, ve které existují tři typy produktů. Běžný produkt určený k prodeji, reklamní produkt a konkureční produkt, jímž se míni produkt prodávaný konkurenční firmou. U běžného produktu se evidují tři různé ceny (běžná prodejní cena, cena vzorku a cena, kterou má produkt, je-li předán jako kompenzace obchodníkovi za splněný objem prodeje). U reklamního produktu a konkurenčního produktu se eviduje jen běžná prodejní cena.V diagramu vytvoříme abstraktní třídu ProductBase (ze zde popsaného zadání její nutnost neplyne, ale berte ji prosím jako aplikací vynucený fakt. :) ), která bude obsahovat atribut běžná cena. Potomci ProductBase "Reklamní produkt" a “Konkurenční Produkt" žádný atribut nepřidávají. Dvě další ceny (vzorek, kompenzace) přidá potomek "Běžný produkt". Z hlediska OOP je vše správně, předek ProductBase obsahuje jen společné atributy. Ostatní části systému ale například při zakládání objednávky na produkt budou nuceny při potřebě přistupovat k cenám pro vzorek a rekompenzaci přetypovávat na potomka "Běžný produkt". Nepříjemný způsob, jak do systému zavést závislost na konkrétní třídě a ne na rozhraní a porušit tak další důležitý OOP princip. Ariadna nás poprvé zradila, protože její niť se odvíjí dvěma zcela rovnocennými směry. Pro jaký směr se rozhodnout? U tohoto systému jsem zvolil následující řešení. Produkty měly být řazeny do jednoho či více katalogů, a proto vznikla třída katalogová položka. Položky objednávky si ukazují na položku v katalogu, ne přímo na produkt. Třída katalogová položka má ve svém rozhraní všechny tři ceny. To znamená, že jako jediná v celém systému zná konkrétního potomka ProductBase, se kterým pracuje, a vrací při požadavku na konkrétní cenu buď nulu, pokud u objektu daný atribut neexistuje, nebo získá a vrátí patřičnou cenu delegací na atribut třídy "Běžný produkt". Třída objednávka neobsahuje žádný specifický kód rozlišující mezi typy produktů. Popravdě řečeno, v reálném systému pracují s potomky ještě business pravidla pro objednávky na různé typy produktů, ale ta jsou uložena mimo objekt objednávky ve speciálních "strategiích". Pravidla ověřují platnost a homogenitu položek objednávky. Ale to je opravdu vše - zbytek aplikace pracuje s produkty bez přetypovávání. Milá nerozhodná Ariadno, tentokrát jsem se z tvého ambivalentního vedení vysmekl.
Podobný případ. Když píšete aplikační logiku, která má být společná pro .Net Framework i Compact .Net Framework, brzy zjistíte, že musíte v kódu zohlednit absentující funkcionalitu v Compact .Net Frameworku. Jestliže aplikaci prošpikujete neustálými testy na platformu, nejste vývojáři, ale nejhorší programátorská spodina, pro níž je Minotaurus vysvobozením a panenské Ariadny byste se neměli ani dotknout.:) Jaký je správná postup? Vezměme si jako vzorový příklad řešení nepříjemného poznatku, že v Compact .Net Frameworku překvapivě chybí známá třída ConfigurationSettings.AppSettings pro načtení konfiguračních parametrů. Abyste mohli načíst konfigurační parametry v rámci celé aplikace jednotným způsobem, vytvoříte rozhraní s metodou GetConfigValue. Poté vytvoříte dvě implementace tohoto rozhraní . Jednu pro .Net Framework s využitím třídy ConfigurationSettings.AppSettings a jednu pro Compact .Net Framework, v níž načítání parametrů z XML souboru napíšete sami. Po spuštění aplikace je zjištěna verze operačního systému, a do proměnné, jejímž typem je rozhraní s metodou GetConfigValue, je dosazena platformně závislá implementační třída. Celá aplikace používá jen metodu GetConfigValue a žádné další testy neprovádí. Ač to tak ihned nevypadá, de facto se jedná o návrhový vzor Bridge, v němž konkrétními implementory jsou platformně závislé třídy a odstiňující abstrakcí pro klienty je rozhraní s metodou GetConfigValue. Také zde je dilema - většina OO příruček hlásá, že by v systému neměly vznikat třídy s jednou odpovědností (funktoidy). Ale zde popsané "funktoidy" jsou užitečné a pro vitální funkce sytému nenahraditelné.
Ariadnina niť se u každého kvalitního návrhu systému nenávratně přetrhne a to je většinou spolehlivý příznak, že jsme nalezli vlastní mapu k bludišti a nepotřebujeme zavádějící berličky třetích stran. Příznak toho, že jsme odborně dospěli a neuctíváme již nekriticky OO autority a jejich subjektivní a nutně počtem i typem navrhovaných aplikací podmíněně platné sentence. Včetně našich dosavadních sentencí .:)
Sunday, 16 May 2004 14:10:00 (Central Europe Standard Time, UTC+01:00)
Analytické drobky