neděle, prosince 27, 2009

Jak jsem přešel na Linux a MonoDevelop

Má trpělivost se smrtící kombinací Vista+Visual Studio+ReSharper byla vyčerpána. Mám pocit, že neustále čekám alespoň na jeden prvek z této trojice. Můj upgrade na Windows7 je přitom minimálně tři měsíce daleko, a navíc si nejsem úplně jistý, že to přinese zásadní zrychlení. Rozhodl jsem se proto vyzkoušet Linux+MonoDevelop, pro začátek na nějakém malém testovacím projektíku.

Pro člověka odkojeného Windows je těžké se v bziliónu Linuxových distribucí a balíčkovacích systémů vyznat. Navíc nemám žádný hardware, který bych mohl obětovat na pokusy, takže potřebuju bootovat z 4GB USB flašky, kam se musí vejít vše. Instalace musí být persistentní, abych při rebootu nepřišel o data. Později jsem zjistil, že také potřebuju, abych mohl bez problémů (a bez kompilace jádra:-) nainstalovat nejnovější verzi Mono a MonoDevelop, to totiž na Linuxu není samozřejmost. O tom za chvíli.

Nejdříve jsem si pohrával s Live CD ze kterých jsem vytvořil persistentní Live USB flašku. Zkoušel jsem Slax, než mi došlo že na KDE asi MonoDevelop nepoběží. Pak jsem si hrál s Ubuntu a openSUSE, ale stále to nebylo ono. Live CD openSUSE vytvořené podle tohoto návodu z neznámých příčin záhadně zamrzalo. Ubuntu běželo bez problémů, jenže jsem s překvapením zjistil, že na něj nenainstaluju MonoDevelop 2.2, protože balíčky nejsou aktuální. Rozhodl jsem se proto vrátit k openSUSE, pro které poskytují zkompilované balíčky přímo tvůrci MonoDevelop. Systém jsem tentokrát nainstaloval přímo na USB flašku, jako kdyby to byl hard disk. Tady je postup:

  1. Pro jistotu jsem z notebooku vyndal hard disk, abych ho omylem nesmazal.

  2. Nabootoval jsem z openSUSE Live CD (verze 11.2 GNOME x86_64) a zvolil instalaci.

  3. V konfiguraci disk partition jsem smazal swap a ponechal jen jednu linuxovou partition. Momentálně swap nepotřebuju, protože Linux využívá zatím pouze 600MB paměti, i když používám tři desktopy a Firefox. Až budu swap potřebovat, stejně ho kvůli opotřebení nebudu chtít mít na flašce, ale na hard disku.

  4. Nainstaloval jsem systém a provedl aktualizace přes GUI nástroj. V tuto chvíli už na flašce nezbývalo moc volného místa, tak jsem odinstaloval hry, cizojazyčné lokalizace a OpenOffice. Díky tomu jsem získal kompletně nainstalovaný systém a 1GB volného místa.

  5. Do Package Repositories jsem přidal Mono (http://ftp.novell.com/pub/mono/download-stable/openSUSE_11.2) a MonoDevelop (http://download.opensuse.org/repositories/Mono/openSUSE_11.2/)

  6. Nainstaloval jsem balíček mono-complete s aktuální verzí Mono 2.6.1. Při tom jsem musel změnit providera všech ostatních mono balíčků (protože mono už je na openSUSE nainstalované, ale z jiného zdroje). Balíčkovací mechanismus mi přijde docela robustní, sám hledá závislosti a hlídá jejich porušení, takže změnu providera vám nabídne automaticky.

  7. Nakonec jsem přidal balíček monodevelop verze 2.2.


Příjemné zjištění bylo, že MonoDevelop už v sobě obsahuje podporu ASP.NET MVC, takže stačilo založit novou solution podle šablony a Hello World byl hotový. Podobně jako VS má MonoDevelop ořezaný vývojářský web server, takže stačilo zmáčknout F5 a projekt se spustil.

Musím přiznat, že jsem si myslel že to bude jednodušší. Zvlášť úvodní tápání mezi distribucemi bylo dost zdlouhavé a chvílema jsem už začal pochybovat, jestli je možné to všechno na flašku dostat. Je to samozřejmě tím že jsem linux-lama, a doufám že se to bude časem zlepšovat;) Na druhou stranu, teď mám (skoro) kompletní vývojářské prostředí a můžu se konečně pustit do předělání tohoto blogu, jak už několik let plánuju.

