Klávesové zkratky na tomto webu - rozšířené Na obsah stránky

Jeden balíček na všechno

07.06 - 20. března 2013 | Moje práce

„Jak to vypadá s tím balíčkem pro Honzu?“ „V pohodě, v podstatě je připravenej, jen tam přidám, aby se generovala rovnou i databáze a nainstalovala windows service…“ Tak určitě. Já vůl. Prej jen…

Nakonec to bylo o dost těžší než jsem čekal. Scénář, který jsem si představoval, zdál se být chvíli i nemožný. Ale vždyť to musí jít! Wizard to dokáže, tak proč bych to neměl dokázat já? Zakopanej pes byl nakonec v neštědře zdokumentované featůře.

Již pár let používám k deployování webových aplikací MsDeploy (aka Web Deployment Tools). Většinou je peklo jen s tím, nastavit cílový server, aby vše běhalo jak má. Tedy hlavně nastavení nejrůznějších práv. To už se naštěstí za tu dobu změnilo a stačí jen spustit pár PowerShellových scriptů. Mám automatizovaný deploy jak webu, databáze, tak i instalaci windows služeb. Jinak mi to ani moje závislost na Continuous Delivery nedovolí.

Jenže jedna věc je mít sadu scriptů pro stroj a druhá věc je mít instalační balíček, který je bez problémů schopný nasadit člověk. Ano, mohl by dostat sadu scriptů a hromadu souborů, se kterýma ty scripty pracují, ale potenciální možnost zanesení chyby je vysoká. A komfort velmi nízký.

První pokus

Nejprve jsem se snažil nasynchronizovat jednotlivé kroky do cílového balíčku. Nejprve jsem přes MsBuild /t:Package vytvořil balíček s webem. Databázové schema verzujeme pomocí databázového projektu ve Visual Studiu a tak pro deploy používám již zastaralý dbSqlPackage provider. Tak jsem se ho snažil syncnout do balíčku. Povedlo se, ale manifest v balíčku obsahoval, jen deploy databáze. Uáááá. Už jsem pomalu propadal beznaději a přemýšlel o tom, jak napíšu uživatelsky přívětivý script. To aby UX nepřišlo na úbytě.

Popravdě, koho z vás by napadlo, že tohleto může vést k cíli? Mně to trvalo asi hodinu. A docvaklo mi to až díky tomuhle nešťastníkovi.

Jak na to?

Základem je nevytvářet balíček rovnou, ale vytvořit si nejprve manifest, který obsahuje jednotlivé kroky deploymentu. Ten můj vypadá nějak tak:

<sitemanifest>
  <dbDacFx path='$dbSource' />
  <runCommand path='$presync' waitInterval='30000'/>
  <dirPath path='$winSource' />
  <runCommand path='$postsync' waitInterval='30000'/>
  <IisApp path='$webSource' managedRuntimeVersion='v4.0'/>
  <setAcl path='$webSource' setAclResourceType='Directory'/>
  <setAcl path='$webSource' setAclResourceType='Directory' setAclUser='anonymousAuthenticationUser'/>
</sitemanifest>

Můžete si povšimnout, že je parametrizovaný. To proto, že ho generuju v build scriptu pro konkrétní implementační projekt.

$dbSource je cesta k dacpac souboru, který je výstupem databázového projektu.
$winSource je cesta k bin adresáři windows služby.
$webSource je cesta k PackageTmp adresáři, kde je připravená webová aplikace.

A konečně $presync a $postsync jsou cesty k vygenerovaným cmd souborům, které se spustí na cílovém stroji. PreSync zastaví a odinstaluje již běžící službu:

net stop Svc_$instance
$installUtil /u /name=Svc_$instance $destPath\$instance\Svc.exe

PostSync nainstaluje novou verzi a tu následně spustí:

$installUtil /name=Svc_$instance $destPath\$instance\Svc.exe
net start Svc_$instance

Opět jsou generované scriptem pro konkrétní instance.

Tím mám připravený manifest a všechno, co potřebuju pro úspěšné vytvoření balíčku. Nyní už jen stačí balíček vygenerovat:

$source = "manifest=$manifest"
$dest = "package=$pkgLocation"

exec { msdeploy -verb:sync -source:$source -dest:$dest }

Kde $manifest je cesta k vygenerovanému manifestu a $pkgLocation je cesta, kde najdu vygenerovaný balíček. No a to je celá magie. Ani to moc nebolelo, když už se ví jak na to. :)

Teď už jen zbývá umožnit Honzovi vyplnit konfigurační údaje pro konkrétní servery…

Funkcionální přístup ke kolekcím

07.34 - 7. března 2013 | Moje práce

Když vidíte začátečníka pracovat s kolekcemi, často se setkáte s tím, že možností první volby je iterace pomocí cyklů a změna stavu. Díky tomu máme spoustu rozbitých aplikací. Ruku na srdce, kdo z vás nemá ve své codebase něco podobného:

