Design is hard

Poslední dobou mám pocit, že v programátorské branži jsou dva typy úkolů, které lze považovat za opravdu těžké:

  1. Implementace složitých low-level systémů – operačních systémů, kompilátorů, virtuálních strojů, vysoce optimalizovných databázových serverů, apod. Vše typicky v céčku, občas v C++.
  2. Design rozhraní. Za rozhraní v tomto článku považuju cokoliv, co je používáno uživatelem, ať už je jím uživatel v klasickém smyslu, nebo jiný programátor. Patří sem nové programovací a jiné jazyky, runtime knihovny těchto jazyků, jakékoliv rozsáhlejší API, uživatelské rozhraní netriviálních aplikací, atd.

Na práci na úkolech z první skupiny je těžké to, že je třeba přemýšlet v abstraktní rovině zadání a teoretických principů, na kterých je systém postaven, a zároveň i na úrovni bitů a bajtů, do kterých se vše nakonec zkompiluje. Chce to mít podrobný přehled jak o různých efektivních datových strukturách a algoritmech, tak o hardwarové architektuře, na které systém běží, a k tomu znát spousty implementačních a optimalizačních detailů specifických pro danou oblast.

Při práci a úkolech z druhé skupiny je potřeba především cit a schopnost představit si, jak výsledný produkt bude používat uživatel.

Jak může být někdy design těžký, ukazuje následující příklad: Elliote Rusty Harold se pustil do kritiky třídy list v jazyce Ruby. Přijde mu, že počet jejích metod (78!) je příliš velký a že mnohé se používají natolik zřídka, že nemají právo být v tak často používané třídě. Jenže Rusty je podle všeho člověk vyrostlý na Javě, což je jazyk sice objektový, leč stále v zásadě procedurální. Oproti tomu Ruby je prodchnuto funkcionálním programováním, ve kterém se s kolekcemi místo "opracovávání for-cykly" typicky pracuje přímo pomocí funkcí bez postranních efektů – v případě objektového jazyka tedy pomocí metod. A odtud plyne jejich hojnost v Ruby. Víc se o tom, jak je Rustyho pohled omezený, rozepisuje Slava Pestov.

(Poznámka stranou: Všimli jste si také, jak funkcionální programování prosakuje čím dál tím víc do nových programovacích jazyků, včetně třeba relativně konzervativního C#? Jako doplňkové programovací paradigma zřejmě má své místo.)

Můj názor na věc? Není nijak silný, protože o Ruby jsem jen četl a v praxi ho nepoužívám. Nicméně vím, že ruby má mechanismus mixins, který umožňuje snadné rozšiřování objektů o nové metody a vlastnosti. Malinko se divím, proč ho autoři nevyužili tady a některé pokročilé metody neimplementovali v samostatných třídách, které s uživatel v případě potřeby do konkrétní instance listu zamixuje. Možná se jim zdálo, že dalšími třídami zvětší složitost knihoven víc, než záplavou metod (tj. zvolili zesložitění kvantitativní oproti kvalitativnímu).

Existuje samozřejmě i varianta méně obvyklé metody neimplementovat vůbec a nechat na jednotlivých uživatelích, ať si základní třídy zdědí a metody v potomcích implementují dle potřeby. Přiznávám, že chvíli se mi toho zdálo jako zjevná správná cesta, ale nyní už si tím vůbec jistý nejsem. Podobná "holost" totiž třeba u C/C++ vedla k tomu, že si mnoho věcí každý kus kódu implementuje po svém a zkombinovat v jedné aplikaci více knihoven z různých zdrojů se stává noční můrou (Příklady? Různé definice booleovského typu v céčku (než se dostal do normy) nebo častý jev "co knihovna, to vlastní třída String" v C++).

Princip jednoduchých tříd je částečně použit i v Javě, ale má jednu obrovskou trhlinu: Klíčové třídy jsou deklarovány jako final, takže z nich nejde zdědit a upravit si je podle potřeby. V komentářích mých Javovských programů se dá na toto téma najít několik ne zrovna dvakrát slušných výroků směrem k tomu, kdo tohle vymyslel :-)

Jak říká titulek, design is hard, a často se všechny možné varianty zdají špatné. Najít mezi všemi požadavky rovnováhu a to nejen na lokální úrovni jednotlivých tříd či podobných malých jednotek, ale konzistentně v celém navrhovaném systému, je obrovské umění a lidé, kteří to dovedou, mají můj obdiv. Stejně jako lidé, co dovedou implementovat opravdu dobrý operační systém, kompilátor, virtuální stroj nebo vysoce optimalizovaný databázový server...

Dec 27, 2005 – 20:45

Comments

johno
No kvôli tomu článku bol na webe dosť velký poprask. Začal to celé Martin Fowler svojim článkom o HumaneInterfaces.

Úplne úžasne problém mnohých metód rieši SmallTalk. Hej, to je ten OO starček. Organizuje metódy do kategórií. To je podľa mňa geniálny nápad, na ktorý sa časom akosi zabudlo.

Celý ten problém vidím však v tom, že Ruby, Smalltalk majú niečo, čo sa volá "blocks". Človek tak bez nejakej veľkej znalosti vnútorných mechanizmov triedy môže "injektovať" potrebnú funkcionalitu.

songs.find { |aSong| aSong.name == "Black or White" }
songs.find { |aSong| aSong.author == "Michael Jackson" }

Toto je oveľa lepšia situácia ako v klasických objektových jazykoch. Tam by som sa musel prekúsať k iterátoru, potom vedieť čo v tom iterátore vlastne mám, porovnávať, ... Enkapsulácia je v prdeli, lebo som takto sa kompromitujú skoro všetky implementačné detaily.

Takže to vidím tak, že tu stoja tak trochu v protiklade dve pravidlá správneho OO designu. Enkapsulácia verzus Trieda má robiť len jednu vec a tú poriadne.

Add comment

It is not possible to add comments to posts older than one month.