Nyní přichází nejdůležitější rozhodnutí ze všech: Jak se bude můj blogovací engine jmenovat?

čtvrtek, listopadu 26, 2009

...a jeden navrch

Kolega mi včera na chvíli půjčil ještě úplně nové HTC HD2. No a musím říct, že je to povedený kousek.



Je sice větší než iPhone, takže ho budete dost těžko strkat do kapsy, ale má zase větší displej a škvíru reproduktoru (telefonovat jsem tím nezkoušel) (používá vůbec někdo smartphone k telefonování?). HTC je zhruba stejně placatý a těžký jako iPhone.

Stejně jako iPhone se ovládá prsty a zvládá multitouch. Je to moc pěkná hračka, na UI si někdo pěkně vyhrál. Mobilní Opera zvládá bez problémů běžný (nemobilní) web, na rozdíl od Internet Exploreru, který je stále 100 let za opicema (sorry, opice). Operační systém v telefonu je přitom Windows Mobile, což má pro nás .NET developery jisté kouzlo. Ještě že musím koupit zimní pneumatiky a nemusím řešit dilema typu iPhone vs. HTC HD2.

pondělí, listopadu 23, 2009

Čtyři smartfouni

Leží mi na stole čtyři smartfouni. Díky tomu, že děláme mobilní verzi našeho produktu, máme příležitost vyzkoušet, jak vypadá web v různých telefonech, a taky si je pěkně ošahat. Máme tu iPhone 3GS, T-Mobile G1, Sony Xperia X1 a BlackBerry 9000.



Rozhodně si netroufám na komplexní a nezaujatou recenzi, ale říkal jsem si že by bylo dobré napsat své první dojmy z těchto čtyřech smartphones, jak působí na laika. Jsem sice zkušený uživatel PC, ale telefonuju z levného vysouváku Samsung (to je ta lednička, smějou se mi kolegové), a smartphone jsem doteď v podstatě nedržel v ruce.



iPhone je fakt sexy telefon a má jednoduché ovládání. Navíc můžete vyměnit sim kartu bez vypnutí telefonu, což při takovémto testování oceníte. U ostatních je nutné odstranit zadní kryt a vyndat baterku. Jako jediný se ho povedlo připojit na firemní wi-fi. Má největší displej a je nejtenčí.



T-Mobile G je ne zcela podařený pokus o iPhone. Má sice dotykové ovládání, ale zvětšovat/zmenšovat musíte tlačítky (dvěma prsty jako u iPhone to nejde). Na druhou stranu můžete po odkazech na webové stránce skákat kuličkou, což je dost fajn. Při dobíjení baterky se zadní strana zahřeje tak, že by šel použít i jako ponorný ohřívač.



BlackBerry bych se styděl vytáhnout na veřejnosti, okamžitě se prozradí že pracujete v IT. Při zadávání URL do browseru nejde zadat tečka jednoduše, musíte podržet Alt. Nemá to dotykovej displej, a ovládat kurzor kuličkou je šílenost, neskáčete po odkazech, ale posunujete kurzor po obrazovce, takže furt prstíte jak vzteklí. Prohlížeč je dost ořezanej, nezvládá moc ani CSS ani javascript. Ze všech čtyřech telefonů bylo na BlackBerry načítání stránky nejpomalejší.



Sony Xperia je tlustá cihla, která se ovládá tenkou tyčkou, musíte se trefit do titěrných čudlíků, a nedokážu si představit jak bych to zvládal v metru nebo ve vlaku. Tyčka se strká shora do telefonu, a asi bych ji hned druhý den ztratil. Na druhou stranu, furt lepší a rychlejší než než klávesnice BlackBerry, ale já jsem myšoidní typ. Windows Mobile asi nebude můj oblíbený systém. Hlášky jako "systém čeká", když ve skutečnosti čekám já na systém, nebo výhrůžky jako "neuložená data budou ztracena", mě zvedaj ze židle. Internet Explorer na Xperii nezvládá javascript, Opera sice ano, ale je dost pomalá.