var list = new List();
foreach (var item in someCollection) {
  // ... some work
  list.Add(item);
}
return list;

Nádhera! :)

Zapomeňte na cykly a mutace, kdo se v tom má vyznat a hledat chyby? Buďte deklarativní.

Disclaimer

Následující řádky používají techniku obvyklou ve funkcionálních jazycích – rekurzi. Je dost možné, že váš jazyk nemá podporu na úrovni kompilátoru pro rekurzi (tail recursion se optimalizuje tak, že se přepíše na cyklus) a může docházet k tomu, že vám bude přetékat zásobník. V první řadě vždy musíte vědět, co děláte. Ale to platí obecně o všem v programování. C# optimalizovat rekurzi neumí, přesto můžete používat funkcionální koncepty, např. LINQ, který je implementovaný pomocí iterátorů. Následující ukázky budou v F#, který rekurzi optimalizovat umí.

Všechny ukázky si můžete vyzkoušet přímo ve vašem prohlížeči na webu Try F#. Jednotlivé ukázky stačí označit a stisknout Ctrl+Enter.

Článek vychází z Higher-order list operations.

Rekurze

Když zjistíte, že cykly ne, většinou přejdete na rekurzi. Častou chybou však je, že píšete příliš mnoho kódu. Proč? Protože vám třeba chybí znalosti o knihovnách nebo konstruktech jazyka, které nám umožní psát kódu méně. Pojďme si ukázat, jak psát kódu méně a jak některé knihovní funkce vlastně fungují.

Přičítání jedné

