Kolem modelování v software už byla napsána a řečena spousta vznešených slov (třeba zde). Podívejme se proto na jednoduchý způsob, jak ho implementovat v praxi.
Příklad
Máme webovou aplikaci (např. online shop), která ukládá data do databáze. Aplikační logika je implementována jako domain model, takže pro každou "entitu" v reálném světě (klient, produkt, objednávka atd.) existuje v aplikační vrstvě třída. Objekty jsou do databáze mapovány pomocí NHibernate.
A nyní si představte, že do objednávky budete chtít přidat další údaj, kontaktní telefon. Maximální počet znaků je 9. Musíte editovat webovou stránku s formulářem objednávky, musíte přidat javascriptový validátor, přidat property Phone do třídy Order a rozšířit její validační metodu, do mapovacího souboru Order.hbm.xml doplnit <property name="Phone" type="string" length="9" not-null="true" /> a do databázové tabulky tblOrder přidat sloupec Phone NVARCHAR(20) NOT NULL. Dost práce, navíc spousta duplicity, že? A po týdnu zjistíte, že telefon se do 9-ti znaků nevejde (národní předvolby) a rozhodnete se zvětšit délku údaje na 20 znaků. Budete opět muset upravit všechna zmíněná místa, na žádné nezapomenout a neudělat překlep.
Model
Přitom se stále jedná o jeden logický koncept: Objednávka má pole telefon s délkou 20 znaků. Daleko rozumnější by proto bylo udržovat tyto definice na jednom místě ("model" naší aplikace) a ostatní "artefakty" (části kódu) generovat automaticky. Vymyslíme tedy způsob zápisu, jak model definovat, vytvoříme model naší aplikace a pak ho budeme nějak parsovat a podle nějakých šablon generovat artefakty kódu. Způsob zápisu modelu je náš doménově-specifický jazyk (DSL), který umožňuje zachycovat entity z domain modelu aplikace.
Nabízí se tři řešení, jak to udělat: Vlastní implementace, univerzální CASE nástroj, nebo použít existující "language workbench".
Vlastní implementace vypadá jednoduše: Uděláme nějaký "texťák" (céčkař) nebo "iks-em-elko" (dotneťák) a napíšeme si jednoduchou utilitku, která ho přečte a na stanovená místa vyplivne kód, v lepším případě pomocí nějakých šablon (např. XSLT). Jenže model bude mít velmi mnoho vlastností a mezi prvky modelu budou vztahy které nelze definovat jinak než pomocí nějakých "ídéček". Ze začátku jednoduché jak facka, ale až model naroste, bude velmi nepřehledný a udržovat ho bude dost pracné. Takže se uchýlíte k tomu, že ve svém volném čase budete psát další utilitky pro kontrolu modelu, prohlížení modelu s navigací po vztazích mezi entitami, editor se zvýrazňováním syntaxe atd. To vám zabírá čas který by jste měli věnovat učení se novým technologiím. Výsledek nestojí za to, veřte mi, zkusil jsem to:-) Navíc ztrácíte podporu internetové komunity, čímž se okrádáte o nejrychlejší způsob řešení problémů - gůglení.
Velmi svůdné řešení je použít nějaký CASE nástroj, nejspíš na bázi UML. Jejich nevýhoda ale je, že jsou příliš univerzální, takže 90% jejich fíčur vůbec nepoužijete, a naopak když budete potřebovat něco trochu jiného než program umožňuje, budete vymýšlet nejrůznější hacky jak to udělat, což opět znepřehledňuje model. Výsledkem je velmi vratký rovnák na vohejbák (věřte mi, také jsem se o to pokoušel). Dále se připravte na to, že tyto nástroje mají obvykle strmou učící křivku (zná někdo lepší překlad "steep learning curve"?), že budete muset zjistit jak to integrovat s vaším IDE a že každý vývojář bude potřebovat placenou kopii nástroje.
"Language workbench" je Fowlerův termín pro sadu nástrojů, které usnadňují tvorbu vlastních DSL jazyků. Plurál je na místě, pro každý účel můžete vytvořit speciální jazyk přesně na míru problému, daleko vhodnější než různě přiohnuté UML. Tyto nástroje zahrnují tvorbu jazyka, práci s modelem, generování artefaktů z modelu a integrace do IDE. Je to nejlepší z obou světů: Snadno vytvoříme jazyk přímo pro konkrétní účel včetně generování artefaktů, a přitom máme k disposici nástroje a internetovou komunitu. Navíc můžeme jazyk tvořit inkrementálně, začít skromě a postupně přidávat funkčnost podle potřeby, tam kde se ukáže že je to možné a přínosné. Tak si v našem příkladě můžeme vytvořit doménový jazyk pro definici entit a jejich vlastností, a později přidat "constraints", např. minimální a maximální hodnoty vlastnosti, a upravit šablony aby generovaly validační kód na všechny vrstvy kde je potřeba.
Osobně vím o dvou takových "workbenchích", jedna je Meta Programming System od JetBrains (stále ještě v beta), druhá DSL Tools od Microsoftu, součást Visual Studio SDK. První jsem nezkusil, ale pokud programujete v javě a používáte IDEA, asi by to stálo za pokus. Druhou používáme v praxi pro definování aplikačního domain modelu a generování SQL, HBM, domain tříd, Repository, Query a DTO tříd. Podle constraints definovaných v modelu jsme schopni nagenerovat i základní klientské validátory v javascriptu.
DSL Tools
Po zdlouhavém úvodu se konečně dostáváme k věci. Protože je na českém internetu velmi málo zdrojů o DSL Tools, pokusil jsem se natočit vlastní screencast pro začátečníky. Má vás přenést přes vstupní bariéru a předvést základní kroky, které jsou nutné k vytvoření jednoduchého DSL jazyka. Sám jsem na začátku dost tápal, tak doufám že vám to ulehčí start. Je to můj první screencast, tak buďte prosím shovívaví (ano, dělám si alibi:-). DSL Tools jsou součástí Visual Studio SDK a k vytváření jazyků je potřeba Professional edice. Pak je možné vytvořit add-in package do Visual Studia a k běžné práci s modelem by měla stačit Standard edice bez nutnosti instalovat SDK (modelovací runtime je od verze 2008 součástí Visual Studia). Zásahů do jazyka je potřeba poměrně málo, protože většinu problémů vyřešíte pouhou změnou tt šablony.
Ve videu uvidíte, jak se nejdřív definuje DSL jazyk, a jak se vyzkouší v nové instanci Visual Studia. Kód se generuje pomocí T4 a zvýraznění syntaxe poskytuje Clarius T4 Editor (plugin do Visual Studia).
Jakmile překonáte vstupní bariéru, vše půjde hladce. Na netu jsou dostupná další videa v angličtině, doporučuju také blog Olega Sycha který se věnuje generování artefaktů pomocí T4 šablon a zná spoustu fíglů. Pokud to s DSL myslíte vážně, kupte si knížku Domain-Specific Development with Visual Studio DSL Tools, to je asi nejlepší existující dokumentace.
A co mám na DSL Tools nejradši? Že prostředí, ve kterém vytváříte váš DSL jazyk, je samo doménově-specifickým jazykem pro modelování DSL jazyků:-) Jinými slovy, meta-modelování a modelování používá totožný modelovací runtime. To co vytváříte v DSL projektu v souboru DslDefinition.dsl, je model vašeho jazyka, který je následně transformován pomocí tt šablon na C# kód, přeložen, nainstalován do experimental hive Visual Studia. Stejně pak funguje váš jazyk: Umožňuje vytvořit model, transformovat na kód, zkompilovat a spustit. Znamená to také, že váš DSL jazyk můžete lehce customizovat pomocí partial a double derived tříd. Není to žádný trik, ale starý dobrý C#. Dál to taky znamená, že vše co vidíte v DslDefinition.dsl (vzhled a layout diagramu, editory vlastností, validace modelu atd.) můžete použít i ve svém jazyku. Můžete si být jistí proto, že váš model poběží na stejném runtime jako designer DSL jazyka.
Solution vytvořené ve videu: SampleDsl.zip
@samhoov: Omlouvám se za opožděnou reakci. Vygenerovat kód jen jednou je samozřejmě možné, ale jaký to má smysl? Vytvořit vlastní DSL jazyk a šablony pro generování kódu je poměrně pracné, a dělá se to právě proto, aby bylo možné jednoduše generovat kód z modelu, kdykoliv je to potřeba. Pokud chcete jednorázově vygenerovat kostru kódu, je podle mého názoru jednoduší si to napsat ručně;)
jde mi oto vytvorit sablonu podle ktere by se treba zakladal novy projekt napřiklad. vim ze kostra projektu ma byt nejaka a potom vim k cemu ma konkretni projekt byt(podle analyzy). napriklad novy modul do SCSF ma nejaka view a ty mohou vyvolavat dialogi. toto si pri zakladani noveho projektu namodeluji a necham vygenerovat. ale pote jiz potrebuji dale s vygenerovymi tridami pracovat. pridavat usercontroly atd.
Diky za zajimavy blog