Pokud bych si měl některý z těch telefonů koupit, určitě bych si vybral iPhone. Je to sice nejhorší telefon na trhu, ale z těchto čtyřech na mě udělal nejlepší dojem. Ovládání je intuitivní, a navíc je to vyloženě pěkná hračka.

pondělí, října 26, 2009

Co by měl každý programátor vědět

Občas mě zarazí, že i lidi, které považuju za dobré programátory, neznají základy programovacího jazyka, ve kterém píšou 95% kódu. Například nedávno v naší kanceláři proběhla debata, proč si StyleCop stěžuje na to, že jsou v kódu nadbytečné závorky. Šlo o složenou podmínku v příkazu if, kde si dotyčný pomáhal závorkami, protože si nebyl 100% jistý s prioritou operátorů && a ||. Co na to říct?

Podle mého jsou určité základy, které musí každý programátor o svém jazyce vědět. Priorita operátorů podle mně patří mezi ně. Argument, že v jazyce XX nebo YY to bylo jinak, je podle mně zcestný. Programovací jazyky jsou natolik jednoduché, že programátor nemůže mít problém si těch pár věcí zapamatovat. Netvrdím, že musíte z hlavy vědět vše o nějakém obskurním jazyku, který používáte jednou za uherský rok, ale pokud se někdo už několik let živí psaním kódu v C#, musí takové věci znát bez zaváhání.

Co všechno by jste tedy měli vědět o svém hlavním programovacím jazyku?

  • Priorita operátorů (!, ++, ||, &&, ?:)

  • Jak zapsat hodnotu různých datových typů (0xAF, 123 a 123L, 0.12M a 0.12D)

  • Základní konverze na string a zpět (například donedávna jsem považoval za samozřejmost, že každý ví jak konvertovat "FFEEDD" na int, ale byl jsem vyveden z omylu)

  • Speciální jazykové konstrukce jako anonymní delegáti a lambda funkce



Tento seznam určitě není kompletní, pokud vás napadne něco dalšího, napište to do komentářů.

Pokud tyto základní věci neovládáte, riskujete dva velké problémy: Budete se pomalu orientovat v kódu ("jakého typu je var a = new[] { 1, 10, 100, 1000 };?"), a při psaní budete používat zbytečné ornamenty (if(((age>60)||(disabled==true))&&(balance>limit))). Druhý problém je o dost horší než první, protože při čtení zpomalujete jen sami sebe. Pokud ale kvůli své neznalosti použijete o 8 závorek navíc, ztěžujete práci ostatním, protože znepřehledňujete kód. Je sice pravda, že ostatní pak nemusí používat svn blame, protože už znají váš styl:-) Být poznat podle stylu kódu ale není nic o co by jste měli stát. A taky budete hodně nadávat na StyleCop:-)))

Pokud neovládáte ani naprosto základní věci, jak chcete přesvědčit ostatní o tom, že jste dobří programátoři?

neděle, srpna 23, 2009

Co všechno musíte vyřešit při psaní WPF aplikace

WPF je kůl, o tom žádná. Proti Windows Forms je to opravdu obrovský skok kupředu. Ale to ještě neznamená, že při tvorbě aplikace založené na WPF nemůžete dostat osypky a že vás nečekají bezesné noci. V tomto příspěvku se pokusím shrnout hlavní problémy, ke kterým je třeba se postavit čelem, bez nároku na nalezení konečného řešení:

  • INotifyPropertyChanged

  • Validace

  • Lokalizace

  • Undo/Redo


INotifyPropertyChanged


Aby View vědělo kdy se má updatovat, je nutné u všech ViewModel objektů implementovat rozhraní INotifyPropertyChanged. Naštěstí je velmi jednoduché, ale budete muset psát spoustu opakovaného kódu:

string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name);
}
}

Vyhnout se mu můžete buď generováním kostry ViewModelu pomocí DSL jazyka, nebo pomocí AOP (kolegové používají PostSharp), kde stejná property může vypadat takto:

[Notify]
public string Name { get; set; }

Lokalizace