Máme za úkol napsat funkci, která projde seznam čísel a přičte ke každému prvku jedničku. V implementaci použijeme datovou strukturu spojový seznam (v C# spíše než LinkedList<T> použijeme ImmutableList<T> z NuGet balíčku Microsoft.Bcl.Immutable) a využijeme rekurze:

let rec add1 lst =
    if List.isEmpty lst then []
    else (List.head lst) + 1 :: add1 (List.tail lst)

add1 [1; 2; 3]

Že jde o rekurzivní funkci nám říká klíčové slovo rec. Funguje to tak, že vezmu první prvek seznamu a přičtu k němu jedničku, ten pak připojím k seznamu (operátor ::), který se spočítá obdobně ze zbytku vstupního seznamu. Pokud je seznam prázdný (nebo jsme došli na konec – každý spojový seznam končí prázdným polem), vrátíme prázdný seznam. Tím se nám rekurze ukončí.

Odčítání jedné

Obdobně naimplementujeme i odčítání:

let rec sub1 lst =
    if List.isEmpty lst then []
    else (List.head lst) - 1 :: sub1 (List.tail lst)

sub1 [1; 2; 3]

Ok, máme funkcionální datovou strukturu, máme rekurzi, je to už teda funkcionální kód?

Tak určitě ne.

Knihovní funkce

Další častou chybou je používání podmínek a ukecaného kódu vůbec. Navíc když se na obě funkce podíváme blíže, zjistíme, že to jsou v podstatě duplicity a jediné, co je odlišuje, je operace sčítání nebo odčítání. Když se oprostíme od implementačních detailů, zjistíme, že neděláme nic jiného než mapování. A tak jde kód přepsat do jednodušší podoby:

let inc x = x + 1
let dec x = x - 1

List.map inc [1; 2; 3]
List.map dec [1; 2; 3]

Nejprve jsme si definovali pomocné funkce pro inkrementaci a dekrementaci prvku a pak použili knihovní funkci List.map, která projde všechny prvky seznamu a na každý aplikuje předanou funkci (v C# je obdobou Enumerable.Select). Důležitým detailem, který nemusí být na první pohled zřejmý, je, že nedojde k úpravě předaného seznamu, ale k vytvoření seznamu nového.

High order function

Jak bychom takovou funkci map naimplementovali? Zkusíme to pomocí jednoduché generalizace našich funkcí add1 a sub1. Už jsme si říkali, že se liší jen v operaci přičtení/odečtení jedničky. Takže z našich funkcí vytvoříme funkci vyššího řádu:

let rec map f lst =
    if List.isEmpty lst then []
    else f(List.head lst)::map f (List.tail lst)

map inc [1 .. 3]

Funkce vyššího řádu se vyznačují tím, že jako parametr přijímají jinou funkci, která definuje, co se uvnitř bude dít (obdoba vzoru strategie z objektového světa). Funkce je takřka stejná jako naše konkrétní ukázky. Jediný rozdíl je, že nyní aplikujeme předanou funkci na první prvek seznamu a pak už je to stejný. Teď tedy máme funkci vyššího řádu, ale pořád je toho kódu zbytečně moc.

Pattern matching

Další skvělou vlastností mnoha funkcionálních jazyků je pattern matching – volba výsledku na základě strukturální podobnosti. Naši map funkci můžeme hezky zjednodušit na:

let rec mapMatch f lst =
    match lst with
    | [] -> []
    | head::tail -> f head::mapMatch f tail

mapMatch dec [1 .. 3]

Kód je de facto stejný jako v předchozím případě, jen nám zmizelo volání knihovních funkcí List.isEmpty, List.head a List.tail a místo nich máme vzory. Prázdný seznam se mapuje na prázdný seznam, seznam s nějakým prvkem se dle vzoru rozdělí na head a tail. Na head se aplikuje funkce a nový tail se vypočítá rekurzí.

List comprehension

Možná jste si všimli, že jsem v ukázkách najednou přestal psát [1; 2; 3;] pro vytvoření seznamu tří čísel, ale přešel k zápisu [1 .. 3]. Funkcionální jazyky mívají pro kolekce větší pochopení a tak nezvládají jen tupé vyjmenování prvků, ale rozumí i složitějším konstrukcím pro vytvoření seznamu (platí i pro pole a sekvence). Zde byl použit operátor rozsahu ... Ve skutečnosti můžeme jít mnohem dál a aplikovat třeba rovnou i mapování:

[for x in 1 .. 3 -> x + 1]

Výsledek je stejný jako v předchozích příkladech. Kód je minimální. Myšlenka zřejmá.

Filtrování kolekcí

Další možností, kde můžeme využít funkce vyššího řádu a pokusíme se zbavit podmínek, je filtrování kolekcí. Funkce filter vrací prvky ze vstupního pole, které splňují předaný predikát:

let even x = x % 2 = 0

let rec filter p lst =
    if List.isEmpty lst then []
    elif p (List.head lst) then List.head lst::filter p (List.tail lst)
    else filter p (List.tail lst)

filter even [1 .. 10]

Nejprve jsme si definovali predikát pro sudá čísla, který budeme používat i v dalších ukázkách, a pak funkci filter implementovanou pomocí podmínek a knihovních funkcí (F# má samozřejmě funkci List.filter již implementovanou, v C# ji hledejte pod názvem Enumerable.Where). Filtr je o něco složitější než mapování, protože se musíme rozhodnout, které prvky chceme a které ne. Ty prvky, které splňují predikát připojíme před zbytek počítaného seznamu. Pokud prvek predikát nesplňuje, pokračuje dál ve výpočtu seznamu. U prázdného seznamu ukončíme rekurzi.

S využitím pattern matchingu kód vypadá následovně:

let rec filterMatch p lst =
    match lst with
    | [] -> []
    | hd::tl when p hd -> hd::filterMatch p tl
    | hd::tl -> filterMatch p tl

filterMatch even [1 .. 10]

Pěkné, že? A ještě ukázka s list comprehension:

[for x in 1 .. 10 do if even x then yield x]

Všimněte si, že zde se nám šipka -> z ukázky pro mapování změnila na do ... yield. Schválně si to zkuste, že jde o stejný kód. Tady už bohužel kompaktní zápis nefunguje, protože tam potřebujeme vložit filtrovací podmínku.

Agregace kolekcí

Další častou operací, kterou se seznamy můžeme dělat, je jejich agregace. Ve funkcionálních jazycích se často můžeme potkat s funkcí fold (v C# Enumerable.Aggregate):

let rec foldr cons nil lst =
    match lst with
    | [] -> nil
    | hd::tl -> cons hd (foldr cons nil tl)

foldr (+) 0 [1 .. 4]

Parametr cons je konstrukční funkce výsledku, nil reprezentuje prázdný prvek.

A ještě tail recursive varianta pro efektivnější zpracování kompilátorem:

let rec foldl cons nil lst =
    match lst with
    | [] -> nil
    | hd::tl -> foldl cons (cons hd nil) tl

foldl (+) 0 [1 .. 4]

Zipování

Další velice důležitou funkcí je zip, která vezme dva seznamy a vytvoří z nich jeden tvořený dvojicemi, podobně jako zip u vaší bundy.

let rec zip lst1 lst2 =
    match lst1, lst2 with
    | [], [] -> []
    | hd1::tl1, hd2::tl2 -> (hd1, hd2)::zip tl1 tl2

zip [1 .. 3] ['a' .. 'c']

Žádný velký objev. :) Novinkou je matchování na více prvků a zavedení n-tic (tuples). Zip je velice šikovná funkce. Schválně jestli vás napadne nějaké rozumné užití. Nebojte se o něj podělit třeba v komentářích.

Také si všimněte, že tu mícháme jabka (int) s hruškama (char) a vše funguje. Není to tím, že by F# byl slabě typový, ale tím, že naše funkce jsou plně generické! Schválně si zkuste v předchozích ukázkách změnit vstupní data.

Rozepnutí

Opakem zipování je unzip, která nám ze seznamu dvojic udělá dva seznamy prvků. A protože nám tato funkce vrací dva seznamy, začíná to být trochu tricky:

let rec unzip lst =
    match lst with
    | [] -> ([], [])
    | (x, y)::tl ->
        let (xs, ys) = unzip tl
        (x::xs, y::ys)

unzip [(1, 'a'); (2, 'b'); (3, 'c')]

Taková nepěkná věc je, že si musíme začít pamatovat výsledek rekurzivního volání, se kterým dále pracujeme. Všimněte si také, že F# umí rozpadnout n-tici do samostatných chlívečků. Nebudu říkat proměnných, protože let vazba je neměnná.

Continuation

Co bychom to byly za funkcionáře, kdybychom nechali takové imperativní přiřazování v naší jinak čisté implementaci? Jak se jen zbavit přiřazení? Co třeba za pomocí continuation? Nic vám to neříká? Co třeba callback? Ten už jistě znáte. :)

let rec unzipk k lst =
    match lst with
    | [] -> k [] []
    | (x, y)::tl -> unzipk (fun xs ys -> k (x::xs) (y::ys)) tl

unzipk (fun xs ys -> xs) [(1, 'a'); (2, 'b'); (3, 'c')]

V této formě musí uživatel funkce přidat callback, ale na druhou stranu si může vybírat, co vlastně chce za výsledek. Funkcionální kompozice je mocná.

Partitioning

Partitioning je něco jako filtrování, jen nám opět vrací dva seznamy. Jeden s pozitivními výsledky a druhý s negativními:

let rec partition p lst =
    match lst with
    | [] -> ([], [])
    | hd::tl ->
        let (ins, outs) = partition p tl
        if p hd then (hd::ins, outs)
        else (ins, hd::outs)

partition even [1 .. 10]

Implementace je dost podobná unzipování, jen teď máme na vstupu pouze jeden prvek a rozhodujeme se, do kterého seznamu ho zařadíme. A opět můžeme implementaci udělat pomocí continuation:

let rec partitionk p k lst =
    match lst with
    | [] -> (k [] [])
    | hd::tl -> partitionk p (fun ins outs ->
                                if p hd then k (hd::ins) outs
                                else k ins (hd::outs)) tl

partitionk even (fun evens odds -> evens) [1 .. 10]

Závěr

Ukázali jsme si, jak si ušetřit spoustu práce pomocí funkcionálního přístupu ke kolekcím. Implementace jednotlivých funkcí je jen ukázkou, jak snadno se dají takové věci implementovat a rychlým průletem, co všechno F# umí. Sice jsme se jen sklouzli po povrchu, ale jako základ je to celkem slušný, ne? :)

Zkuste si pohrát se spustitelnými ukázkami. Zkuste mapování a filtrování implementovat pomocí continuation. Zkuste si třeba filter implementovat pomocí partition. Většina kombinátorů lze totiž implementovat pomocí pouhých tří základních, ale k tomu zase třeba jindy.

CodeRush - editor na steroidech

10.18 - 15. února 2013 | Moje práce

V čem musím s Borkem souhlasit je, že Visual Studio má opravdu debilní editor kódu. Jenže je to věc, kterou si uvědomuju pouze ve chvílích, kdy sedím před studiem, které nemá nainstalovaný CodeRush.

CodeRush toho podobně jako ořezávátko umí celkem hodně, ale v čem opravdu vyniká je editace kódu.

K Borkovým bodům:

  • Shift+Enter duplikuje řádky a navíc skočí na symbol, který je aktuálně v rozporu a je třeba ho upravit.
  • samotná reindentace je pro mě jen poloviční řešení, používám vždy reformát Ctrl+K,D, abych udržoval kód konzistentní s coding standardem.
  • na přesouvání řádku existuje plugin, nepoužívám.
  • Smart Copy, řeší odsazování i formátování, navíc má kontextovou citlivost. Když zkopíruju deklaraci fieldu a vložím ji do konstruktoru, vloží se jeho inicializace apod.
  • mazání řádků funguje přes Shift+Delete
  • Ctrl+click je značně neefektivní způsob navigace. VS umí F12 pro skok na deklaraci, CR přidává Tab pro navigaci po symbolech, Shift+F12 pro seznam všech referencí v solution a Ctrl+Alt+N pro kontextovou navigaci (potomci, předkové, implementace, přetížení…).
  • Na místo poslední editace mě přenese Esc (pakliže refactoring zanechal značku nebo jsem si značku vložil sám pomocí Alt+Home). Značky fungují na principu zásobníku. Přidávat lze i permanentní značky pod číslem, pakliže někam skáčete pravidelně. (Ctrl+Alt+číslo pro vložení značky, Atl+číslo pro skok na značku.
  • přehledy brzdí, důležitá je rychlá kontextová navigace. Ctrl+Alt+F pro soubory, Ctrl+Alt+. pro naposledy použité soubory a Ctrl+Shift+Q pro navigaci po memberech.

Něco na víc:

  • Alt+←/→ funguje podobně jako Ctrl+←/→ akorát i po jednotlivých slovech v camel/PascalCase symbolech. Stejně tak funguje Alt ve spojení s klávesami Shift a Delete pro výběr, či smazáni textu.
  • Ctrl+` pro refactoring.
  • F2 neskutečně rychlý inline Rename.
  • historie schránky
  • inteligentní šablony
  • smart semicolon
  • zen coding

…a spousta dalších věcí, ke kterým se dostanem třeba jindy.

Alessio Busta - Vesper

09.38 - 23. ledna 2013 | Muzika

Po delší době nějaká ta hudba k tanci i poslechu. Back to basics.

  1. Pink Floyd — Is There Anybody Out There?
  2. Ame — Rej
  3. Christian Smith — Cinnamon
  4. Oliver Huntemann — Decks & The City
  5. Marc Romboy & Stephan Bodzin — Atlas – Gui Boratto Remix
  6. Christian Smith — Flyertalk
  7. Cirez D — Teaser – Oliver Huntemann Remix
  8. Paperclip People — 4 My Pepz – Dubfire Rework
  9. Jesper Dahlback — Alright
  10. Sasha — Smoke Cone
  11. Udo Kier feat. Nicolette Krebitz — Prayer – Gui Boratto Mix

dj set Alessio Busta – Vesper

CI tip: Neočekávejte

12.11 - 3. ledna 2013 | Moje práce

Jednou z dobrých praktik v Continuous Integration je, nemít žádná očekávání. Repository musí obsahovat vše, co vývojář potřebuje k bezproblémovému vývoji. Ok, minimální subset jako OS a SDK předpokládat můžeme. Ale nic nám nebrání v tom, připravit script, který potřebná SDK a nástroje nainstaluje a nastaví.

Malý příklad z praxe

Vezměme si reálný scénář. V teamu máme lokální nuget server, kde jsou k dispozici balíčky s interními komponentami. Pro správný chod musí mít každý nastavenou adresu lokálního serveru ve zdrojích ve svém Visual Studiu, každý nový vývojář si to musí nastavit. Jinak se balíčky neaktualizují. Kde takovou informaci udržovat? Má být ve wiki, kterou nikdo nečte? Má být v readme souboru v repository? Nejlepší je najít automatické řešení, na které už nikdo nemusí myslet!

NuGet například umožňuje definovat vlastní zdroje v konfiguračním souboru, který se vztahuje k projektu. Jednou do repository přidáte tento soubor a máte vystaráno. Všichni kdo si otevřou projekt mají již vlastní zdroj balíčků nakonfigurovaný, aniž by museli něco řešit. A to jen pro aktuální projekt. Takže je to řešení nejen nevyžadující aktivitu, ale je i celkem čisté. Win win situace.

A navíc to funguje i na integračním serveru, protože ten je taky součástí teamu. ;)

Statické třídy

08.39 - 1. prosince 2012 | Moje práce

Často se setkávám s celkem nepěkným nešvarem, kterým je hrůzostrašné pojmenování statická třída. Tenhle problém se dotýká především C# vývojářů a trochu zasahuje i do Javy. Když se podíváte na na ASP.NET, tak tam to je jedna statická třída vedle druhý. Celý framework jsou nudle statických APIs a Singletonů. Co je na tom tak špatnýho?

Co je to statická třída?

Tak v první řadě je to blbost. Statická třída je špatné pojmenování pro koncept obecně známý jako modul. A nemusíme chodit zrovna daleko. Naši nechtění bratia z VB.NET tento koncept mají pojmenovaný správně. ;) Modul by ideálně měl být souborem čistých funkcí. V ideálním (funkcionálním) světě bez vedlejších efektů ale nežijeme a tak dochází k ovlivňování vnějšího světa a v modulu máme i nějaké ty procedury. S tím se ještě dá žít.

Singleton

Problém nastává, když začneme do našich modulů cpát stav, tak jak jsme zvyklí u objektů. Tento koncept máme také již dávno zmapovaný a pojmenovaný. Ano, jde o debilní (nechtěl jsme psát naivní) implementaci Singletonu. Například Scala má pro singleton podporu přímo na úrovni jazyka. Jenže na rozdíl od statické třídy může implementovat rozhraní. Tudíž je to celkem dobrý koncept slučitelný s OO principy. Ne však statická třída.

Statická třída se nechová polymorfně (aka dynamic dispatch). Statická třída nemůže implementovat rozhraní. Statická třída dost často brání konkurenci a je těžké u nich dosáhnout mezivláknové bezpečnosti. Takže statickou třídu jako náhradu singletonu nebrat! :)

Mixin

Další možné uplatnění statické třídy je definice extenzních metod. Pokud jsou extenze opravdu pouze rozšířením rozhraní pomocí čistých funkcí, vše je ok. Ale netahejte tam žádný stav, jinak se vracíme zpátky k předchozímu bodu, a hledání takových chyb je snad ještě vypečenější.

Fluent API

Statické třídy můžeme využívat jako syntaktické vstupní body pro plynulá rozhraní. Jde jen o syntaktický cukr v DSL. Pokud jsou pocukrovaná API dostupná i běžnými OO přístupy, pak je vše ok. Ale bacha ať nedopadnete jako ASP.NET, kde alternativy neexistují a pak jste pěkně v…

Modul

Posledním užitím, které mě teď napadá (protože mám slabou představivost na zrůdnosti), je již na začátku zmiňovaný modul. Z pohledu funkcionálního přístupu je modul soubor funkcí, které operují nad stejnými datovými strukturami. V lepších jazycích se dají moduly otvírat a pak můžeme jejich funkce používat jako kostrukty jazyka. Ale to bychom po C# chtěli už moc. Vlatně proč? Vžyť to umí i Java. ;)

Závěr

static class je jen implementační detail několika různých konstruktů a proto by se v našem vyjadřování tento pojem neměl často vyskytovat. Raději zmiňujte koncept, který máte konkrétně na mysli. Budou vám rozumět jak programátoři z jiných jazyků, tak bude i snadněji pochopitelné o co konkrétně vám jde. A nezapomeňte statická třída není typ a už vůbec ne objekt. ;)

Technologický radar

10.07 - 31. října 2012 | Moje práce

Miro se na devblogu trošku rozepsal o tom, co ho zaujalo v poslední vydání Technology Radaru a na závěr vyzval k reakci. Tož tady je ta má.

Technology radar sleduji již nějaký ten rok. Srovnávám si sním svůj pohled na to, co vypadá perspektivně a co ne. Často se dost shodujeme, občas rozcházíme. Ale takový je život. Já tu vypíchnu věci, který mě překvapily nebo zaskočily.

Nědělejte feature banching

S tímhle se ztotožňuji a preferuji chytřejší návrh, který umožňuje pružné nasazování featur změnou konfigurace a podle potřeby i za běhu. U služeb nebo produktů se tedy branchování vyhýbejte. Ale jak už to tak bývá, existují výjimky, kde se tato praktika naopak hodí. Jednou z nich jsou pull-requesty do OSS. Zde patří k etice odklonovat, odbranchovat a pak až dělat změny.

Micro služby

Tahle technika souvisí s předchozím bodem na úrovni komponent. Místo komponent máme malé nezávislé služby, které spolu spolupracují. Umožňuje to rozpadnout velké aplikace na dílčí služby, které se samy o sobě lépe udržují i vyvýjí. Zajímavé je, že ESB je naproti tomu v hold. Z mého pohledu micro služby s ESB souvisí a nebo jsem jen zatim nenašel lepší způsob provázání micro služeb.

Automatizace vývojářské infrastruktury

Tohle je technika, kterou mi přineslo Continuous delivery. Na Webmiu jsme ji celkem slušně prověřili a dneska si bez ní nedovedu představit, že bych mohl pracovat. Rozhodně ne efektivně. Což pro změnu zase souvisí s následujícím bo­dem:

Rake pro automatizaci Java i .net

S tímhle bodem bych byl schopnej souhlasit u projektu, kde je zapotřebí buildit i na jiných platformách, než jsou Windows. Jako mackař, bych občas uvítal, možnost dělat všechno v MacOS a nemuset do Parallels. Nadruhou stranu mi syntaxe Psake a fakt, že má za sebou kompletní PowerShell, vyhovuje tak, že to jsem schopný skousnout. :)

Takže tady se držím Psake, s kterým automatizuju nejem integrační build, ale i vývojářskou infrastrukturu. Jednoduchá syntaxe, mocný shell, snadná dokumentace scriptu, značná vyspělost tohoto nástroje a dostupnost na všech windows jsou pro mě jednoznačná plus.

Angular a Knockout

U mě jasně hraje prim AngularJS. Můj přístup k vývoji aplikací je s ním velmi kompatibilní. Naproti tomu KO vychází ze zmetku MVVM, který vychází z nepochopení MVC/MVP. Dále je vidět, že autor je webem nepolíbený a všechno co dělá to akorát zdůrazňuje. Viz neprefixovaný data-bind atribut, syntaxe v něm použitá, vlastní „elementy“ definované pomocí komentářů a další podobná zvěrstva. Jo, jsou to detaily, ale pro ortodoxního webaře nepřekonatelný. :)

Meteor.js a Dart

Meteor je v sekci hold a s tím souhlasím. Zatím tento framework ve mně vyvolává spíš rozpaky než nadšení. Dart se taky drží v holdu. Mě Dart zajímá z pohledu language designu a iženýringu, proč, co a jak dělaj. Ale že bych ho v následujích letech měl používat si nedovedu představit…

F#

Bohužel F# se na radaru moc nepohnul (stále trial) a myslím, že je to škoda. Doufám, že se to do příště změní, přesto, že Microsoft má F# spíš jako inkubátor zajímavých věcí (generics, type inference, async, type providers, immutable), který pak flákne do mainstreamových jazyků jako C# a VB. Mnozí si myslí, že jeho budoucnost a případné rozšíření je na Monu. MonoDevelop už má stabilní language binging, který si člověk nemusí ručně buildit. Mono má fsc i fsi v základní distribuci.

F# je čistý jazyk, ve kterém jdou složité koncepty vyjadřovat jednoduše. Netrpí obskurní alegorickou dekorativností a při blbé inicializaci fieldu přes kontruktor nemusím psát třikrát stejný kód (sice ho za mě píše CodeRush, ale pořád je ve zdrojácích). V práci pořád C#, ale osobní projekty rozhodně v F#.

NuGet

Na předchozím projektu jsme NuGet používali pouze pro správu externích závislostí. Aktuálně ho používám i interně pro balíčkování komponent a tvorbu různých konfigurací produktu. K tomuhle tématu se snad ještě vrátím v samostatném spo­tu.

Konfigurace v DNS

Na tohle se musim podívat blíž. Mám tušení, že to brzy použiju a snad se k tomu vrátím i tady na blogu. :)

Závěr

Tak to je můj zkrácený pohled na technologický radar. Jak ho vidíte vy?

DIP, IoC a DI - díl třetí

21.20 - 26. října 2012 | Moje práce

Vidíte, vidíte? Máme za sebou dva krátké spoty plné nezodpovězených otázek. Nahlodaly vás aspoň trošku? Vyřešili jste některé? Dobře. Pojďme dál.

V minulém díle jsme se zbavili závislosti na konkrétní implementaci repository a zavedli tak Inversion of Control (ok, taky zjednodušuju, ale základní rozdíl IoC od DI je v abstrakci). Ovšem pořád jsme nedosáhli DIP. „Jak to,“ ptáte se? No důvod je celkem prostý. Tu největší chybu jsme udělali hned na začátku. Začali jsme datovou vrstvou!

Všimněte si, že naše doménová vrstva má stále závislost na té datové, i když jsme zavedli abstrakci a jakoby neznáme konkrétní implementaci. Problém je v tom, že abstrakce nepatří do vrstvy, kde je implementována. IRepository<TEntity> nepatří do MyAwesomeApp.Data, ale do „business layeru“. Kdybychom dělali top-down design, samozřejmě by nám to z toho vylezlo samo. Ale protože jsme byli chytřejší a stavěli „od spoda“ (bottom-up), dostali jsme se celkem do potíží…

Takže, když uděláme tuhle malou změnu, tedy že přesuneme IRepository<TEntity> z data do business layeru, otočíme závislosti. Nyní, už doménová vrstva nezávisí na datové, ale naopak. Datová vrstva se nám stala plug-inem. A konečně máme i DIP!

Top-down design

Ještě bych se rád zastavil u jedné konfůznosti a tou je top-down design. Tj. design kdy začneme od shora a pokračujeme dolu. Jenže kde je to nahoře? Když si vybavíte běžná schémata tradiční třívrstvé aplikace, tak nahoře máte UI vrstvu, pod ní business vrstvu a pod ní vrstvu datovou. Tak je to jasný, ne? Top-down design začneme od UI vrstvy! Rovnou si dám facku, že jsem to vůbec napsal. plesk

Tak zase špatně. Ve skutečnosti je nahoře vrstva s největší abstrakcí. Což by měla být asi ta doménová vrstva. :) UI i data jsou detaily, hodně konkrétní! Dneska máme běžné, že jedna a ta samá business vrstva žije za obrázkama na iPhonu, Androidu, Windows Phonu, desktopových i webových aplikací. Ano, ta samá. protože BL je to, co dělá vaši aplikaci aplikací. Je jedno, na jakým systému běží, jak přistupuje k datům, jakým způsobem data prezentuje.

Abstrakce nám pomáhá odkládat řešení detailů do doby, kdy to bude nezbytné. Pomáhá nám být agilní a umožňuje nám pružně reagovat na změny požadavků. A změna je jedinou jistotou, na kterou se můžeme v naší branži spolehnout. Žádné smlouvy vás před ní neochrání. A nebraňme se ji, však jsme přece vývojáři.

DIP, IoC a DI - díl druhý

14.47 - 26. října 2012 | Moje práce

V minulém díle jsem si začal pohrávat s myšlenkou, že to, jak nedokážeme sdělovat to, co chceme říct, ovlivňuje schopnost chápat těch, kterým myšlenky směřujeme. Řešením je jistě dialog, ve kterém se snažíme dobrat k podstatě sdělení, ale v kódu a asi i na blogu dialog nějak pokulhává.

Minule jsme se snažil naznačit, že i když se snažíme dodržovat domněle správné postupy a poučky, tak se stejně dostaneme do potíží. Čím to je? Vždyť nám to šlo tak hezky, proč to bylo špatně?

Snažil jsem se také dokázat, že dependency injection nemá nic společnýho s DIP nebo IoC. Doufám, že vám v mnohých případech zatrnulo. Takže jsme ve stavu, kdy máme v aplikaci DI, ale po DIP nebo IoC ani památky. Teď se vrátím k poslednímu kroku, kde jsme zavedli závislost doménového modelu na datové vrstvě.

Všimněte si, že vrstva, která by měla mít vyšší úroveň abstrakce si vytváří závislost na celkem konkrétní datové vrstvě. Když bychom aplikovali IoC, museli bychom změnit konstruktor našeho publikačního manažera na něco takového: PublishingManager(IRepository<Article> repository). Tím jsme zavedli závislost na abstrakci (všimněte si, že nám nějak zmizely konkrétní dotazy z ArticleRepo­sitory), naše třída už nemá znalost, jaká implementace repository bude za běhu doručena. Ale stále jsme nezavedli DIP!

Vždyť DIP je obecný princip. IoC je přeci vzor, podle kterého se DIP dosáhne a DI je jeho konkrétní implementací. Nebo to dost často někde čtu nebo slýchám. Jak to, že teda mám zavedenou DI, mám zavedený IoC, ale po DIP ani památky? Že by to všechno bylo jinak?

DIP, IoC a DI - díl první

12.54 - 26. října 2012 | Moje práce

Zmatení pojmů je úžasná věc. Něco si myslíte a tak řeknete něco, co vás napadne jako první. A většinou je to blbost. Ale lidi se to naučili tolerovat, protože stejný blbosti děláme všichni. Nakonec si nikdo nerozumí, ale všichni dělaj, jako že jo. Nechtěj vypadat jako blbci.

Disclaimer: Článek si neklade za cíl se do někoho navážet, někoho ranit nebo zesměšňovat. Klade si za cíl otevřít otázky nad obecně přijímanými pravdami.

Dependency Inversion Principle, Inversion of Control a Dependency Injection mají tolik vzájemně společných slov, že musí být i blbci jasné, že spolu úzce souvisí. A přitom je to blbost. Existují jistá místa, kde se můžou vzájemně dotýkat, ale vůbec to tak nemusí být.

Začněme jednoduchým příkladem z praxe. Běžně začneme tak, že si vytvoříme rozhraní pro repository IRepository<TEntity> a prdneme ho do projektu/namespace MyAwesomeApp.Data. To je jasný, repository přece patří do datový vrstvy! Ne asi. Pak si uděláme konkrétní implementace jako MyAwesomeApp.Data.ArticleRepository : IRepository<Article> a podobně. Každá si v konstruktoru vytvoří DataContext, který se nějak nakonfiguruje. Ten pak obalíme svejma metodama a spokojeně rozšiřujeme rozhraní třídy s každým dalším dotazem…

Pak si někde přečteme, že testovatelnost, DI a tak, a tak teda uděláme přetížení konstruktoru, který bude brát DataContext odněkud z venku, ale abychom nechodili moc daleko, to venku bude původní přetížení, který jen zavolá tohle nové. Poor men's DI ve své nejčistší formě.

Pro jistotu se zeptám: Vidíte tu nějaký Inversion of Control nebo uplatnění Dependency Inversion Principle? Houby s octem! DI s nimi tedy určitě nesouvisí. Pokud vám někdo tvrdí, že IoC je obecný princip a DI je jeho konkrétní implementace, neříká pravdu. :)

Ok, pokračujeme dál. Vytvoříme si ten doménovej model. Vlastně to jsou jedna k jedný entity z databáze, ale who cares? Je třeba si z checklistu odškrtnout další zkratku začínající na D. Taková malá úlitba OO bohům. Na to si vytvoříme novej projekt MyAwesomeApp.BusinessLogic, aby bylo jasný, že děláme ten business a má to nějakou logiku. Sem krom entit z databáze nasereme taky všemožný statický helpery a podobně, co zrovna budeme potřebovat znovu použít. No a protože některý helpery budou potřebovat naše entity ukládat do databáze a taky se na ně dotazovat, tak z nich uděláme teda instanční třídy a necháme si nainjektovat nějaký repository. Třeba tak: PublishingManager(ArticleRepository repository).

Držíme se dependency injection. No a proto, aby nám vůbec šla ta repository nainjectovat, přidáme referenci na naši datovou vrstvu. Tak určitě. Jak jinak byste to chtěli přece řešit?