Komentáře k článku
GRASP – 4 – Polymorphism, Pure fabrication a Indirection

V tomto díle o návrhových principech GRASP (General Responsibility Assignment Software Patterns) projdeme principy Polymorphism, Pure fabrication a Indirection, které se zabývají strukturními prvky architektury.
Re: GRASP – 4 – Polymorphism, Pure fabrication a Indirection
Jinými slovy nikdy neřiďte chování pomocí podmínek testujících typ objektu.
To se ale běžně dělá třeba v případě výjimek.
Pokud tento princip není dodržen, tak přidání každé další podtřídy vyžaduje úpravu na všech místech, kde se dříve testoval typ objektu.
Tahle skutečnost je určitým způsobem symetrická ke skutečnosti, že po přidání abstraktní metody do bázové třídy v jazycích jako C# nebo Java musíme upravit podtřídy, jenž nemají být abstaktní. Stejně jako v případě abstraktních podtříd může kompilátor upozornit na místa, kde testujeme typy objektů a chybí tam test na nějaký (nově přidaný) typ.
Informace o chování jsou tedy roztříštěné na mnoho míst kódu (což mimo jiné porušuje princip Don’t repeat yourself – DRY).
Pokud se kód neopakuje, tak DRY není porušen.
Tento princip říká, že „podtřídy by měly být zaměnitelné s jejich bázovými třídami“.
LSP mluví o podtypech a ne o podtřídách. Koneckonců aby nějaký typ byl podtyp, tak to nemusí být podtřída (a naopak) – bohužel v mainstreamových staticky typovaných jazycích musí.
Re: GRASP – 4 – Polymorphism, Pure fabrication a Indirection
Jako každé pravidlo i polymorfismus má svoje výjimky. V případě zachycování výjimek je typ výjimky hlavním a v podstatě jediným nositelem informace o tom co se stalo.
To že existují případy vyžadující nevyhnutelnou úpravu na mnoha místech kódu přece neznamená, že se jim nemáme snažit vyhnout. V mnoha případech (vývoj knihoven) pak často ani nemáme nad všemi užitími našich tříd kontrolu.
DRY bývá porušeno v případech, kdy na mnoha místech používáme stejnou sadu podmínek kontrolujících typ. Jde tedy obvykle o opakující se kód. Opakující se informací je právě seznam známých podtříd.
S podtypy/podtřídami v LSP máte pravdu.
Re: GRASP – 4 – Polymorphism, Pure fabrication a Indirection
To že existují případy vyžadující nevyhnutelnou úpravu na mnoha místech kódu přece neznamená, že se jim nemáme snažit vyhnout.
S tím souhlasím. Představte si, že mám třídy pro reprezentaci aritmetických výrazů (jazyk C#, ale závěr se vztahuje i na Javu a jiné jazyky):
Mám minimálně 2 možnosti, jak implementovat vyhodnocení výrazu:
Nevýhoda první možnosti je v tom, že musím upravit kód na mnoha místech – ve třídách ExprAdd a ExprInt. Navíc je logika vyhodnocení výrazu rozházena po mnoha třídách.
Nevýhoda druhé možnosti je v tom, že pokud přidám např. ExprMult (uzel pro násobení), tak musím upravit i funkci eval a každou další funkci, která dělá switch dle typu, takže opět musím upravit kód na mnoha místech.
Pokud nebudu přidávat další uzly, tak je druhá možnost lepší. Pokud nebudu přidávat další metody, tak je první možnost lepší. Programátor by se měl tedy rozhodnout podle toho, zda bude přidávat nové metody nebo nové uzly.
Re: GRASP – 4 – Polymorphism, Pure fabrication a Indirection
jak říkáte, vždycky je potřeba se zamyslet nad výhodami a nevýhodami jednotlivých řešení. GRASP principy je vždycky potřeba zvažovat jako celek a dle předpokládaného vývoje.
Osobně bych ale volil první možnost, protože přidání nového typu výrazu považuji za pravděpodobnější než přidání nové metody do tříd výrazů. (Protected variations)
Zásada Polymorphism zde jasně preferuje první možnost.
Dle principu Information Expert (bude probrán příště) by zodpovědnost za výpočet měla mít třída, která má potřebné informace. V našem případě tedy opět třída výrazu.
Třída Eval by představovala Pure fabrication, kterému bychom se měli vyhnout pokud k tomu není nějaký důvod. Ten by zde ale je možné představit například v efektivitě výpočtu nebo jak píšete v rozhození logiky výpočtu na mnoho míst.
V zásadě jde o to, že GRASP nám nikdy nedá jasnou odpověď jak to udělat, ale poskytuje vodítka jak o tom jasněji uvažovat. Volba je ale vždy na vývojáři.