Existuje několik přístupů k lokalizaci WPF aplikace, které se liší způsobem uložení resource stringů, možností výběru lokalizovaných vlastností a možností přepínat jazyk "za běhu", bez nutnosti restartu aplikace. Řešení které doporučuje Microsoft (přes binární XAML, neboli BAML) potřebuje externí nástroj LocBaml.exe, editovat jazykové verze musíte v CSV a při přepnutí jazyka za běhu se nelokalizují již otevřená okna. Které vlastnosti budou lokalizované se dá určit až při lokalizaci, takže překladatel se může rozhodnout, že např. sloupec v tabulce bude mít jinou velikost nebo label se zarovná na jinou stranu. To může být výhoda i problém, záleží na vašich potřebách. Další dva způsoby naleznete v článku Localizing WPF Applications using Locbaml, oba ale neumožňují přepínat jazyk za běhu.

Zajímavé řešení je popsáno v WPF Localization - On-the-fly Language Selection. To už umožňuje skutečné přepnutí jazyka bez nutnosti restartu aplikace. Je založené na vlastním Converteru, který provádí lokalizaci, díky čemuž lze používat poziční parametry v resource stringu ("{0}") a jejich hodnoty dokonce bindovat. Trik umožňující okamžité přepnutí jazyka spočívá v nahrazení lokalizovaných hodnot MultiBinding objektem, kde první vazba je na LanguageContext.Instance.Dictionary, která při změně jazyka posílá událost PropertyChanged. To způsobí znovunačtení všech hodnot. Tomuto řešení bych vytknul pouze způsob uložení resourců (vlastní XML), ale velmi lehce si můžete napsat vlastní LanguageDictionary a číst hodnoty např. z resx souborů.

Validace


Při prvním pohledu na WPF se zdá, že validace je vyřešená věc, ale pokud chcete použít vzor MVVM, není to tak jednoduché. Ve ViewModelu tedy implementujeme IDataErrorInfo a projdeme všechny deklarace Binding v XAMLu a přidáme ValidatesOnDataErrors=true.

Jenže kromě business validací je nutné také validovat vstup od uživatele. To jsou opravdu dvě různé věci: Mějme třídu Person s property Age typu int. "Business" validace je, že Age nesmí být záporné a musí být menší než 150. Když je hodnota mimo tento rozsah, vrátíme přes IDataErrorInfo chybovou hlášku. Jenže co když uživatel zadá "qweqweqwr"? Výchozí chování Bindingu je výjimku prostě spolknout. Můžeme nastavit ValidatesOnExceptions na true, ale jednak nemůžeme změnit validační hlášku, a také nechceme žádné výjimky. Výjimky jsou pro výjimečné případy, např. když je vyhlášen výjimečný stav. Spoléhání na výjimky může aplikaci znatelně přibrzdit.

WPF Binding zpracovává vstup přes řetěz validátor - konvertér - property setter. Možným řešením by bylo vkládat vlastní validátory do deklarace Bindingů. To ale odporuje našemu cíli, dostat všechnu logiku do ViewModelu. Navíc až budeme objekt ukládat (uživatel zmáčkne Ctrl+S), nedozvěděli bychom se, že na formuláři jsou nevalidní data. Informaci o validitě si přece chceme přečíst z ViewModelu. Nejen kvůli tomu odpadá i další možnost: generovat validátory podle metadat ViewModelu. Ani to nezkoušejte, nejde to. Jak zjistil Miro, Binding je jen MarkupExtension, který vytvoří BindingExpression, který se teprve stará o update hodnot, jenže většina jeho důležitých vlastností je internal.

Jediným mně známým řešením je proto deklarovat ve ViewModelu property Age jako string a provádět konverzi až tam. Když se konverze povede, zapíšete property do Modelu, kde už je typu int a kde se provede business validace. Je to víc práce ale zase má ViewModel informaci o tom, jestli je formulář validní. Implementace je popsaná v článku Using a ViewModel to Provide Meaningful Validation Error Messages.

