Daniel Kolman

Jak jsem přešel na Linux a MonoDevelop

| 3 comments |

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.

Čtyři smartfouni

| 2 comments |

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.

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

| 18 comments |

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?

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

| 0 comments |

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.

Architektura Ginger

| 2 comments |

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.

Trochu teorie kolem DSL

| 1 comments |

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.

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

| 0 comments |

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:-)