|
klasický příklad na posun meta s ukázkami chybných úvah
autor: RNDr. Ilja Kraval, březen 2011
Object Consulting s.r.o.
Server objektových technologií
Úvod
V některých předešlých článcích již padly zmínky o tzv. posunu META v návrhu aplikace. Tento článek pojednává o jednom velmi vypovídajícím příkladu na posun META.
Co je to posun META
Základní problém posunu META souvisí se vzorem Dichotomie třída - instance, který patří k „nejzákladnějším“ vzorům celého návrhu IS. Podle tohoto vzoru je každý informační systém rozdělen na dvě disjunktní části (proto tzv. dichotomie) a to na prostor tříd a prostor instancí. Libovolný informační systém následně funguje tak, že se „naprogramují třídy“ a aplikace pak „žije“ dynamicky v instancích, které se ze tříd „rodí“. Vztah od tříd směrem k instancím je tedy vztahem „zrodu“ alias „instanciováním“, opačný vztah od instancí ke třídám se nazývá vztahem META.
Poznámka: Vztah META ještě výše od tříd k tzv. metatřídám a dál není teoreticky uzavřen, tj. teoreticky se dá zavést meta-meta resp. meta-meta-meta atd. Prakticky a účelně se v programování používá pouze první úroveň, tj. vztah od tříd k „metatřídám“. Například k tomuto účelu slouží v C# a Javě knihovna zvaná Reflection.
Jedním z hlavních úkolů analytika je dobře navrhnout analytické třídy, ze kterých budou vznikat evidované analytické „žijící“ instance. Díky tomu se analytik může setkat s problémem volby pozice „co je třída a co instance“.
Tento problém se nazývá buď „chtěný“ nebo „nechtěný“ posun META. Pokud není takový posun META žádoucí anebo je učiněn chybně, jedná se o fatální vývojářskou chybu, celý systém je dále vývojově neudržitelný a celý projekt většinou skončí následným kolapsem.
Protože se z hlediska návrhu jedná o kritický problém, je mu v našich školeních věnována patřičná pozornost, a to jak u internetových, tak u pobytových kurzů včetně odpovídajících cvičení a konzultací.
klasický Příklad na posun meta
Představme si, že máme za úkol navrhnout analytickou třídu A, která obsahuje několik datumů různých významů například datum X, datum Y, datum Z. Klasickým příkladem budiž třída Doklad, která má obsahovat informace jako datum založení, datum odsouhlasení aj.
Existuje několik variant, jak zavést takovéto datumy v dané entitě A, z toho uvedu následující dvě základní, které odpovídají možnému posunu META:
1. Datumy zavedeme „natvrdo“ jako atributy s jejich názvy, tj. ve třídě zavedeme atribut datum X, datum Y, datum Z atd. V modelu tříd tomu odpovídá následující jednoduchý model tříd s entitou A:
2. Oproti předešlému řešení provedeme posun META v tom smyslu, že dotyčná interpretace významu datumu nebude „natvrdo“, tj. na úrovni tříd jako název atributu, ale posuneme tuto interpretaci významu datumu do prostoru instancí. Třída A tedy bude obsahovat seznam datumů s odkazem do číselníku, který bude udávat význam daného datumu:
V tomto případě bude interpretace významu „schována“ do číselníku s instancemi, které budou mít odpovídající hodnoty: [kód = 1, text = „datum X“] [kód = 2, text = „datum Y“] [kód = 3, text = „datum Z“] atd. Všimněme si, že to, co bylo původně na úrovni tříd jako název atributu, se nyní stalo obsahem instance.
Poznámka: Existuje ještě varianta hybridní, o které se ještě zmíníme.
Které řešení je lepší?
Všimněme si, že obě řešení budou fungovat a obě se dají použít. Které tedy vybrat?
V prvé řadě se nedejme zmást výrazně viditelnou výhodou flexibility přidání nového významu datumu v druhém řešení. Tato varianta má totiž i své nevýhody. Pokud totiž zmíněnou flexibilitu nepotřebujeme, potom bychom si pouze přidělali vážné problémy.
Při volbě řešení se držme těchto pravidel:
1. Pokud si uživatel přeje přidávat své vlastní další datumy, není zbytí (a jinak to nejde), než tyto datumy řešit pomocí varianty druhé, tedy s posunem do META. Může tak vzniknout hybridní stav, kdy některé datumy jsou řešeny variantou prvou a jiné druhou anebo se hybrid zavede tak, že prvních N prvků číselníku významů datumu jsou „natvrdo zamknuty“ (původní neposunuté do META) a ostatní jsou uživatelsky „přidatelné“ (původně posunuté do META). Problém může nastat v případě různého chování s různými datumy, které si přidá sám uživatel. V tomto případě je třeba v maximální možné míře nasadit Design Patterns (GOF), zejména vzory STRATEGY, INTERPRETER, DECORATOR apod.
2. Varianta druhá s posunem META je bohužel složitější a v kódu nepřehlednější (a mnohdy technologicky pomalejší), v testování obtížnější atd., protože význam polí se oslovuje přes hodnoty. Proto bych obecně nedoporučoval (resp. raději zakázal) používat posun tam, kde se nežádá.
3. Je třeba upozornit na častou chybu špatného nedokonaného posunu do META: Žádá se a provede se sice posun ve třídách podle modelu 2 (tj. zavede se seznam datumů a číselník), ale kód se stejně napíše natvrdo, což je samozřejmě chybou. Je třeba si uvědomit, že pokud bychom v GUI například přiřadili k popisnému poli Label jeho text „natvrdo“, tedy v pseudokódu nějak takto:
LabelDatum.Text = ‘datum odsouhlasení’;
potom jsme samozřejmě zvolili variantu prvou, tj. variantu natvrdo a ne posunutou do META, ačkoliv model tříd vypadá jako posunutý do META. V našem příkladu při totálně dokonaném posunu META se musí význam datumu přiřazovat dynamicky přes daný číselník významů datumů. Klasické jednoduché řešení spočívá ve výběru významu uživatelem z Comboboxu. V pseudokódu zapsáno např. nějak takto:
LabelDatum.Text = ListCodeTagDates(selectedkod);
4. Pokud se obecně nežádá posun do META a aplikace se celá navrhne v posunu do META, jedná se o dost závažný problém s katastrofálními důsledky. Mnohdy se tak stane při úpěnlivé snaze získat maximální flexibilitu, ale nedomyslí se přitom důsledky. Je třeba si uvědomit, že uvedené dva návrhy (neposunutý a posunutý) mohou vypadat navenek pro uživatele „velice podobně“, ale z hlediska analytického modelu se jedná o dvě úplně odlišné aplikace dokonce s jiným zadáním! Pro vysvětlení uvedu jednoduchý problém v neposunutém a posunutém řešení: V neposunutém řešení se žádá při editaci zkontrolovat, že datum Y je vyšší nebo rovno než datum X. V pseudokódu zapsáno tedy velmi jednoduše:
if datum_Y < datum_X then RaiseError atd.
Pokud však provedeme posun do META, potom již nemůžeme oslovit tato dvě pole přímo, ale musíme dát uživateli možnost, aby si vybral ta pole, která budou podléhat takovéto kontrole (a to nejenom jednou!). Postup je tedy následující: Obsluha nejprve při administraci agendy vybere dva významy datumů, které budou této kontrole podléhat, my si odchytíme kódy těchto dvou polí a uložíme si je do konfigurace. Následně při editaci prvku z A musíme tuto kontrolu vyvolat, což znamená spustit tuto kontrolu, načíst tyto dva kódy polí a nechat kontrolu vykonat. Složitost problému spočívá v tom, že takovýchto dvojic polí může být více a navíc i různých kontrol s různými algoritmy bude také více a musíme je naprogramovat. Doporučuji v tom případě nasadit některý ze vzorů nejlépe INTERPRETER resp. DECORATOR apod. Velmi důležité je však to, že druhá aplikace posunutá do META řeší úplně něco jiného (výběr polí, uložení konfigurace atd.), což navenek vypadá podobně jako aplikace neposunutá. Navíc je zřejmé, že varianta posunutá je díky flexibilitě náchylnější k chybám (včetně chybného nastavení uživatelem) a také se obtížněji testuje.
Závěr
V článku bylo na konkrétním názorném příkladu na posun do META a na něm byly ukázány rozdíly obou přístupů - posunuté a neposunuté varianty.
Pokud je posun do META vyžadován (v zadání se vyskytuje takovýto požadavek na flexibilitu), je třeba jej samozřejmě nasadit. Pokud navíc dochází k různému zpracování různých instancí původně odlišených na úrovni tříd (viz příklad porovnání datumů), je třeba věnovat zvýšenou pozornost vzorům GOF a to zejména těmto: INTERPRETER, STRATEGY a DECORATOR.
Důrazně varuji před posunem do META, který není přímo ze zadání a tedy obchodně není vůbec vyžadován. Aplikace se stává zbytečně složitou a to minimálně o řád. Z praxe je mi známo několik projektů, které v touze po dokonalosti díky nešťastnému a zbytečnému posunu do META totálně zkolabovaly.
Konec článku
|