Aby to nebylo tak lehké, představte si že chcete z ViewModelu vrátit hlášku "Věk musí být mezi {0} a {1}". Obstarožní interface IDataErrorInfo umožňuje vracet pouze string, takže ve ViewModelu provedeme String.Format a vrátíme už hotový text. Jenže to komplikuje lokalizaci. Kvůli přepnutí jazyka za běhu bychom potřebovali vracet klíč do resourců a pole parametrů, aby lokalizátor mohl aktualizovat hlášku. Ideální by tedy bylo napsat si vlastní interface, lepší a hezčí než IDataErrorInfo (to by věru nebylo těžké), protože do ValidationError objektu můžeme ukládat daleko více informací než jen string. Pomocí Reflectoru kolega Miro zjistil, že Binding při nastavení ValidatesOnDataErrors=true vkládá do pole validátorů objekt DataErrorValidationRule. Kdybychom tedy napsali vlastní ValidationRule, mohli bychom přečíst errory pomocí vlastního interface a vytvořit takový ValidationError objekt, který by byl lokalizátor schopen za běhu překládat. Jenže opět narážíme na to, že property BindingExpression.ItemSource, která obsahuje zdroj data bindingu (ViewModel), je internal! Zbývá nám tedy buď oželit okamžitou lokalizaci validačních hlášek, přinutit všechny BindingExpression objekty provést refresh, nebo si do stringu serializovat template s parametry a v lokalizátoru je deserializovat, vše mi přijde ošklivé.

Undo/Redo


WPF má sice vestavěnou podporu pro undo/redo v TextBoxu, ale to nám samozřejmě nestačí, potřebujeme undo/redo v celé aplikaci (nebo lépe, v celém editovaném projektu, který tvoří undo scope - projektů můžeme mít otevřených víc naráz). Zde se nabízí klasické řešení pomocí UndoItem objektu a undo a redo stacku, popsané třeba zde. Je ovšem nutné vyřešit otázku jak nalézt undo scope (projekt), když naše ViewModel objekty tvoří košatě rozvětvený strom.

Také musíme implementovat undo transakce. Kvůli tomu, aby bylo uživatelské rozhraní živější, jsme totiž ve všech Binding deklaracích nastavili UpdateSourceTrigger=PropertyChanged. Díky tomu je uživatelův vstup okamžitě validován, ale protože se přes ViewModel posílá každý jednotlivý znak který uživatel zadá do TextBoxu, bylo by undo dost nepoužitelné. Potřebujeme tedy zahájit undo transakci při vstupu do TextBoxu a ukončit ji při jeho opuštění (nebo stisku enter). To nás nejen odsuzuje k handlování událostí nebo vlastnímu TextBoxu, ale má to i jeden háček: Při kliknutí myší do menu nebo na toolbar TextBox neztratí focus. Tím pádem se při Save/Load neukončí undo transakce a nezahájí se nová, na což je potřeba myslet a modelovat root undo scope (projekt) tak, aby ho bylo možné při Load celý zahodit a vytvořit nový (i pak si ale TextBox drží focus a nezahájí novou transakci).

Závěr


Jak je vidět, komplexitu dobré aplikace není radno podceňovat ani s tak vyspělým frameworkem, jako je WPF. Pokud narazíte na další problémy nebo na jiná řešení, dejte vědět.

sobota, června 13, 2009

Architektura Ginger

Ginger je náš framework pro webové aplikace. Tvoří ho knihovny s infrastrukturním kódem a sada doporučení (nebo spíš přikázání:-))), jak rozdělit aplikaci na vrstvy a které projekty umístit do které vrstvy. Je určen pro webovou vícevrstvou škálovatelnou aplikaci, která ukládá data do databáze.

Podporován je jednoduchý deployment na jeden počítač i složitější varianta s webovými servery v DMZ a aplikačními uvnitř sítě; v zásadě jde jen o změnu konfigurace.

Nedávno jsem o tom dělal interní přednášku, ze které pochází prezentace, kterou zde s laskavým svolením mého zaměstnavatele uveřejňuji. Dozvíte se v ní, jak vypadá cílové prostředí, pro které je Ginger určen(a), jak je aplikace rozdělená do vrstev, jak vypadá kód na jednotlivých vrstvách, co v našem případě znamená "M" v ASP.NET MVC a kolik návrhových vzorů z PoEAA používáme (nebo plánujeme používat).

Požadavek na oddělené nasazení webových a aplikačních serverů měl na architekturu Ginger zásadní vliv. Musíme totiž počítat s poměrně pomalým a nespolehlivým síťovým spojením (v porovnání s programováním v jedné aplikační doméně), a proto nemůžeme použít Open Session In View (OSIV) pattern, ani skládání ovládacích prvků ve stránce přes RenderAction. Všechny požadavky na data nutná pro renderování stránky potřebujeme mít už v controlleru, při volání aplikační vrstvy (Remote Facade), to znamená před tím, než se vůbec vytvoří instance View (kdo neví proč, nechť si přečte vynikající knihu Release It!). "M"odelem, který v ASP.NET MVC podstrkujeme View, je v našem případě ResponseMessage, kterou vrací aplikační vrstva. Díky tomu máme zcela oddělený doménový model (kde je aplikační logika) od View, a proto problém zmíněný na diskusi o MVC Best Practices, kdy hrozí že se z View bude měnit doménový model, je nám zcela cizí.

Abychom co nejvíce omezili opakování kódu na různých vrstvách, vytvořili jsme si pomocí DSL Tools modelovací jazyk, ze kterého spoustu kódu automaticky generujeme: SQL create scripts, doménové objekty, repository a query objekty a DTO objekty, které v sobě obsahují metadata, takže z nich je možné za běhu vygenerovat validační javascripty do stránky. Vygenerovaný kód je přitom možné rozšiřovat bez nutnosti úpravy DSL jazyka nebo generovacích šablon, díky partial třídám, nebo volitelně double derived vzoru.



Zde si můžete stáhnout plnou verzi prezentace, se všemi animacemi, které SlideShare neumí zobrazit.

pondělí, března 16, 2009

Trochu teorie kolem DSL

Doufám, že vás v mém posledním postu moc nevystrašilo slovní spojení "Domain-Specific Language". Je to jen další buzzword: zní to sofistikovaně, ale ve skutečnosti to není nic nového. Laskavý čtenář, kterému rostou víc vousy než vlasy, si možná vzpomene na minijazyky v unixu, což není nic jiného než DSL - malý jazyk vymyšlený pro konkrétní účel.

Dnes se DSL nejčastěji objevují ve formě konfiguračních souborů. Například *.hbm.xml soubory v NHibernate jsou zapsány jazykem speciálně vytvořeným pro problémovou oblast (=doménu), kterou je v tomto případě mapování mezi databází a objekty. Nebo obyčejný web.config v asp.net aplikaci: To je jazyk specifický pro konfiguraci .NET website, kde je nutné určit které moduly a handlery budou pro který typ souboru použity:-) Takže s nějakým DSL se setkal každý programátor, jen o tom možná neuvažoval jako o DSL.

Doporučuju vám tuto prezentaci, kde Martin Fowler vysvětluje, co si představuje pod pojmem DSL. Všimněte si, že říká že hranice co už je DSL a co ještě není je neostrá. Dokonce i C# nebo java kód může být chápán jako DSL, kde forma zápisu je shodná s programovacím prostředím. V takovém případě se jedná o interní DSL, který lze přímo spustit.

Oproti tomu externí DSL jazyky mají svůj vlastní způsob zápisu, nezávislý na existujících programovacích jazycích. To přináší větší svobodu v definici syntaxe jazyka, na druhou stranu musíme kód nějak parsovat a interpretovat sami. To je natolik složité, že ve většině případů by komplexita parseru a generátoru převážila nad zisky z používání DSL jazyka, což je mimochodem důvod, proč mají tvůrci dnešních DSL tak rádi XML, XSD a XSLT. Další nemalé úsilí by bylo zapotřebí k tomu, aby se "user experience" při používání DSL jazyka přiblížila moderním IDE: Kdo jednou zkusil intellisense ve Visual Studiu a průběžnou analýzu kódu s ReSharperem, těžko se bude chtít vracet k plaintextu.

Když už máme náš nový jazyk definovaný a umíme ho parsovat, je nutné se rozhodnout jak ho budeme interpretovat. Můžeme z DSL jazyka generovat zdrojové kódy v jiných (existujících) jazycích, kompilovat je a distribuovat náš program jako binární knihovny. Druhou variantou je napsat "framework", který bude náš DSL kód interpretovat za běhu a distribuovat tento framework s výchozí podobou DSL kódů, které si koncový klient může upravit podle svého. Typickým příkladem prvního přístupu jsou programy pro modelování v UML, v druhém případě pak konfigurační soubory nebo skriptovací jazyky.

Generování kódu nám jako autorům programu dává do rukou mocnou techniku: Možnost následných úprav vygenerovaného kódu v místě různých "extension points", jako jsou partial třídy nebo double-derived pattern (vygenerujeme base třídu s virtuálními metodami a vlastnostmi a prázdného partial potomka). Díky tomu náš DSL jazyk nemusí řešit všechny krajní varianty. Když to obrátíme můžeme říct, že pokud se rozhodnete váš DSL interpretovat až za běhu, musíte vaše řešení zcela popsat pomocí DSL - včetně všech výjimek a minoritních úkazů. Dostáváte se tak do "customizační pasti", kdy musíte napsat kompletní framework dřív, než budete moci váš DSL použít. Generovat kód je jednodušší, protože se stačí zaměřit na bežné jevy a speciální případy ošetřit v C# nebo jiném vhodném jazyce. Postupně můžeme přidávat počet situací, které jsou v našem DSL řešeny, nebo dokonce můžeme časem implementovat framework pro interpretaci za běhu, ale nebrání nám to začít v malém.

A nyní, milé děti, můžeme plně ocenit sílu language workbenches jako je DSL Tools pro Visual Studio: Jsou to externí jazyky, kde máme syntaxi plně pod kontrolou, máme k disposici parser a generátor, prvotřídní integraci do vývojového prostředí, a navíc grafický editor s předpřipraveným systémem validace modelu, který můžeme upravovat dle libosti. Programátorský Eden. Jediné co chybí je standardní způsob slučování více modelů do jednoho, ale to lze poměrně jednoduše obejít. O tom ale jindy.

sobota, března 14, 2009

Model Driven Development v praxi

Přeskočit přímo na video

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


sobota, února 07, 2009

O čem bych chtěl psát ale nemám na to čas

Od mého posledního ublognutí uplynulo mnoho LOC. Není to tím že bych neměl o čem psát, ale nějak se mi nedostává času. Tak alespoň ve stručnosti, na čem pracuju a o čem bych si v budoucnu chtěl ublognout:


  • Codename Ginger: Aplikační framework pro tvorbu distribuovaných (nejen) webových aplikací. Řeší architektonický základ takové aplikace: rozdělení do vrstev a vrstev (v češtině se dost špatně rozlišuje layer a tier), jazyk pro modelování domény vytvořený pomocí DSL Tools, generování různých artefaktů jako skriptů pro vytvoření databáze, mapovacích souborů, doménových a DTO objektů přímo z modelu pomocí T4. Není to žádná chiméra, máme reálně fungující a solidně protestovanou verzi a vyvíjíme v tom komerční aplikace. Na webové vrstvě používáme ASP.NET MVC, na datové NHibernate a trochu jsme to okořenili Spring.Net (ale jen s citem). Velkou inspirací pro nás je NServiceBus a pak samozřejmě bible enterprise aplikací Patterns of EAA od Martina Fowlera.


  • WPF: Celkem dlouho jsem tuto technologii ignoroval, protože jsem někde četl že v ní není DataGrid. Hrozná chyba! Příliš mnoho času jsem strávil ve Windows Forms, takže jsem si nedokázal představit, jak flexibilní může být UI framework. Teď si to vynahrazuju. Jsem nadšen ze stylování, data bindingu a content modelu. Kromě toho, že v tom nejdou kloudně zobrazovat bitmapy, je to naprosté programátorské blaho.


  • Scrum: Snažím se v práci prosadit, abychom v příští verzi našeho produktu nasadili tuto metodologii. Máme trochu problém s plánováním, termíny a komunikací, a od Scrumu si slibuju, že budeme mít přesnější představu jak na tom jsme, zpřesníme plánování, díky projektovým tymům usnadníme komunikaci, vývojáři se budou moct lépe soustředit na výsledek a díky tomu také zvedneme kvalitu našeho kódu. Nehledě na to, že nás to bude víc bavit:-) Teorie je na webu dost, ale našel jsem super knížku, která se věnuje praktické realizaci. Je zdarma, tak neváhejte a stahujte, je opravdu dobrá. Doufám že to vyjde a že budu moct časem ublognout jak nám to jde.




Tak to je ve zkratce všechno, mám v hlavě sice daleko víc nápadů, ale den má jen 24 hodin:-)