tag:blogger.com,1999:blog-30587260841451902582024-02-20T21:30:58.792+01:00Daniel KolmanDaniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.comBlogger54125tag:blogger.com,1999:blog-3058726084145190258.post-3425003141830706932015-12-14T07:01:00.000+01:002015-12-14T07:01:04.141+01:00Funkcionální JavaScript - Rekurze s ocasem<p>Rekurze patří mezi základní techniky funkcionálního programování, protože umožňuje velmi hutně a stručně vyjádřit, čeho chceme dosáhnout, a to bez použití cyklů. Mezi programátory má ale velmi špatnou pověst, protože je velmi jednoduché "vyrobit" stack overflow. Není to však vždy pravda. V moderních jazycích (jako je v JavaScript) může mít rekurze ocas a pak je stejně efektivní jako cyklus. Ne, "ocas" není žádná zkratka z angličtiny ani hipsterská šifra, je to prostě normální český vocas, oháňka, ohon, chvost.
</p>
<a name='more'></a>
<h3>Rekurze je deklarativní</h3>
<p>Rekurzivně napsané algoritmy jsou velmi deklarativní a připomínají matematické vzorce. Například, faktoriál pro n spočítáme pomocí vzorce:
</p>
<pre>
0! = 1
n! = n * (n-1)!
</pre>
<p>
Což můžeme přímo přepsat pomocí rekurzivní funkce:
</p>
<script src="https://gist.github.com/kolman/4460dde27b7d50639dc5.js"></script>
<p>JavaScript bohužel nemá pattern matching, a tak je nutné nějak přidat podmínku pro ukončení rekurze a zápis není tak hezky deklarativní jako v jazycích, které pattern matching mají. Nicméně i tak je zápis daleko blíže matematickému vyjádření než for/while cyklus.</p>
<p>Pomocí rekurze můžeme jednoduše zapsat celou řadu užitečných funkcí pro operace s kolekcemi. Trik je rozdělit kolekci pomocí destructuringu na první prvek <span class="code">x</span> (head) a zbytek <span class="code">xs</span> (tail) a definovat co se má stát s jedním prvkem a rekurzivně to samé aplikovat na zbytek kolekce:</p>
<script src="https://gist.github.com/kolman/449a929432c2c9e84bbc.js"></script>
<p>Všimněte si, jak moc užitečný je destructuring a spread operátor, kterým můžeme spojovat pole a prvky bez pomocných funkcí (např. <span class="code">[...reverse(xs), x]</span>). Pokud destructuring a spread neznáte, mrkněte třeba na <a href="https://babeljs.io/docs/learn-es2015/#destructuring">dokumentaci Babelu</a>, bude se vám to určitě hodit.</p>
<p>Dokonce se tak dá napsat i quicksort, což je super, protože tím se vždycky vytahovali Haskellisti, že to umí napsat na pět řádků, tak teď jim to můžeme natřít, že to umíme taky a ještě to poběží ve všech <i>devajsech</i> na světě, heč.</p>
<script src="https://gist.github.com/kolman/3e7204a79161daee92a6.js"></script>
<a name="ocas"></a><h3>A teď ten slíbený ocas</h3>
<p>Problém všech výše uvedených příkladů je, že rekurze s každou iterací konzumuje stack. Pro malé kolekce to bude bez problémů fungovat, ale když pak v produkčním prostředí přijdou tisíce řádků, může dojít ke stack overflow.</p>
<p>Jenže. Co kdybychom funkci factorial přepsali tak, aby návratová hodnota rekurzivního volání byla zároveň návratovou hodnotou funkce samotné?</p>
<script src="https://gist.github.com/kolman/eb1244c17d28386da17f.js"></script>
<p>Výsledekem volání <span class="code">factorial(69)</span> je teď volání <span class="code">factorial(68, 69 * accumulator)</span>. Jinými slovy, výsledek <span class="code">factorial(68, 69 * accumulator)</span> se už nijak dál nezpracovává, jen se vrátí. A tomu se říká <i>tail position</i>, neboli <i>poloha ocasu</i> (a proto my puberťáci radši používáme angličtinu).</p>
<p>Pokud je rekurzivní volání v <i>tail position</i>, vlastně už není nutné držet stack pro každou iteraci, protože se s ním po návratu z rekurze stejně nic nedělá. A proto mají některé chytré jazyky kompilátory a interprety, které dokáží automaticky rozeznat tail position a fakticky převést rekurzi na cyklus. Jmenuje se to <i>tail call optimization</i>, neboli <i>optimalizace ocasu</i> (hahaha hehehe). A ES2015 je chytrý jazyk (jak jinak)! A Babel umí <a href="https://babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&code=function%20factorial(n%2C%20accumulator%20%3D%201)%20%7B%0A%20%20return%20n%20%3D%3D%3D%200%20%0A%20%20%20%20%3F%20accumulator%20%0A%20%20%20%20%3A%20factorial(n%20-%201%2C%20n%20*%20accumulator)%3B%0A%7D">převést rekurzi na cyklus</a> už dnes:</p>
<script src="https://gist.github.com/kolman/fc72aeab8672b5b682fa.js"></script>
<p>Původní verze funkce <span class="code">factorial</span> není tail recursion proto, že se výsledek volání <span class="code">factorial(n - 1)</span> ještě před vrácením násobí n.</p>
<p>Hmm takže aby byla rekurze efektivní, musíme všechny příklady nahoře přepsat tak, aby měly <span class="code">accumulator</span>? To je ale pěknej opruz! Navíc se dost ztrácí hutnost a čitelnost. Co s tím?</p>
<h3>Reduce all the lists!</h3>
<p>Špatná zpráva je: Ano, aby byla rekurze efektivní a šla automaticky optimalizovat, musíme výše uvedené příklady přepsat. Dobrá zpráva je, že je tu jedna operace s kolekcemi, která má akumulaci takříkajíc v krvi a navíc se jedná o tail recursion:</p>
<script src="https://gist.github.com/kolman/0087e7df607871a621f7.js"></script>
<p>Tato operace je navíc tak obecná, že většinu ostatních operací s kolekcemi lze přepsat pomocí reduce, a díky tomu i ony budou využívat tail call optimization:</p>
<script src="https://gist.github.com/kolman/52d58ec7991178b04d06.js"></script>
<p>(Když říkám "většinu" operací, všimněte si že třeba pro take(n) to nedává smysl. Nemá smysl iterovat přes všechny prvky kolekce (jak to dělá reduce), když vás zajímá prvních n prvků.)</p>
<h3>Vtip dne</h3>
<p>Implementace tail recursion se v různých jazycích liší. Javistům jsme se posmívali <a href="http://blog.kolman.cz/2015/12/funkcionalni-dobrodruzstvi-v-javascriptu.html">minule</a>, takže teď je na řadě jediný možný způsob tail recursion v Pythonu:</p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj23FCoRTMqe-YnFsLY97KPUskCKkPZKiomujjiW32UoQc-YbvsDuYwWZdaaPImGtOoz3ouU6Mi98z6ZOn_uQU7mIaB5tEH-pplEQahVAN57aZXagrDiQaW-GR9cvziOVeZfPR_nys4i4Y/s1600/python-tail-recursion.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj23FCoRTMqe-YnFsLY97KPUskCKkPZKiomujjiW32UoQc-YbvsDuYwWZdaaPImGtOoz3ouU6Mi98z6ZOn_uQU7mIaB5tEH-pplEQahVAN57aZXagrDiQaW-GR9cvziOVeZfPR_nys4i4Y/s320/python-tail-recursion.jpg" /></a>
<p>Tail recursion je navíc děsně vtipná sama o sobě. Akorát musíte nastudovat poměrně <a href="http://www.explainxkcd.com/wiki/index.php/1270:_Functional">dlouhé vysvětlení.</a></p>
<p>A pokud to pořád ještě nechápete, <a href="#ocas">klikněte zde</a>.</p>
Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com0tag:blogger.com,1999:blog-3058726084145190258.post-23081277450135756022015-12-12T08:50:00.000+01:002015-12-12T08:51:46.253+01:00Funkcionální dobrodružství v JavaScriptu<p>Funkcionální programování je boží, bohužel tady prostě zatím nemá vybudouvanou tradici. Asi proto, že tu lidi málo <strike>hu..</strike> studujou funkcionální jazyky. S příchodem ES2015 alias ES6 alias JavaScriptu budoucnosti se ale FP konečně může dostat do mainstreamu, a tak se nabízí otázky: Co všechno ES2015 nabízí? Kam až nás může ES2015 posunout? Jak by vypadal JavaScript, kdybychom ho psali striktně funkcionálně? Pohodlně se usaďte, otevřte si popcorn a oblíbenou láhev, začínáme! Vítejte v budoucnosti JavaScriptu, v roce 2015!
</p>
<a name='more'></a>
<h3>Currying</h3>
<p>Nechci vás hned na začátku vystrašit definicí a tak si rovnou ukažme příklad ve "starém" JavaScriptu (za starý, nemoderní a beznadějně zastaralý JavaScript budeme pro účely tohoto článku považovat jakýkoliv kód napsaný od velkého třesku do roku 2014):
</p>
<script src="https://gist.github.com/kolman/adae597d07a8ab395f79.js"></script>
<p>Jak je vidět, currying je částečná aplikace funkce, vlastně takové přednastavení parametrů funkce. Tento koncept není v JavaScriptu nový, například Lodash má na to funkci <a href="http://devdocs.io/lodash/index#partial">partial</a>, a dokonce poskytuje wrapper <a href="http://devdocs.io/lodash/index#curry">curry</a>, kterým obalíte funkci a ona se vám bude auto-curryovat sama:
</p>
<script src="https://gist.github.com/kolman/02d4d6e012f413cd8f57.js"></script>
<p>V ES2015 ovšem můžeme dosáhnout podobného výsledku přímo, bez knihovny. Musíme ale použít trik, kdy všechny parametry funkce zapíšeme pomocí <i>fat arrow</i>:
</p>
<script src="https://gist.github.com/kolman/643c1d40d713067403ca.js"></script>
<p>Na rozdíl od "plain old" funkce je nutné použít pro každý parametr extra závorky (<span class="code">sum(1)(2)</span>), za to ale získáváme automatický currying bez pomocných funkcí a knihoven! Pokud vám ty závorky navíc vadí, aspoň už chápete, proč v některých jazycích nejsou potřeba, třeba v <a href="http://learnyouahaskell.com/starting-out#babys-first-functions">Haskell</a>u. Mimochodem, celý tento post a kód v něm byl inspirován podobností zápisu <span class="code">x => y => x + y</span> a signaturou funkce v Haskellu, která by v tomto případě vypadala:
</p>
<script src="https://gist.github.com/kolman/331dce52253e432857a3.js"></script>
<p>První řádek definuje typ funkce sum a můžete ho číst takto: <span class="code">sum</span> je funkce, která bere Int a vrací funkci, která také bere Int a vrací Int. No a stejně můžete číst zápis v JavaScriptu:
</p>
<script src="https://gist.github.com/kolman/6823f353a6899f15dea1.js"></script>
<p>Samozřejmě, zapsat to jde i ve starém JavaScriptu, ale postrádá to eleganci Haskellu (teď mě právě všichni skalní Haskellisti odepsali).</p>
<h3>A komu tím prospějete co?</h3>
<p>Pokud vám doteď připadá že je to jen akademické cvičení bez praktického významu, pak se mýlíte! Currying je velmi praktický například pro <a href="https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750#.8v9xtylue">Higher-Order komponenty</a> v Reactu. Pokud je budete psát jako dekorátory (podobně funguje funkce <span class="code">connect</span> v react-redux), můžete je krásně poskládat a aplikovat na komponentu:
</p>
<script src="https://gist.github.com/kolman/b2c6d7379305fd41493a.js"></script>
<p>Dekorátory tříd se možná stanou součástí specifikace ES7, ale k čemu potřebujete zápis se zavináčem a klíčové slovo <span class="code">class</span>, když můžete stejného výsledku dosáhnout jednoduchou funkcionální kompozicí? Nemusíte přece být takoví hipsteři a použít <i>úplně každou</i> novinku, že?
</p>
<h3>Můžu dnes psát v ES2015?</h3>
<p>A nejlepší na tom všem je, že nemusíte čekat, až všechny browsery implementují ES2015, můžete ho začít používat ještě dnes! Zřejmě nejpopulárnější nástroj, který umí transpilovat ES2015 do ES5 (který zvládne skoro každý browser) je Babel. Můžete dokonce zkusit <a href="https://babeljs.io/repl/">online REPL</a> nebo <a href="https://jsfiddle.net/">JSFiddle</a>, který nedávno přidal podporu Babelu (v panelu script si vyberete jazyk Babel). Babel používáme ve Vendavu a můžeme ho bez obav doporučit.
</p>
<h3>Vtip dne</h3>
<p>V JavaScriptových kruzích patří k dobrému vychování zakončit diskusi zostuzením nějakého OOP jazyka. Takže tady to máte:</p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAs_dGbSu-4RqFKb6tT-Ecv0fzKMyYtCV-4qmq2aDOp2zEQkOQY1rl6r6tnTOLJBjhuu_0fTjD18KCtIkhsdteZu5WEfr8qybZwswEQ_LpSmLJLlHhcFRhc6Ku5mBLNd5COzerPmWwpDA/s1600/javameme.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAs_dGbSu-4RqFKb6tT-Ecv0fzKMyYtCV-4qmq2aDOp2zEQkOQY1rl6r6tnTOLJBjhuu_0fTjD18KCtIkhsdteZu5WEfr8qybZwswEQ_LpSmLJLlHhcFRhc6Ku5mBLNd5COzerPmWwpDA/s320/javameme.jpg" /></a>
<p>
Hehehe hahaha hihihi, v Javě? Oh wait! Je tu přece Java 8, takže konec legrace, ono to jde a ani to nevypadá <a href="http://stackoverflow.com/a/14750362/243263">moc hrozně</a>. Ok, takže mi ještě ukažte transpiler Javy 8 na Javu 6, kterou používá většina velkých firem a budu zticha. Cože, jak že se ten transpiller jmenuje? Scala? Okay, curry all the things!
</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com2tag:blogger.com,1999:blog-3058726084145190258.post-64667565470067184282015-01-31T15:03:00.001+01:002015-01-31T15:10:18.726+01:00Game of Life v Clojure<p>Clojure je boží. Tento týden jsme společně s <a href="http://www.rarous.net/weblog/">Alešem Roubíčkem</a> o ní měli v Hradci přednášku. Aleš začal obecným úvodem a představením jazyka, já jsem pak ukazoval, jak se v Clojure napíše Game of Life.</p>
<a name='more'></a>
<p>Kdo už někdy zkoušel napsat <a href="http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">Game of Life</a>, dá mi za pravdu, že je to složitější než se zdá, zvlášť když použijete naivní algoritmus a ne-úplně-nejlepší jazyk. O to větší bylo moje překvapení, když mi Aleš před lety ukázal řešení v Clojure, které bylo na pár řádků.</p>
<p>To původní řešení <a href="http://clj-me.cgrand.net/2011/08/19/conways-game-of-life/">naleznete zde</a>. Svoji přednášku jsem založil právě na něm, akorát jsem udělal malé syntaktické změny, aby byl kód čitelnější pro začátečníky. Abych vás dlouho nenapínal, kompletně to vypadá takhle:</p>
<script src="https://gist.github.com/kolman/1516c5f38ab6ab9828e8.js"></script>
<p>Záznam přednášky je na <a href="http://youtu.be/hr8RnTfijx0">youtube</a>. Vystřihl jsem z něj asi tříminutovou pasáž, kde se pokouším zjistit, proč to nechce kreslit buňky.</p>
<iframe width="640" height="480" src="https://www.youtube.com/embed/hr8RnTfijx0" frameborder="0" allowfullscreen></iframe>
<p>Pokud si chcete sami vyzkoušet <a href="http://gorilla-repl.org/">interaktivní REPL v browseru</a>, celé jsem to <a href="https://github.com/kolman/GoL-Clojure">pushnul na github</a>, stačí mít nainstalovaný Leiningen. Instalační kroky najdete v readme.</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com0tag:blogger.com,1999:blog-3058726084145190258.post-59433682962318620652014-04-17T00:39:00.000+02:002014-04-17T08:27:56.853+02:00Nebojte se "domácího espressa"<p>
Kafe je boží. Teda záleží jaký kafe a jak udělaný, ale každý ví, že nejlepší je piccolo. Teda <a href="http://www.piccoloneexistuje.cz/">kdyby existovalo</a>. Co už je o dost míň boží je pokoušet se <strike>piccolo</strike> espresso vyrobit v domácích podmínkách. Budou vám <a href="http://www.cuketka.cz/?p=3697">tvrdit, že to nejde</a>, že je to strašně drahé, nebo že máte zvolit radši jiný způsob přípravy.
</p>
<a name='more'></a>
<p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihejllWNqdAVqVl7O1MoKVbwGHC66mWM89_-znEllejtvD8GL8uDzN8QavoLaJQFut7vsEQGWg_gTmazAeRvPhX0ZKkTZBZANGRRFvXwDoBRt1AbYFgjFxc_8jsgCvmOSWaVLRPZELBqo/s1600/gaggiaevo.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihejllWNqdAVqVl7O1MoKVbwGHC66mWM89_-znEllejtvD8GL8uDzN8QavoLaJQFut7vsEQGWg_gTmazAeRvPhX0ZKkTZBZANGRRFvXwDoBRt1AbYFgjFxc_8jsgCvmOSWaVLRPZELBqo/s320/gaggiaevo.jpg" /></a></div>
French press, Aeropress nebo Cleverdrip jsou určitě moc fajn techniky, <em>ale není to espresso.</em> Espresso je prostě espresso (nekecám) a nic se mu nevyrovná. Jenže připravit dobré espresso v domácích podmínkách je prý nemožné. No, záleží na jakou laťku kvality míříte. Moje zkušenost je taková, že domácí espresso od neškoleného baristy-samouka (mě) na kávovaru lehce přes 5 tisíc je <em>daleko lepší</em> než espresso v 95% kaváren v našem maloměstě a 94% kaváren v hlavním městě (ve zbývajících pěti procentech je srovnatelné). Stačí vychytat pár důležitých věcí.
</p>
<p>
Předem upozorňuju, že jsem naprostý laik. Neprošel jsem žádným školením a nemám žádné kávové vzdělání. Proto je nutné brát moje názory s rezervou, jsou to jen moje osobní zkušenosti. Než se rozhodnete utratit své peníze podle mých rad, gůglete, zkoumejte a hledejte zkušenosti ostatních. Jak říkáme my od software, THE INFORMATION IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS INFORMATION INCLUDING ALL IMPLIED WARRANTIES... atd ale dál to stejně nikdo nečte.
</p>
<p>
Na co si tedy dát pozor když chcete dělat espresso doma?
</p>
<h3>Kávovar</h3>
<p><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-J0FK_LCDWnE/U08Ch2DTdCI/AAAAAAAAMGM/VlUu9w98fVI/s1600/14+-+2" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-J0FK_LCDWnE/U08Ch2DTdCI/AAAAAAAAMGM/VlUu9w98fVI/s200/14+-+2" /></a></div>
Dobrý espresso kávovar poznáte podle jedné základní věci: Pořádný portafilter (=páka) s "profi" miskou. Co je to "profi" miska a že jste o ní nikdy neslyšeli? Běžné kávovary ze supermarketu mají takzvané "přetlakové" misky s dvojitým dnem. Když misku otočíte vzhůru nohama, najdete jen jednu maličkou dirku. To znamená, že všechna tekutina se při výrobě espressa musí protlačit skrz tuto dirku. Proč to tak výrobci dělají? Má to víc důvodů. Jednak můžou použít levnější čerpadlo, které by jinak neudrželo požadovaný tlak po celou dobu "tahání" espressa, jednak to kafe našlehá a vytvoří <em>pěnu</em> i ze staré a nekvalitní kávy a také si poradí se špatně namletou surovinou.
</p>
<p><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-6f5kkLP5w5g/U08Ch-dZIWI/AAAAAAAAMGU/ugpb5FKo4Ec/s1600/14+-+1" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-6f5kkLP5w5g/U08Ch-dZIWI/AAAAAAAAMGU/ugpb5FKo4Ec/s200/14+-+1" /></a></div>
"Profi" miska je vlastně jen kus děravého plechu. V případě přetlakové misky se o odpor proti tlaku čerpadla postará ta malá dirka, s profi miskou je to jen kávový disk samotný. Udělat espresso s profi miskou je o dost těžší, protože záleží na správné kombinaci hrubosti mletí, stlačení kávy v portafilteru tamperem (to je takové to těžítko) a na surovině samotné. Se starou nebo nekvalitní kávou prostě <em>crema</em> nebude (všimněte si, že zatímco na běžném supermarketovém přístroji vyrábíte pěnu, opravdové espresso má cremu). Udělat espresso s profi miskou je těžší, ale výsledek bude lepší a celý proces daleko dobrodružnější!
</p>
<p>
Solidní přístroj přitom nemusí stát ranec. My jsme v <a href="http://vendavo.cz/">práci</a> pořídili <a href="http://www.heureka.cz/?h%5Bfraze%5D=gaggia+evolution">Gaggia Evolution</a> a jsme s ní dost spokojeni. Vyplatí se koukat po výrobcích, kteří dělají "velké" mašiny do kaváren, protože do svých malých domácích přístrojů někdy montují stejné komponenty. Za naprostou klasiku platí např. <a href="http://www.heureka.cz/?h%5Bfraze%5D=gaggia+classic">Gaggia Classic</a> nebo (o dost dražší) <a href="http://kavovary-cajovary-espressa.heureka.cz/rancilio-silvia-v3/">Rancilio Silvia</a>, za pokus by asi stál i <a href="http://kavovary-cajovary-espressa.heureka.cz/lelit-pl-42/">Lelit PL42</a>, rovnou i s mlýnkem.
</p>
<h3>Mlýnek</h3>
<p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgES7ES5HGszu_Tuh12_m2bziut0RZmQJnZ-NBC4106352rCl0mPAcDkH24sJfHyDaGZJTJ4GIZGOzdhANibwQHuwgCP8Jssu85LJwbPqNejWeujZUpdsB96rMlihdUkb8UHV9X1gLUYXo/s1600/tonino.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgES7ES5HGszu_Tuh12_m2bziut0RZmQJnZ-NBC4106352rCl0mPAcDkH24sJfHyDaGZJTJ4GIZGOzdhANibwQHuwgCP8Jssu85LJwbPqNejWeujZUpdsB96rMlihdUkb8UHV9X1gLUYXo/s200/tonino.jpg" /></a></div>
Z řečeného jste už možná pochopili, že kvalitní mlýnek je při používání "profi" misek zcela zásadní. Nejen že potřebujete dostatečně jemné a konzistentní mletí, ale mletá káva strašně <a href="http://www.doubleshot.cz/blog/2010/08/26/proc-nenabizime-mletou-kavu/">rychle oxiduje</a>. Proto nelze kupovat kávu už namletou - neudělá cremu.
</p>
<p>
Dobrý mlýnek se většinou podceňuje. I já jsem udělal tu chybu a ze začátku jsme používali obyčejný mlýnek ETA. Když jsme pak pořídili pořádný mlýnek, nestačil jsem se divit, o kolik lepší espresso jsme najednou byli schopni udělat na stejném kávovaru a ze stejných zrn.
</p>
<p>
U mlýnku je důležitý způsob mletí: Musí používat kameny, čím větší průměr, tím lepší. Malé kameny se musí točit rychleji a hrozí, že se kafe spálí. Jiné způsoby mletí (např. nože) zase nemůžou zajistit potřebnou konzistenci a jemnost, nutnou pro espresso.
</p>
<p>
Mlýnky rozumné kvality začínají na 5 tisících Kč, my máme <a href="http://kavomlynky.heureka.cz/gaggia-mdf-black/">Gaggia MDF</a>, do podobné kategorie se řadí také Baratza Vario nebo Rancilio Rocky. Do mlýnku se podle mě vyplatí investovat i když nechcete dělat espresso, čerstvě namletá káva je základ pro každý způsob přípravy.
</p>
<h3>Zrno</h3>
<p><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-3QIKOvq36ec/U08Ch9Ebg8I/AAAAAAAAMGc/APJELlB-Hs0/s1600/14+-+3" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-3QIKOvq36ec/U08Ch9Ebg8I/AAAAAAAAMGc/APJELlB-Hs0/s200/14+-+3" /></a></div>
Kávová zrna musí být především čerstvá. Proto nekupujeme kafe v supermarketu, ale přes internet, ve specializovaných prodejnách nebo kavárnách (ale tam bývá drahé).
</p>
<p>
Kafe můžete koupit buď přímo z pražírny, nebo namíchanou značkovou směs. A je v tom velký rozdíl. Vyhlášené pražírny (jako třeba Doubleshot) totiž používají "moderní" světlé pražení, které ve výsledném nápoji lépe zachová vůně a chutě bobulí. Jejich kafe je také pocitově "lehčí" a můžete jich denně vypít víc. Jenže pokud děláte espresso, je daleko jednosušší to zkazit. Světle pražená zrna z fancy pražíren vám prostě neodpustí žádnou chybu při přípravě, což je zvlášť pro začátečníky dost nepříjemné. Také jsou chuťově kyselejší, což některým lidem může vadit.
</p>
<p>
Oproti tomu značkové směsi jsou pražena více na tmavo, což potlačuje kyselost a rozdíly mezi odrůdami, kafe je pocitově "těžší" a když si dáte čtyři za den, máte pocit že už byste další neunesli. Na druhou stranu, chuť je spíš hořko-sladká, což tradiční český konzument požaduje. Pro začínajícího domácího baristu je jednodušší udělat espresso z dobré italské směsi než z moderního kafe z cool pražírny.
</p>
<p>
Moje rada proto zní: Experimentujte. Začněte klasickou směsí z Itálie a nebojte se směsi s robustou. Robusta dělá lepší cremu a dodává kafi "grády". Postupně jsem dospěl k tomu, že rozdíl mezi 100% arabikou a směsi s podílem robusty je jen záležitost osobních preferencí a chutí. Až získáte jistotu a espresso se vám začne dařit, zkuste 100% arabiku a pak jednodruhové kafe z nějaké známé pražírny (třeba <a href="http://www.coffeesource.eu/">CoffeeSource</a> nebo <a href="http://www.doubleshot.cz/">Doubleshot</a>).
</p>
<h3>Závěrem</h3>
<p>
Nákup mlýnku nebo kávovaru si dobře promyslete. Podívejte se na fóra a hodnocení ostatních. Kvalitní zdroj informací je třeba <a href="http://coffeegeek.com/reviews/">CoffeeGeek</a>, v češtině lze opatrně doporučit <a href="http://www.primacafe.cz/primafrm/">Primacafé</a>.
</p>
<p>
Domácí espresso je cesta, na které utratíte spoustu peněz a vylejete spoustu nepovedených kafí. Ale pokud vás nebaví standard směs a baví vás experimentovat, stojí to za to. Za pár dnů budete mít nejlepší kavárnu ve městě!
</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com3tag:blogger.com,1999:blog-3058726084145190258.post-58200020354380116662014-04-10T21:40:00.000+02:002014-04-10T21:40:34.497+02:00Spock<p>
Spock je boží. Nedávno jsem o něm měl talk na super akci, <a href="http://jopenspace.cz/">jOpenSpace</a>. No dobře, nebylo to nedávno, ale v listopadu, ale teprv teď jsem si vzpoměl, že jsem o tom zapomněl napsat. Ale to nevadí, protože nás stejně všechny brzo nahradí počítače.
</p>
<a name='more'></a>
<p>
Ale popořadě. <a href="https://code.google.com/p/spock/">Spock</a> je obecný nástroj pro automatizaci testů, něco jako JUnit. Na rozdíl od něj má pár skvělých featur, které z něj dělají řádově lepší nástroj než JUnit. V přednášce se dozvíte proč:
</p><p>
<iframe width="560" height="315" src="//www.youtube.com/embed/ePEQ60UFQts" frameborder="0" allowfullscreen></iframe>
</p><p>
Pokud se nechcete koukat na video, zkuste <a href="http://www.slideshare.net/danielkolman1/spock-27942005">komentované slajdy</a>.
</p><p>
Mimochodem, jOpenSpace se mi moc líbila. Je to menší akce, takže máte možnost seznámit se se všemi účastníky, a protože všichni musí mít přednášku, odpadají pasivní konferenční povaleči. Všichni účastníci byli zajímaví lidé, se kterými je radost pokecat. Také formát konference je bezva: Časový limit přednášky je 15 minut. Nejdřív se mi to zdálo málo, ale v praxi se ukázalo, že se do té čtvrt hodiny zkondenzuje vše podstatné a odpadne balast. Navíc na dvoudenní akci stejně neudržíte pozornost déle. No a když vás řečník nebo téma nudí, trvá to nejdéle 15 minut.
</p><p>
A jaké přednášky mě nejvíc zaujaly? Zdeněk Merta ukazoval <a href="http://jopenspace.cz/2013/presentations/zdenek-merta-vert.x.pdf">Vert.x</a>, takže už javisti nemusí javascriptařům závidět node.js. Pepa Cacek popisoval zajímavý nástroj usnadňující testování java aplikací, <a href="http://jopenspace.cz/2013/presentations/pepa-cacek-Arquillian.pdf">Arquillian</a>. Jak se ostěhovat na Nový Zéland a pracovat tam vysvětlil <a href="http://jopenspace.cz/2013/audio/jOpenSpace-2013-Martin-Vich.mp3">Martin Vích</a> (<a href="http://jopenspace.cz/2013/presentations/martin-vich-prace-v-zahranici.pdf">prezentace</a>). Trochu off-topic, ale zajímavou a hezkou prezentaci měl Honza Štěrba o <a href="http://www.slideshare.net/HonzaSterba/jopenspace-2013-12-dvod-pro-chi-bt-pekaem">pečení chleba</a>. Ale nejvíc mě dostal Jiří Fabián AKA Filemon se svojí vizí, jak chce aby vypadala jeho <a href="http://jopenspace.cz/2013/audio/jOpenSpace-2013-Jiri-Fabian.mp3">další firma</a>. Pokud podnikáte v IT, určitě si to poslechněte.
</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com0tag:blogger.com,1999:blog-3058726084145190258.post-35025192359492577032014-03-31T23:20:00.000+02:002014-03-31T23:20:35.447+02:00Lambda Days 2014 Krakow<p>
Na konci února se v Krakowě konala dvoudenní konference o funkcionálních jazycích, <a href="http://www.lambdadays.org/">Lambda Days</a>, což byla skvělá příležitost udělat si výlet, proszwisztit szi szlowiczka a dozvědět se něco nového.
</p>
<a name='more'></a>
<p>
Dva hlavní motivy konference byly Erlang a Scala, což není tak překvapivé, projdeme-li si seznam sponzorů (což nijak nesnižuje kvalitu přednášek). Na druhou stranu, akce byla zdarma, takže dík. Celá konference byla v angličtině, kromě jedné přednášky, což jsme dali s klukama pořadatelům pořádně sežrat (na <a href="https://twitter.com/dkvasnickajr/status/439040477648920577">twitteru</a>), nakonec jsme si ale vzpomněli, že jsme bratři Slovani a že polštině vlastně rozumíme, zvlášť když je v IT polovina výrazů stejně anglicky ("V Scale jest bardzo mocny pattern matching"). Za ten sprdung na twitteru se nám pak pořadatelé pomstili svým národním nápojem, vařenou řepou. Abych se přiznal, vzal jsem si kelímek jen proto, že se tam něco rozdávalo zdarma, a teprv pak jsem zjistil, že je to řepa. No nakonec to bylo celkem dobré a aspoň jsem si rozšířil obzory. Ale teď už k obsahu:
</p>
<p>
Erlang je hrozně zajímavá věc. Nemyslím teď samotný jazyk, ale celou platformu (<a href="http://learnyousomeerlang.com/what-is-otp">OTP</a>) pro tvorbu distribuovaných, masivně paralelních, odolných (fault-tolerant) aplikací, které mají mít minimální down-time. Na rozdíl od mnoha jiných populárních platforem je prověřen praxí, a to ne jen tak ledajakou: Vyvinul ho Ericsson, používá ho T-Mobile a další telekomunikační společnosti, Basho, Facebook nebo WhatsApp.
</p>
<p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHpMEyCcpdIOQoK5DKWB8lEGWU08YaSqzFa0YGEJJS7GqomgAqL8ufb39NAxeXBUrGSY_0Rmn2lBt0IAD4CTf49WdSa7LaOJZQrR7-ZDqycl5DMARkzHlxeKCwfN48fo9M_C6ku7FaCCk/s1600/14+-+2" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHpMEyCcpdIOQoK5DKWB8lEGWU08YaSqzFa0YGEJJS7GqomgAqL8ufb39NAxeXBUrGSY_0Rmn2lBt0IAD4CTf49WdSa7LaOJZQrR7-ZDqycl5DMARkzHlxeKCwfN48fo9M_C6ku7FaCCk/s320/14+-+2" /></a></div>Jak uvedl ve své <a href="http://www.lambdadays.org/static/upload/media/139417090858031201402krakowlambdadays.pdf">přednášce</a> o výhodách Erlangu z pohledu businessu Torben Hoffmann (CTO Erlang Solutions), po akvizici WhatsApp Facebookem je 19 miliard důvodů, proč ho používat (tolik dolarů za WhatsApp Facebook zaplatil). Business hledisko stálo přímo u zrodu Erlangu: Ericsson potřeboval platformu, která by byla spolehlivá, umožňovala upgrade za běhu, měla minimální downtime a zároveň nabízela větší produktivitu než C++ nebo Java. Tři základní principy, ze kterých Erlang jako platforma vychází, jsou "fail fast", "failure handling" a "share nothing".
</p>
<p>
"Share nothing" je dnes stále důležitější. Rychlost procesorů přestala růst někdy kolem roku 2006, místo toho roste počet jader na čipu. Díky tomu se stále častěji budeme potkávat s "manycore" a "megacore" počítači. Aby naše programy dokázaly na plno využít síly takových počítačů, budou muset být masivně paralelní. Příklad: Aby program opravdu využil kapacitu 64 jádrového stroje, musí být paralelizovatelný z více než 90%. Existuje totiž zákon, který stanovuje maximální teoretické zrychlení algoritmu podle míry jeho paralelizace - <a href="http://en.wikipedia.org/wiki/Amdahl's_law">Amdahlův zákon</a>. Takže i když bude algoritmus paralelizovatelný ze 75%, což se může zdát hodně, maximální teoretické zrychlení je čtyřnásobné, a od 128 jader přestane rychlost růst (viz <a href="http://en.wikipedia.org/wiki/File:AmdahlsLaw.svg">graf</a>). Masivní paralelizace lze dosáhnout pouze tehdy, pokud program rozdělíte na malé nezávislé jednotky, které mezi sebou nic nesdílí.
</p>
<p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVIGnlwOrUzkYtJb3bxkHD9R-lNbBfzQRA2eFBGZuDNU9JQQJJHqCUWND1lCNvILNrJNUIlYezfqG8_Uk2vDns45YorbPL40MH5o42SHVpWaxRipER-YqbPobotUbZQdx1XInpI8wqzCQ/s1600/erlangcodestructure.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVIGnlwOrUzkYtJb3bxkHD9R-lNbBfzQRA2eFBGZuDNU9JQQJJHqCUWND1lCNvILNrJNUIlYezfqG8_Uk2vDns45YorbPL40MH5o42SHVpWaxRipER-YqbPobotUbZQdx1XInpI8wqzCQ/s320/erlangcodestructure.png" /></a></div>"Share nothing" má i další výhodu - pokud nastane neošetřená chyba v monolitické aplikaci, může se dostat do nekonzistentního stavu a nejlepší je ji celou restartovat. Pokud nastane chyba v jedné z mnoha oddělených jednotek, zbytek aplikace není ovlivněn. V Erlangu každou takovou jednotku (worker) spravuje supervizor, který ji při chybě prostě zahodí a vytvoří novou. <a href="http://www.erlang.org/doc/design_principles/des_princ.html">Supervizory tvoří strom</a>, takže když selže supervizor, jeho nadřazený supervizor ho zahodí a vytvoří znovu. To je druhý princip - "failure handling" - díky kterému je Erlang tak robustní. No a to umožňuje třetí princip - "fail fast". Když nastane chyba, nemá cenu ji řešit, postará se o to supervizor. Díky tomu je podíl kódu, který ošetřuje chyby, v Erlangu velmi malý. Torben Hoffmann to nazval "agressive/offensive programming - don't be defensive, just fail fast".
</p>
<p>
Když jsem to tak poslouchal, říkám si, proč je Erlang tak málo rozšířený? Může za to dominance PHP a Javy, nezvyklá syntaxe, nebo neexistence webhostingu? Bude to určitě kombinace více faktorů. Pokud vám vadí syntax, zkuste <a href="http://elixir-lang.org/">Elixir</a>, který na konferenci prezentoval přímo jeho tvůrce, José Valim (záznam podobné přednášky z jiné konference <a href="http://www.erlang-factory.com/conference/SFBay2013/speakers/JoseValim">najdete zde</a>). Je to jazyk trochu podobný Ruby, který umožňuje vytvářet vlastní DSL. José zmínil jednu zajímavou teorii z lingvistiky (<a href="http://cs.wikipedia.org/wiki/Sapir-Whorfova_hypot%C3%A9za">Sapir-Whorfovu hypotézu</a>), která ve zkratce tvrdí asi tolik, že jazykové pojmy které používáme ovlivňují naše vnímání reality. Prostě pro co nemáme slovo, o tom se velmi těžko dá přemýšlet a naopak: Když existuje pojmenování nějakého konceptu (například "thread-safe"), považujeme tento koncept za přirozený a nutný, i když tomu tak nemusí nutně být. Více k tématu by měl obsahovat text <a href="http://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf">Growing a Language</a>, který napsal jeden z autorů Javy, Guy Steele.
</p>
<p>
Princip workerů a supervizorů používá také <a href="http://akka.io/">Akka</a>. Je inspirovaná Erlangem, ale běží nad JVM a umožňuje programovat ve Scale, Javě nebo Groovy.
</p>
<p>
Další hodně zajímavou přednášku měl Kevin Hammond o již zmíněných "megacore" počítačích (zřejmě je autorem tohoto termínu). Počítače (blízké) budoucnosti budou mít zřejmě desítky až stovky jader, ale budou to jádra heterogenní - nebudou všechna stejná. Budou mít pár "heavyweight general-purpose" jader, tak jak je známe dnes, stovky matematických jader pro operace s plovoucí čárkou (tak jak dnes fungují GPU), několik specializovaných jader pro grafiku, bezpečnost, síťové přenosy apod. Také zřejmě budou obsahovat jádra s různou frekvencí - první vlaštovkou v tomto směru je mobilní telefon (jak příznačné) Samsung Exynos, který má jádra s různou frekvencí - a různou spotřebou elektřiny. Stále více počítačů funguje (alespoň částečně) na baterku, a tak bude spotřeba energie stále důležitější faktor. Procesorová jádra zřejmě nebudou všechna sdílet paměť jako dnes, protože to není dobrá abstrakce a zpomalovala by fungování počítače. Možná že se objeví nativní message-based systémy v rámci jednoho čipu.
</p>
<p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQPr0_kT3GGs6wK8xQT6RrD5Ky3mwyWbuU3IGjtd1oYkcW4JA-FQCxBD_l9PYKb-B8EdxiWWmyNqLnx3eHC6wOLo0E4d3__3xXY1skP3towvWL9gz_l9WDcvRGSeaQlEm4PtQSz9WjdIY/s1600/paraforming.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQPr0_kT3GGs6wK8xQT6RrD5Ky3mwyWbuU3IGjtd1oYkcW4JA-FQCxBD_l9PYKb-B8EdxiWWmyNqLnx3eHC6wOLo0E4d3__3xXY1skP3towvWL9gz_l9WDcvRGSeaQlEm4PtQSz9WjdIY/s320/paraforming.png" /></a></div>Funkcionální programování se zdá být vhodnějším nástrojem pro výzvy blízké budoucnosti než objektově orientovaný přístup. Kevin Hammond vyvíjí poloautomatický způsob refaktoringu - paraforming - který má kód udělat více paralelizovatelným. Pokud vás zajímá víc, projděte si slidy z jiné konference, které <a href="http://schd.ws/hosted_files/buildstuff2013/6c/BuildStuff-Vilnius-Dec2013-2%20Kevin%20Hammond.pdf">najdete zde</a>.
</p>
<p>
Zajímavých přednášek bylo na Lamda Days více, tohle je můj subjektivní výběr toho nejlepšího. Když to shrnu, organizátorům se povedlo sehnat výborné zahraniční řečníky, často přímo autory zmíněných technologií a uspořádat dvoudenní konferenci o funkcionálním programování zdarma, na půdě místní univerzity. Přál bych si, aby se to stalo inspirací pro naše lokální Hradubické univerzity - podobnou akci, byť i polovičního rozsahu, bych považoval za velký úspěch.
</p>
<p>
Na závěr bych chtěl poděkovat <a href="http://vendavo.cz/">Vendavu</a> za podporu a financování tohoto príma výletu:)
</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com0tag:blogger.com,1999:blog-3058726084145190258.post-58021045468900062352014-01-05T09:27:00.000+01:002014-01-05T09:27:25.558+01:00Být manažerem<p>
Setkávám se s tím zas a znova - mladí programátoři touží stát se manažery. Zřejmě za to může rozšířený mýtus, že po třicítce měkne mozek a že starý programátor je špatný programátor. Nebudu tady moralizovat, protože před lety jsem to chtěl taky. Naštěstí jsem byl vyléčen a tak vám můžu popsat, do čeho se řítíte.
</p>
<a name='more'></a>
<p>
Když budete mít to "štěstí", tak se po pár letech ve větší firmě stanete šéfem menšího týmu programátorů. V řeči teorie jste se právě stali <i>liniovým manažerem</i>. Jeho hlavním úkolem je zajistit splnění cílů zadaných středním managementem. Jinými slovy, musíte svým podřízeným vysvětlit, co mají dělat a sledovat jak to dělají, aby je třeba nenapadlo dělat nějaké zbytečné <i>tásky</i>, které nepřinášejí žádnou <i>byznys hodnotu</i>.
</p>
<p>
Pokud jste před vstupem do manažerského stavu patřili k hlasitým zastáncům ideálů jako TDD, clean code nebo refactoring, budete to mít o dost těžší. Budete muset svým ovečkám vysvětlit, proč to náhle nemá <i>byznys hodnotu</i>. Budete to muset udělat šikovně a přesvědčivě, aby to nedělali tajně (pak nestihnou plán), nebo aby nezačali remcat moc nahlas. Nejhorší je, když začne nějaký obyčejný programátor obtěžovat střední management (vaše nadřízené) s nějakými dotazy nebo nedej bože návrhy. To se pak hned pozná, že liniový manažer nezvládá plnit svoje rozvojové cíle.
</p>
<p>
Můžete zkusit přesvědčit střední management, že vaše staré ideály mají <i>byznys hodnotu</i>. Bude to ale těžké, protože střední management dostal svoje úkoly od top managementu a má o prioritách jasno. Navíc, proč by vám měl střední management věřit, že zrovna <i>váš tým</i> dokáže například refaktoring části aplikace úspěšně zvládnout? Otázka je, jestli tomu budete věřit vy sami, protože složení týmu nejspíš nebudete mít možnost ovlivnit (alespoň ne ze začátku). Střední management vám přidělil (lidské) zdroje a úkolem liniového manažera je doručit <i>byznys hodnotu</i> s tím, co má.
</p>
<p>
Nad některými úkoly od středního managementu budete kroutit hlavou. Například budete muset zajistit, aby vaši podřízení vykazovali správné procento času na správný typ <i>tasku</i>, aniž by to mělo vliv na plnění cílů týmu. Pokud po vás budou chtít něco podobného, nerejpejte do toho, je to ztráta času. V naprosté většině jsou to reporty, které musí střední management vykazovat top managementu a vy s tím nic neuděláte. Vaším úkolem je dodat <i>správná</i> čísla. A pokud budete dodávat i grafy, pamatujte základní manažerské pravidlo: Graf musí směřovat vždy vzhůru!
</p>
<p>
Proto vám radím dobře: Neblázněte! Pokud jste programátoři ve slušné firmě, <i>už máte</i> dreamjob. Nemusíte se hnát nikam výš. Místo sezení na poradách můžete řešit technické problémy. Místo přípravy prezentací v powerpointu si můžete zkusit nový framework. Místo vymýšlení statistik můžete tajně napsat nějaké testy. A hlavně vás nemusí zajímat politika.
</p>
<p>
A nebo změňte firmu. Čím lepší firma, tím menší rozdíl mezi <i>manažerem</i> a <i>lidským zdrojem</i>. V nejlepších firmách není "flat organization structure" jen heslo na slajdy. V nejlepších firmách se i jako "obyčejný programátor" zajímáte o <i>byznys hodnotu</i>, protože máte přístup k důležitým informacím a důvěru managementu.
</p>
<p>
Navíc, v nejlepších firmách nejsou <i>manažeři</i>, ale <i>leadeři</i>. Rozdíl není v názvu pozice. Autorita manažera je daná jeho pozicí ve firemním orgchartu. Autorita leadera je daná tím, že mu ostatní věří, že to dělá dobře.
</p>
<p>
A vysoké školy nabízející "management" přímo jako studijní obor? Meh.
</p>
Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com12tag:blogger.com,1999:blog-3058726084145190258.post-52741853350148263162013-11-27T23:12:00.000+01:002013-11-27T23:12:56.863+01:00GOTO 2013 Amsterdam - Erik Meijer<p>
Jedním z hlavních důvodů, proč jsem chtěl jet na konferenci GOTO 2013 do Amsterdamu, byly přednášky Erika Meijera. Byly celkem tři a týkaly se "strojového učení" (neboli machine learning), budoucnosti databází a lightning talk o funkcionálním programování.
</p>
<a name='more'></a>
<p>
Předchozí díly:
<a href="http://blog.kolman.cz/2013/07/goto-2013-amsterdam-big-data-nosql.html">Big Data a NoSQL</a>,
<a href="http://blog.kolman.cz/2013/07/goto-2013-amsterdam-legacy-systems.html">Legacy Systems</a>
</p>
<p>
Erik Meijer je člověk, na kterého intenzivně myslím pokaždé, když v Javě potřebuju přefiltrovat nebo setřídit kolekci objektů. Ale popořadě. Nejdříve byl jedním z vývojářů funkcionálního jazyka Haskell. To je ten jazyk, který si někteří účastníci <a href="http://jopenspace.cz/">jOpenSpace</a> spletli s BrainFuckem. Ke své vlastní škodě. <a href="http://www.haskell.org/">Haskell</a> je funkcionální, silně staticky typový jazyk (na rozdíl od Javy), ve kterém musíte málokdy psát deklaraci typu (na rozdíl od Javy), a přitom nemusíte počítat kupu závorek (na rozdíl od Clojure). Neodolám a musím vám ukázat klasický příklad, na kterém se demonstruje jeho síla - QuickSort:
</p>
<pre class="brush: sql;">
-- funkce quicksort transformuje seznam prvků [a] na seznam prvků [a]
-- prvky musí být porovnatelné - implementovat Ord
quicksort :: Ord a => [a] -> [a]
-- pokud je vstupem prázdný seznam [], výstupem je prázdný seznam []
quicksort [] = []
-- pokud je vstupem neprázdný seznam, rozdělíme ho na první prvek (p) a zbytek (xs)
-- z xs získáme prvky menší než p (lesser) a větší nebo stejné jako p (greater)
-- lesser a greater rekurzivně setřídíme funkcí quicksort
-- výstupem je sloučení setříděných seznamů a prvku p (operátor ++ slučuje seznamy)
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
where
lesser = filter (< p) xs
greater = filter (>= p) xs
</pre>
<p>
Haskell je líný, to znamená, že výraz se začne vyhodnocovat teprve v okamžiku, kdy je potřeba jeho výsledek. Podobně v C# funguje LINQ, na kterém Meijer později pracoval v Microsoftu:
</p>
<pre class="brush: c#">
// tento kód nic nedělá s kolekcí users, jen sestaví iterovatelný výraz:
var adults = users
.Select(new { Age = u.Age, Name = u.FirstName + " " + u.LastName, })
.Where(u => u.Age >= 18)
.OrderBy(u => u.Name);
// adults nyní obsahuje pouze referenci na původní kolekci users
// a složenou funkci pro její iterování
// teprve tímto příkazem se projde a vyhodnotí prvních 5 prvků:
adults.Take(5).ToList();
</pre>
<p>
A to je prosím čistý .NET Framework, bez jakékoliv rozšiřující knihovny. Celé je to navíc typově bezpečné, hlídané kompilátorem. A vyšlo to v roce 2007. Zkuste si to v Javě. (Pokud si myslíte, že se to dá vyřešit šikovnou knihovnou, tak ony lambda funkce v parametrech lze rozložit na AST a přeložit třeba do SQL. Bez reflexe.)
</p>
<p>
Groovy má sice pár metod, které mohou LINQ připomínat (collect, findAll...) ale je tu jeden zásadní rozdíl: Groovy je vyhodnocuje "horlivě" nebo "dychtivě" (neboli "eager", ať žijou české překlady). Takže v předchozím příkladě by se nejdřív vytvořila nová kolekce obsahující Age a Name, a to i pro prvky, kde je Age menší než 18. V C# mají takto složené operace minimální dopad na paměť, protože se pracuje s původní kolekcí a složenou funkcí (pokud to jde).
</p>
<p>
Další známá věc z Erikovy dílny jsou <a href="http://msdn.microsoft.com/en-us/data/gg577609.aspx">Reactive Extensions (Rx)</a>, které pak byly zkopírované snad do každého používanějšího jazyka. Zajímavé je, že inspirace pro Rx pochází z jeho hípísáckého přesvědčení, že svět je složen z dualit, z protikladů, které se vzájemně doplňují. Reactive extensions vám v podstatě umožňuje skládat funkce nad proudem událostí. Rx je <a href="http://channel9.msdn.com/Shows/Going+Deep/Expert-to-Expert-Brian-Beckman-and-Erik-Meijer-Inside-the-NET-Reactive-Framework-Rx">matematicky duální</a> k LINQ.
</p>
<h3>Nahradí vás počítač</h3>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL4pzm_idxFgH4CnTNoCPn-nqwAghVMm5K6DsdOFfWhZV5o_tQ4IC97bXEPNcW-WtPcflJ0QgieCRLmPJNwf4MYXMpIwoXqfklGNT6ffSHKqVxTN2IiFug3Rz-ctyLTLZTLASugOY2fjw/s1600/meijerduality.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL4pzm_idxFgH4CnTNoCPn-nqwAghVMm5K6DsdOFfWhZV5o_tQ4IC97bXEPNcW-WtPcflJ0QgieCRLmPJNwf4MYXMpIwoXqfklGNT6ffSHKqVxTN2IiFug3Rz-ctyLTLZTLASugOY2fjw/s320/meijerduality.jpg" /></a></div>
<p>
A v podobném duchu začala i jeho první přednáška na GOTO 2013 Amsterdam, která se jmenovala <a href="http://gotocon.com/amsterdam-2013/presentation/Machine%20Learning%20==%20Automated%20TDD">Machine Learning == Automated TDD</a>. Machine learning je zjednodušeně řečeno generování algoritmů na základě dat. Typickým příkladem může být spam filter. Bylo by dost neefektivní psát spam filter ručně. Spam je hrozně proměnlivý, triky které používá k ošálení filtrů se stále mění. Proto je lepší vzít velké množství emailových zpráv, mezi kterými nám uživatelé označili spam, a pomocí nějaké techniky strojového učení nechat vygenerovat algoritmus, který spam rozpozná. Navíc lze vygenerovaný kód průběžně vylepšovat pomocí nových emailů. Machine learning (generování kódu na základě dat) je duální k dotazování do databáze (vyhledání dat na základě kódu). A Erik Meijer věří, že když někde nalezne dualitu, musí časem vzniknout stejně efektivní algoritmy pro generování kódu jako dnes existují pro vyhledávání dat.
</p>
<p>
Hlavní pointa přednášky byla ovšem v tom, že machine learning do značné míry připomíná Test Driven Development. Když píšete testy, (většinou) postupujete tak, že vytvoříte v testu určitou situaci, a pak ji zimplementujete tak, aby test prošel. A pak napíšete další test s další situací, a upřesníte implementaci, aby pokrývala i tuto novou situaci. A tak dál až do chvíle, kdy máte pocit, že testy pokrývají všechny myslitelné situace.
</p>
<p>
No a machine learning funguje stejně - vezmete první dávku dat (v případě spam filtru jeden email) a vygenerujete kód, který rozpozná jestli je to spam. A pak další dávku dat a necháte počítač vygenerovaný kód upřesnit. A opakujete tento postup, dokud vám nedojdou data nebo čas. Jediný rozdíl oproti TDD je ten, že už nepotřebujete programátora - kód vygeneruje počítač.
</p>
<p>
A tak si hledejte novou práci, protože do deseti let vás podle Erika Meijera nahradí počítače. To bylo samozřejmě řečeno s nadsázkou, i když ne tak docela. A když se ho někdo z publika zeptal, kam dal studovat syna, řekl, že budoucnost vidí ve spojení hardware a software, jako to dělá Apple, a proto jeho syn studuje elektrotechniku.
</p>
<p>
Pokud vás to zaujalo a chtěli byste si machine learning zkusit sami, pořiďte si knížku <a href="http://shop.oreilly.com/product/0636920018483.do">Machine Learning for Hackers</a>, která je podle Erika Meijera dobrý úvod do tématu a je napsaná tak, že nepotřebujete PhD ze statistiky.
</p>
<h3>Smrt databázím!</h3>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7-hqirA2PhY5osbIHyZJ6e0nxBVcYjdh8FNovtwUXq727OJIqT7JAbT05hjRD5SmWhcDtDRufNso5xx7KTfWD9zLYWomFN39P9IoBl9yrZMIhylFf34CUb5AjNySF1xwMhx5Qr2_4aYQ/s1600/meijerdb.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7-hqirA2PhY5osbIHyZJ6e0nxBVcYjdh8FNovtwUXq727OJIqT7JAbT05hjRD5SmWhcDtDRufNso5xx7KTfWD9zLYWomFN39P9IoBl9yrZMIhylFf34CUb5AjNySF1xwMhx5Qr2_4aYQ/s320/meijerdb.jpg" /></a></div>
<p>
Kouzlo konferencí je v tom, že člověk, kterého považujete za poloboha, sestoupí o přestávce mezi nás obyčejné smrtelníky do bufetu a můžete si s ním popovídat nad šunkovým chlebíčkem nebo smaženým křídlem o tom, co teď teda dělá, o smyslu života a vůbec. Já považuju za čest, že se mi to stalo, a že jsem se tak dozvěděl, proč odešel z Microsoftu, že teď programuje hlavně (prosím sedněte si) v PHP (ano, PHP, má first-class functions a closure) a že je to celkově moc fajn člověk, se kterým by asi bylo bezva zajít jen tak na pivko do hospody. No a někdy mezi prvním a druhým řízečkem (všechno konferenční jídlo je v <i>mini</i> verzi) zmínil, že jeho snem je zničit databáze. Vždyť s reactive extensions nejsou databáze potřeba! Je lepší na události reagovat ihned když nastanou než zpětně analyzovat uložená data.
</p>
<p>
A (také) o tom bylo jeho <a href="http://gotocon.com/amsterdam-2013/presentation/KEYNOTE:%20A%20Monadic%20Model%20for%20Big%20Data">keynote</a>. Relační algebra, používaná v SQL databázích, je jen konkrétní implementace obecnějšího rozhraní - monád. A s monadickým rozhraním bychom mohli zpřístupnit jak klasická (uložená, pull-based) data, tak reaktivní (push-based) data. Přiznám se, že to už bylo lehce nad moje chápání, a tak vás pouze odkážu na text o <a href="http://queue.acm.org/detail.cfm?id=1961297">(ko)relačním modelu databází</a> a na <a href="http://queue.acm.org/detail.cfm?id=2169076">kostku</a>, kterou používá ke kategorizaci existujících databází. Původně jsem to chtěl všechno nastudovat a pak vám to tu vysvětlit, což je důvod, proč o červnové konferenci píšu v listopadu. Třeba si na to někdy udělám čas:)
</p>
<h3>What conference do you want to visit today?</h3>
<p>
Konference jsou príma. A tak mám pro vás na závěr jeden tajný tip. Není to tutovka, protože to bude první ročník a protože je to <i>mladý</i> obor (heh), ale může to být pecka. Taky jste jako děti koukali na Indiana Jonese? Tak teď máte možnost splnit si sny a stát se archeology (softwaru)! Na konci ledna se bude v Londýně konat první ročník <a href="http://ticosa.org/">The International Conference on Software Archaeology</a>. Původně to byl vtip, ale organizátoři to vzali jako výzvu, a tak sehnali zajímavé řečníky (Michael Feathers, Matt Wynne, Keith Braithwaite a další) a stylové místo (Museum of London). Za £100 a lowcost letenku to patří mezi levnější akce v zahraničí a mohla by to být sranda.
</p>
Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com5tag:blogger.com,1999:blog-3058726084145190258.post-31666674414438042332013-07-21T13:50:00.000+02:002013-07-22T23:26:25.934+02:00GOTO 2013 Amsterdam - Legacy Systems<p>
Když se řekne "legacy code", asi se vám vybaví dost netechnické pojmy: Hnůj, špagety, případně roztoky proměnlivé hustoty vyúsťující z různých tělesných otvorů. Starý kód je po technické stránce téměř vždy problém, protože je těžké mu rozumět a změny mohou mít nezamýšlené vedlejší efekty. Jenže je tu i jiný pohled: Starý kód je užitečný, má klienty a vydělává peníze. Už jen fakt, že přežil tak dlouho, je známka toho, že ho někdo potřebuje. Ať se nám to líbí nebo ne, ten starý hnusný kód bez testů je úspěšný kód. A o tom byl track "Legacy & Big Systems" na konferenci GOTO 2013 Amsterdam, kam jsem se podíval díky <a href="http://vendavo.cz/">Vendavu</a>.
</p>
<a name='more'></a>
<p>
Předchozí díl:
<a href="http://blog.kolman.cz/2013/07/goto-2013-amsterdam-big-data-nosql.html">Big Data a NoSQL</a>
</p>
<h3>Se stim smiř</h3>
<p>
Psát aplikaci "na zelené louce" by chtěl každý. Proto se i u zkušených programátorů setkáme s názorem, že by bylo nejlepší starý kód zahodit a aplikaci napsat znovu a (tentokrát) dobře. My co jsme už takový přepis zažili víme, že to sice jde, ale je to daleko náročnější, než se na začátku zdá. Starý kód v sobě akumuluje letitou zkušenost, hlavně nezdokumentované use casy a hraniční situace, které už dávno nikdo nezná a nedokáže popsat, takže vám nikdo neřekne, jestli je musíte v nové aplikaci zpětně podporovat nebo ne (respektive řekne vám to až zákazník formou kritické chyby a rollbacku systému, což může být dost nepříjemné). Ostatně je tu <a href="http://www.joelonsoftware.com/articles/fog0000000069.html">slavný precedens</a>, kdy přepis programu vedl k zániku kdysi nejoblíbenějšího webového prohlížeče a s ním celé firmy.
</p>
<p>
Legacy code je prostě realita, kterou je potřeba přijmout jako fakt a se kterou je potřeba se naučit zacházet.
</p>
<p>
<blockquote class="insert">
Pojem <b>legacy code</b>, doslova "zděděný kód", byl původně používán pro kód, který jsme převzali od někoho jiného. Jeho autoři buď už opustili firmu, nebo pracují na něčem jiném, nebo byl zakoupen od jiné firmy. Časem se význam rozšířil na obtížně čitelný kód s nekonzistentní strukturou, vysokým počtem vazeb a zbytečnou komplexitou, který je těžké měnit bez nechtěných vedlejších efektů. Jednodušší definici nabídl Michael Feathers v knize <a href="http://c2.com/cgi/wiki?WorkingEffectivelyWithLegacyCode">Working Effectively with Legacy Code</a>: Legacy code je jednodušše <b>kód bez testů</b>. Nezáleží na tom, jak dobře je napsaný, jak je hezký ani jestli respektuje <a href="http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)">principy OOP</a>. Kód bez testů je špatný, protože neumožňuje dělat změny rychle a verifikovatelně.
</blockquote>
</p>
<h3>Legacy je výzva</h3>
<p>
To ovšem neznamená že, máme hodit flintu do žita a patlat kód stejným způsobem, jako to dělali naši předchůdci. Legacy je výzva: Dokážete starý kód zkrotit pomocí testů? Dokážete refaktorovat staré kousky tak, abyste přitom nic nerozbili? Dokážete starý systém inovovat? A dokážete přitom zůstat pragmatičtí a produktivní? A bez psychického poškození?
</p>
<p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZunWIde2_mW3e_OTSpeUY0WR_nQ2j-sXUtAbPllmUsVJk5X4Mv-pOIWjBPyuN0ZAOF_72kN3-BFekrMVSLo26KtDPLn77rVuM2DhqdoD7gzliQ6w5MHJmD_ZmI7QJjrL7Zdr3FsJOxYE/s1600/IMG_4260.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZunWIde2_mW3e_OTSpeUY0WR_nQ2j-sXUtAbPllmUsVJk5X4Mv-pOIWjBPyuN0ZAOF_72kN3-BFekrMVSLo26KtDPLn77rVuM2DhqdoD7gzliQ6w5MHJmD_ZmI7QJjrL7Zdr3FsJOxYE/s320/IMG_4260.JPG" /></a></div>
A o tom byla přednáška Dave Thomase <a href="http://gotocon.com/amsterdam-2013/presentation/Legacy%20%20Evolution%20-%20The%20Innovation%20Opportunity">Legacy Evolution - The Innovation Opportunity</a>. Jako programátoři si musíme uvědomit, že každá změna do legacy systému musí přinést nějakou hodnotu pro zákazníky/uživatele, jinak nemá smysl. Samotné čištění kódu a splácení technického dluhu nemůže být cílem samo o sobě, spíše by se mělo dít v rámci změn, které hodnotu přinášejí. Jaké to tedy jsou? Třeba přidání nové funkcionality (thank you mister obvious), zjednodušení přístupu k datům (přes standardní rozhraní jako ODBC, REST nebo SOAP), zlevnění či zrychlení nějakého workflow. Soustřeďte se na data a jejich toky, protože data jsou to nejcennější co organizace má a hlavní funkcí informačních systémů je transformace a zpřístupnění dat.
</p>
<p>
Čistý refaktoring přitom má smysl, pokud se zaměříte na nějakou problematickou část kódu s omezeným rozsahem. Cílem by mělo být něco s praktickým dopadem, třeba zrychlení zpracování dat, nebo předělání třídy, která se často musí měnit, nebo ve které je často nalezena chyba. Najít takový kus kódu můžete pomocí nějaké smysluplné metriky, třeba <a href="http://www.stickyminds.com/sitewide.asp?Function=edetail&ObjectType=COL&ObjectId=16679&tth=DYN&tt=siteemail&iDyn=2">File Churn vs. Complexity</a>.
</p>
<p>
Podle Thomase je i v legacy systémech je dost příležitostí pro zavádění nových technologií, namátkou:
<ul>
<li>Nové jazyky (ruby, clojure, nodejs) pro skriptování testů a deploymentu</li>
<li>NoSQL databáze jako rychlá cache v rozhraní nad starým systémem</li>
<li>Cloud pro deployment a testování aplikace</li>
<li>"Use F# or Scala for algorithms, C# and Java for <i>muddleware</i>"</li>
</ul>
</p>
<h3>S metrikami opatrně</h3>
<p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnZPvxGXMyWcQkLTwDEgTF5EDV3c1eO0aZudpDLVSqQfndAfJRhivfsZW7cUE3TEm8VqwsHfWlXfLUd7feir4qvRGxwG7RAFWXYp1ozpxPMUiQYUQbXbS43zxvRFnCzX5SC-KG69HOjEw/s1600/IMG_4264.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnZPvxGXMyWcQkLTwDEgTF5EDV3c1eO0aZudpDLVSqQfndAfJRhivfsZW7cUE3TEm8VqwsHfWlXfLUd7feir4qvRGxwG7RAFWXYp1ozpxPMUiQYUQbXbS43zxvRFnCzX5SC-KG69HOjEw/s320/IMG_4264.JPG" /></a></div>Staré systémy bývají dost rozsáhlé a pracuje na nich spousta lidí a tak je užitečné sledovat nějaké metriky kódu, abychom měli základní představu o směru, kterým se ubíráme. Že jsou metriky dvousečná zbraň a že můžou v rukou necitlivého <a href="http://www.zivotvkorporaci.com/index.php?id=2013-03-28-o-profesionalni-kandidat">profesionálního manažera</a> zlikvidovat motivaci lidí i kvalitu kódu je známá věc, a Michael Feathers ve své přednášce <a href="http://gotocon.com/amsterdam-2013/presentation/The%20Metrics%20Trap">The Metrics Trap</a> nastínil pár způsobů jak se bránit.
</p>
<p>
Produktivitu softwarového vývoje <a href="http://www.martinfowler.com/bliki/CannotMeasureProductivity.html">nelze měřit</a>, nicméně stále jsou manažeři, kteří se zaklínají populární formulkou "co neměříš, neřídíš", přesto že už ji odvolal i <a href="http://www2.computer.org/cms/Computer.org/ComputingNow/homepage/2009/0709/rW_SO_Viewpoints.pdf">její autor</a>. Stejné je to s kvalitou. Když se zaměříte na jednu věc a nastavíte kvantitativní hranice, které nelze překročit, ostatní věci budou trpět. Například podle Featherse vede vynucování vysoké code coverage k hůře čitelným testům. Proto je lepší používat "tiché alarmy" (silent alarms), které mají za úkol vyvolat diskusi. Například je dobré měřit komplexitu kódu, ale místo aby build server automaticky zamítnul commit, když se komplexita zvýší nad nějakou hodnotu, je lepší poslat mail a upozornit ostatní, že se tak stalo. Jsou totiž rozumné případy, kdy je dobré pravidlo porušit. Srozumitelnost je důležitější než metriky.
</p>
<p>
A co dělat když narazíte na manažera staré školy, kterého nelze přesvědčit o riziku přeceňování metrik? Zavalte ho metrikami! Tuto strategii Feathers nazývá "metrics deluge" a spočívá v měření velkého množství parametrů. Čím více je metrik, tím těžší je brát jednu z nich vážně. Je také dobré používat metriky které jdou proti sobě, např. LOC versus duplicita kódu. Výsledky měření neukládejte, aby někoho nenapadlo stanovit cíle v duchu socialistických hesel "zvýšit code coverage o 10 procent".
</p>
<p>
No a když to nezabere? Pak je ve vaší firmě špatně nastavená firemní kultura. A to je špatná zpráva. Firemní kultura je největší bariérou zavádění agilních technik, a změna kultury je ten nejtěžší a nejdelší boj, do kterého se můžete pustit. Když vás někdo začne hodnotit podle LOC nebo code coverage, je načase zvážit přestup. Protože jen ve svobodném prostředí je práce s legacy systémem výzva; v legacy prostředí je legacy code jen opruz.
</p>
<p>
Příště se podíváme na to, jak chce Erik Meijer změnit svět databází.
</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com3tag:blogger.com,1999:blog-3058726084145190258.post-79619301525533277962013-07-03T00:48:00.000+02:002013-07-03T00:48:35.844+02:00GOTO 2013 Amsterdam - Big Data a NoSQL<p>
Na konci června jsem se díky <a href="http://vendavo.cz/">Vendavu</a> podíval do Amsterdamu na konferenci <a href="http://gotocon.com/amsterdam-2013/">GOTO 2013</a>, zřejmě jako jediný z Česko-Slovenska. Díky tomu jsem měl příležitost osobně potkat lidi, jejichž knížky čtu a které považuju za superstar mezi programátory.
</p>
<a name='more'></a>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIUPbrM1OYk4KBnIFPxj8C5HCCT-XWfkDWQ9jgm0UyNK1OQF6nq-pZs3sBoAh6fxSxlxiXYBFi4S0XwlhOTLLzKwDlVcqgHVGSlJRJ2lFRfazqq2XcQ_ZG_q-Q21VSzpdOGPLTH_9Q8zI/s1600/IMG_4216.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIUPbrM1OYk4KBnIFPxj8C5HCCT-XWfkDWQ9jgm0UyNK1OQF6nq-pZs3sBoAh6fxSxlxiXYBFi4S0XwlhOTLLzKwDlVcqgHVGSlJRJ2lFRfazqq2XcQ_ZG_q-Q21VSzpdOGPLTH_9Q8zI/s320/IMG_4216.JPG" /></a></div>
<p>
V posledních letech jsem navštívil pár konferencí v česku a začínám mít pocit, že se tu začínají opakovat lidi i témata. Rozhodl jsem se proto poohlédnout se po nějaké "first-class" konferenci v zahraničí, a jelikož jsem si dobře vybral <a href="http://vendavo.cz/">zaměstnavatele</a>, sen se stal skutečností a já mohl strávit dva skvělé dny v Amsterdamu a vidět a mluvit s lidmi, kteří patří celosvětově mezi špičky v IT.
</p>
<p>
Program konference byl rozdělen do více než deseti <a href="http://gotocon.com/amsterdam-2013/tracks/">tracků</a>, mě nejvíc zajímaly tři: <i>Big Data NoSQL Search</i>, <i>Legacy & Big Systems</i> a <i>Bring your own Language</i>. Pokusím se shrnout to nejpodstatnější z mého dvoudenního programu. Bylo toho opravdu hodně a tak rozdělím článek na víc částí. Dnes se dozvíte něco o databázích, příště budeme pokračovat o legacy systémech, machine learningu a agilním hype.
</p>
<h3>Hadoop, Hive a HBase</h3>
<p>
Vypadá to, že základním stavebním kamenem všeho distribuovaného se stává <a href="http://hadoop.apache.org/">Hadoop</a>. Hadoop je distribuovaný souborový systém, který umožňuje data zpracovávat pomocí map-reduce. První den konference byly hned dvě přednášky o nástrojích na Hadoopu postavených.
</p>
<p>
Friso van Vollenhoven představil <a href="http://hive.apache.org/">Hive</a>, data warehouse, který umožňuje přistupovat k datům uloženým v Hadoopu pomocí SQL syntaxe. Klasické řešení warehousu spočívá v procesu ETL (= Extract Transform Load). Každá větší organizace má data uložena v několika různých systémech. Aby bylo možné tato data společně analyzovat, je nutné je z těchto systémů vytáhnout (=extract), převést do nějaké jednotné formy (=transform) a nahrát do jedné databáze (=load), které se pak říká warehouse. Protože taková operace není nejrychlejší, provádí se v dávkách, většinou jednou denně. Problém nastane, když potřebujete přidat další sloupec, nebo sledovat data po hodinách a ne po dnech. V těchto případech musíte upravit ETL proces a spustit ho znova.
</p>
<p>
Hive umožňuje ponechat data ve zdrojových formátech (vynechat transform) a dělat dotazy přímo nad textovými soubory v jazyce podobnému SQL (HiveQL). Výhodou je, že Hadoop, na rozdíl od tradičních warehousů, jednoduše a levně škáluje. Podobně jako v relační databázi i v Hive vytváříte tabulky, s tím rozdílem že definujete ze kterého souboru se má načítat a jaký regulární výraz se má použít pro parsování řádku.
</p>
<p>
Další přednáška (Jean-Daniel Cryans) byla o <a href="http://hbase.apache.org/">HBase</a>, což je NoSQL databáze inspirovaná BigTable ale postavená nad Hadoopem. Hlavní výhoda je, že umožňuje ukládat hodně velká data (miliardy řádků x milióny sloupců) na běžném serverovém hardware. Například Facebook ji používá k ukládání messages, nebo Mozilla k analýze crash reportů.
</p>
<p>
V přednášce byl také zmíněn zajímavý nástroj k monitorování distribuovaných aplikací, <a href="http://opentsdb.net/">OpenTSDB</a> (Time Series Database). OpenTSDB je postaven nad HBase a umožňuje jednoduše analyzovat a graficky zobrazit metriky běžícího systému.
</p>
<h3>O pivu a grafových databázích</h3>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk3y-XKthTvD7eoe75ZthJOEGpTEz52eZ8rBUe1u9Q3FH6O_TQr9T0z9nShyK43Le_LYi9rGuqRf3MKYeWQsLlDxMjpM9zJUxpwtP_jESj4iTtlE_Jo-W2OzWlLXu0Uzj6IxXFscozTMk/s1600/IMG_4208.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk3y-XKthTvD7eoe75ZthJOEGpTEz52eZ8rBUe1u9Q3FH6O_TQr9T0z9nShyK43Le_LYi9rGuqRf3MKYeWQsLlDxMjpM9zJUxpwtP_jESj4iTtlE_Jo-W2OzWlLXu0Uzj6IxXFscozTMk/s320/IMG_4208.JPG" /></a></div>
<p>
Sympatický příklad si pro vysvětlení principu grafových databází zvolil Rik van Bruggen. Rik je belgičan a belgičani mají rádi pivo. Ale dělají ho tolik různých druhů, že i oni sami mají problém se v nich vyznat (to se českovi jen tak nestane). A tak vznikla grafová databáze mapující různá piva, pivovary a jejich vlastnosti. Data jsou v grafové databázi uložena v podobě propojených uzlů, přičemž jak uzly, tak hrany mohou mít vlastnosti. Hledání v takové databázi probíhá ve dvou fázích - nejdřív se podle indexu najde výchozí uzel (např. "pivo s názvem Duval") a pak se v jeho okolí hledá pattern (např. "piva vařená stejným pivovarem jako Duval"). Rik o tom sepsal <a href="http://blog.neo4j.org/2013/01/fun-with-beer-and-graphs.html">dost obsáhlý článek</a>, případně je dostupná <a href="http://webexpo.cz/praha2012/prednaska/uvod-do-grafove-databaze-neo4j/">přednáška v češtině</a> z loňského WebExpa.
</p>
<h3>NoSQL neznamená No Schema</h3>
<p>
Na Martina Fowlera jsem se opravdu těšil. Pokládám ho za jednu z největších osobností IT. Jeho knihu <a href="http://martinfowler.com/books/eaa.html">Patterns of Enterprise Application Architecture</a> by měl znát každý, kdo se chystá navrhnout aplikaci nad relační databází, a neuškodí ani nikomu, kdo na takové aplikaci pracuje (aspoň by bylo víc lidí kteří znají <a href="http://martinfowler.com/eaaCatalog/identityMap.html">identity map</a>). Jeho <a href="http://martinfowler.com/">webové stránky</a> jsou nevyčerpatelnou studnicí moudrosti v podstatě o čemkoliv, co se týká vývoje enterprise software, navíc byl jedním ze zakládajících signatářů <a href="http://agilemanifesto.org/">dokumentu</a>, který odstartoval agilní revoluci.
</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi81ftiAgX8BS0_PbSX1MW8st3_JK-DwUibNRTFW2SHLU94afgEMRtIQ3_e0vcnAjrmxWJDBqPIuUvzatRtHeg9kUkywtiv9lD__fJuq9xl8db3DoKRLI82cKBFqQpw5Ja6pZKwtdfs-3g/s1600/IMG_4250.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi81ftiAgX8BS0_PbSX1MW8st3_JK-DwUibNRTFW2SHLU94afgEMRtIQ3_e0vcnAjrmxWJDBqPIuUvzatRtHeg9kUkywtiv9lD__fJuq9xl8db3DoKRLI82cKBFqQpw5Ja6pZKwtdfs-3g/s320/IMG_4250.JPG" /></a></div>
<p>
Zřejmě kvůli vysokým očekáváním mě jeho přednáška, nazvaná Schemaless, trochu zklamala. Ne že by byla špatná, nebo špatně přednesená, naopak, Martin má přesně utříděné o čem mluví a dokáže to podat velmi srozumitelným způsobem. Jen mi přišlo že obsah je poněkud banální a nepřináší nic nového. Nakonec, posuďte sami: Přednáška byla téměř identická s první částí <a href="http://youtu.be/8kotnF6hfd8?t=2m20s">tohoto videa</a> (čas 2:20-25:50), a to včetně slovních obratů a úvodních vtípků.
</p>
<p>
Hlavní informací bylo, že ačkoliv v NoSQL databázích nemusíte definovat žádné schema, data přesto implicitní schema obsahují. Argumenty, že absence schema je výhoda, jsou liché, protože pokud se nechcete dostat do problémů s různými verzemi dat v databázi, musíte data držet konzistentní a data migrovat na novou verzi schema, stejně jako v relační databázi musíte spouštět upgrade scripty. Implicitní schema je špatná věc, a proto se i v NoSQL databázích snažte schema vyjádřit explicitně. Další body přednášky (jak držet dynamické vlastnosti v objektech a kontextuální validace) považuju za všeobecně známé, pokud vás zajímají detaily, pusťte si tu přednášku.
</p>
<p>
<b>Pokračování příště.</b>
</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com1tag:blogger.com,1999:blog-3058726084145190258.post-39321871322134245352013-05-15T00:32:00.001+02:002013-05-15T00:32:22.121+02:00Jak v javě strukturovat testy velkých tříd<p>
Pro každou třídu typicky existuje jedna testovací třída. Tento klasický pattern má ale nevýhodu, když testujete velkou třídu, která se používá v různých scénářích. Testovací třída pak obsahuje mnoho testů, setup různých situací vyžaduje ještě další metody navíc. Pokud mají vaše testovací třídy mnoho metod, měli byste se zamyslet, jak je lépe strukturovat.
</p>
<a name='more'></a>
<p>
Samozřejmě, můžete namítnout, že pokud má třída mnoho metod nebo se používá v různých scénářích, je to jasné porušení Single Responsibility Principle a to hlavní o co byste se měli snažit, je definovat zodpovědnost a extrahovat vše nesouvisející do jiných tříd. Jenže jsou minimálně dvě situace, kdy to dost dobře nejde:
<ol>
<li>Controller třídy v běžných MVC frameworcích (ASP.NET MVC, Spring MVC, Grails...), které mají pro každou obsluhovanou URL a HTTP verb (GET, POST) jednu metodu.</li>
<li>Píšete regresní test, který má zafixovat chování části aplikace. Typicky se nejedná o čistý unit test (kde testujeme jednu třídu v izolaci), ale o integrační test, který má fungovat jako záchranná síť pro refaktoring.</li>
</ol>
</p>
<p>
Abychom si ukázali o co jde na konkrétním příkladu, vypůjčíme si příklad použitý v <a href="http://haacked.com/archive/2012/01/02/structuring-unit-tests.aspx">diskusi na stejné téma</a> ve světě C# (přeložený do javy):
<pre class="brush: java;">public class Titleizer {
public String titleize(String name) {
if (name == null || name.length() == 0)
return "Your name is now Daniel the Foolish";
return name + " the awesome hearted";
}
public String knightify(String name, boolean male) {
if (name == null || name.length() == 0)
return "Your name is now Sir Jester";
return (male ? "Sir" : "Dame") + " " + name;
}
}
</pre>
Co konkrétně tato třída dělá není důležité, hlavní je, že obsahuje dvě velmi nezávislé metody. Každá z nich ale bude potřebovat několik testů a my bychom rádi, aby byly testy pro každou z nich nějak seskupené.
</p>
<p>
Trik který na to ve zmiňovaném článku použili, je vytvořit pro každou metodu vnořenou třídu a seskupit do ní její testy. Funguje to v xUnit i v NUnit a takhle se to dá napsat v JUnit:
<pre class="brush: java;">import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
@RunWith(Enclosed.class)
public class TitleizerTest {
public static class TheTitleizeMethod {
@Test
public void returnsDefaultTitleForNullName() {
// Test code
}
@Test
public void appendsTitleToName() {
// Test code
}
}
public static class TheKnightifyMethod {
@Test
public void returnsDefaultTitleForNullName() {
// Test code
}
@Test
public void appendsSirToMaleNames() {
// Test code
}
@Test
public void appendsDameToFemaleNames() {
// Test code
}
}
}
</pre>
Všimněte si anotace <span class="code">@RunWith(Enclosed.class)</span>. Bez té vám test nepoběží (ba dokonce spadne kvůli legendární hlášce "No runnable methods"). Naopak s touto anotací bude i v IntelliJ krásně vidět struktura testovacích metod:
</p>
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5DHM3QVOqPMNXuIjTGpTmT-7-E9CfbR8X5Ltna6ZrFzf9PH-qFVS4-retplQzZ4Zhf6ATt3YAhxh0eD3hp-YIYSoe-aaz2t0jQOpjKcw6UJEWZAvifgg7zMCGKjpczcWJk3jqQFyO4W4/s1600/enclosedidea.png" />
<p>
A teď si představte, že vaše třída potřebuje nějaký netriviální setup, který je potřeba pro každou z testovaných metod nějak modifikovat. Takže bychom chtěli sdílenou <span class="code">@Before</span> metodu, která by provedla setup společný pro všechny scénáře, a pak další <span class="code">@Before</span> metodu pro každou vnořenou třídu. V C# to jde vyřešit tak, že vnořené třídy <a href="http://zendeveloper.blogspot.cz/2012/01/structuring-unit-tests.html">dědí z hlavní test třídy</a>. To v JUnitu nejde (věřte mi, zkoušel jsem to), ale můžeme si pomoci abstraktní base třídou a malým trikem:
<pre class="brush: java;">@RunWith(EnclosedDoneRight.class)
public class TitleizerTest {
public static abstract class TitleizerScenarioBase {
protected Titleizer titleizer;
public void setUp() {
titleizer = new Titleizer();
}
}
public static class TheTitleizeScenario extends TitleizerScenarioBase {
private String defaultTitle;
@Before
@Override
public void setUp() {
super.setUp();
// let's pretend that defaultTitle is required by all tests of titleize()
// and it takes more effort to create it
defaultTitle = "Your name is now Daniel the Foolish";
}
@Test
public void returnsDefaultTitleForNullName() {
String title = titleizer.titleize(null);
assertThat(title).isEqualTo(defaultTitle);
}
}
}
</pre>
Je to samozřejmě triviální příklad, ale představte si že obě metody <span class="code">setUp()</span> mají kolem dvaceti řádků a vytvářejí několik objektů nutných pro test. Pak má smysl jednotlivé vnořené třídy považovat za <b>scénáře</b>, které definují určitou výchozí situaci, která vyžaduje několik objektů (collaborators) v určitém stavu.
</p>
<p>
Všimli jste si malého háčku? Ano, už ke spuštění testů nemůžeme použít runner <span class="code">Enclosed</span>! Test by spadnul, protože by se pokoušel spustit i abstraktní třídu <span class="code">TitleizerScenarioBase</span>, která nemá žádné testovací metody (a jsme zpět u <a href="http://weknowmemes.com/wp-content/uploads/2012/03/for-seal-thanks.jpg">"No runnable methods"</a>).
</p>
<p>
Musíme si bohužel napsat runner sami. Naštěstí je to velmi jednoduché:
<pre class="brush: java;">import java.lang.reflect.Modifier;
import java.util.ArrayList;
import org.junit.runners.Suite;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;
public class EnclosedDoneRight extends Suite {
public EnclosedDoneRight(Class<?> klass, RunnerBuilder builder) throws InitializationError {
super(builder, klass, getRunnableInnerClasses(klass));
}
private static Class<?>[] getRunnableInnerClasses(Class<?> klass) {
ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
for (Class<?> inner : klass.getClasses()) {
if (!Modifier.isAbstract(inner.getModifiers()))
classes.add(inner);
}
return classes.toArray(new Class<?>[classes.size()]);
}
}
</pre>
Mimochodem stejně je udělaný runner <span class="code">Enclosed</span>, ale ten prostě vezme vše co vrátí <span class="code">klass.getClasses()</span>.
</p>
<p>
A je to! Můžeme psát složité setupy, vyloučit duplicitu a mít strukturované testy. Co vy na to komunito?
</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com0tag:blogger.com,1999:blog-3058726084145190258.post-3208375091822736452013-05-02T22:44:00.000+02:002015-08-28T04:56:26.975+02:00Co s novým mekbůkem aneb rady pro konvertity<p>
Dnes je to přesně rok, co jsem v mém novém zaměstnání ve <a href="http://www.vendavo.cz/">Vendavu</a> rozbalil krabici s novým MacBook Pro a stal se po letech s Windows spokojenou apple ovcí. Nikdy jsem toho nelitoval, i když se mi občas zastesklo po některých okenních vychytávkách. Pro zrádce a renegáty jako jsem já tu mám pár tipů, jak si ulehčit nový začátek.
</p>
<a name='more'></a>
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOuZ9cAO7HRXM_DE9XgqdyKksNj0U9lcKQvoqrVXEK0rm90TB0RtmDNIEmDCVlAogMvZqI9WibOKjn3WNTQFqMbXHVAclECtG02HkoKly842c_C6gfkvlNl-CPq9yEY1_mXiTdq5rI8Lk/s320/photo+1.JPG" />
<h3>Ohýbáme mek</h3>
<p>Trackpad je na mekbůku radost používat. Je velký, citlivý a dokáže registrovat všech pět prstů naráz, čehož je chytře využito pro podporu gest. Nemá žádná tlačítka, místo pravého tlačítka se používají dva prsty najednou. U nového meka se ale musí trackpad mačkat, což je dost otravné. Otevřte proto System Preferences - Trackpad a zapněte <b>Tap to click</b>. Doporučuju rovněž povolit <b>Three finger drag</b>, a to co nejdřív, protože se tím mění některá další gesta. System Preferences - Trackpad funguje zároveň jako tutorial s krátkými animacemi, kde se dozvíte jak se gesta používají. Osobně se mi trackpad tak zalíbil, že vůbec nepoužívám myš. V práci na stole mám externí klávesnici a externí trackpad.
</p>
<p>Pokud budete programovat v nějakém "velkém" IDE (IntelliJ, Eclipse), budete dost často potřebovat funkční klávesy F1-F12. Na Macu jsou standardně namapovány na klávesu fn, protože normální lidi častěji pouští hudbu než kompilují. Změnit to můžete v System Preferences - Keyboard - Use all F1, F2, etc. keys as standard function keys.
</p>
<p>Z neznámých důvodů je ve výchozím nastavení vypnuté ovládání tlačítek v dialogových oknech z klávesnice. Např. vyskočí hláška že nemáte uložený soubor s obligátní otázkou a třemi možnostmi, a vy musíte použít myš, pokud chcete zvolit "neukládat". Naštěstí to jde zapnout v System Preferences - Keyboard - Keyboard Shortcuts. Dole v sekci "Full Keyboard Access" zvolte "All controls".
</p>
<h3>Best of Windows, on Mac</h3>
<p>Přiznejme si to, i Windowsy mají své světlé stránky. Nebudeme to samozřejmě nikde řikat nahlas, protože jsme apple ovce a windowsákům se smějeme. Ale například <b>aero snap</b> je skvělá věc a "maximalizace" oken na macu je celkem opruz. Existuje celá řada aplikací které umožňují něco podobného. Doporučuju začít s <a href="http://www.boastr.de/">Better Touch Tool</a>, která je zdarma, a umí toho mnohem víc - např. definovat vlastní klávesové zkratky a vlastní gesta na trackpadu.
</p>
<p>
Další nezbytnost je hledání aplikací a souborů. Na Macu se to jmenuje <b>Spotlight</b> a aktivuje Cmd+Space. Dál už to funguje stejně jako na Windows. Největší rozdíl je, že je to v opačném rohu obrazovky než na oknech.
</p>
<p>
Poněkud horší je to s ovládáním menu z klávesnice. Na Windows je možné spustit libovolný příkaz z menu pomocí klávesy Alt a zkratek jednotlivých položek. Na macu se lze dostat do menu stiskem Ctrl+F2, ale nejsou tam žádné zkratky, prostě píšete začátek slova. Pokud se příkazy jmenují podobně, máte prostě smůlu a je lepší použít šipky. Pokud nějaký příkaz z menu používáte často a nemá klávesovou zkratku, můžete si ji vytvořit pomocí již zmíněného <a href="http://www.boastr.de/">Better Touch Tool</a>.
</p>
<p>
<a href="http://historje.tumblr.com/post/48029821492/pozvitimesi-trhcu-na-garmatiku-pravopiz-a-vivoj">Naboteníčka jsou fjúčr!</a> Ale přepínání mezi českou a anglickou klávesnicí není moje hobby. Možná jsem si jen příliš navykl na to, jak to funguje na Windows (volba per aplikace), a nestihl jsem se zmutovat na Mací způsob (volba per okno). Navíc v OSX 10.7 to občas zlobilo, např. v login okně mi to zobrazovalo jinou klávesnici než se právě používala, což je u hesla dost prekérka. Takže jsem nakonec skončil u <a href="http://blog.destil.cz/2012/10/ceska-programatorska-klavesnice-pro-mac.html">české programátorské klávesnice</a> a používám ji úplně všude.
</p>
<h3>Třešně pro programátory</h3>
<p>Patříte-li mezi lid programovací, pak vězte, že jste přechodem na mac pro sebe udělali jeden z nejlepších kroků v životě. Mac OS X je takový hezky převlečený unix, takže má perfektně fungující příkazovou řádku a lomítka správným směrem. Spousta frameworků a nástrojů jsou buď rovnou předinstalované (ruby, python, php, svn, PostgreSQL) a nebo je snadné je nainstalovat pomocí .dmg balíčku.
</p>
<p>
Za pozornost stojí balíčkovací systém <a href="http://mxcl.github.io/homebrew/">Homebrew</a>. Instalace mongodb nebo git je pak záležitostí jednoho příkazu: "brew install mongodb".
</p>
<p>
Přestože je systémový terminál na macu daleko lepší než na windowsech, rovnou si nainstalujte <a href="http://www.iterm2.com/">iTerm2</a>. Má pár příjemných fíčur navíc, zásadní výhoda je chytrá historie příkazů - napíšete začátek a tím nastavíte filtr pro šipku nahoru. Přidejte <a href="https://github.com/robbyrussell/oh-my-zsh">oh-my-zsh</a> a máte pluginovatelný systém s připravenou konfigurací pro <a href="https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins">mnoho nástrojů</a>. Díky tomu např. získáte command completition a barevný prompt pro git. Malý tip: pro git použijte plugin gitfast místo standardního git. Změnit to můžete v souboru ~/.zshrc - hledejte řádek který začíná plugins=.
</p>
<p>
Hodit se bude i jednoduchý editor textu. Výhoda proti velkým IDE je, že startují okamžitě a umí otevřít libovolný soubor z příkazové řádky. Doporučuju zkusit <a href="https://www.sublimetext.com/">Sublime</a> (který je ale placený) a nebo legendární TextMate, který je ve verzi 2 zatím <a href="https://github.com/textmate/textmate">zdarma</a>.
</p>
<p>
Další nástroje, které používám pravidelně každý den, jsou <a href="http://sourcetreeapp.com/">SourceTree</a> pro vizuální práci s gitem a <a href="http://www.perforce.com/product/components/perforce-visual-merge-and-diff-tools">p4merge</a> pro mergování a diff (jak ho nastavit je popsáno <a href="http://zbyhoo.eu/2011/09/27/git-merge-and-diff-with-p4merge-on-mac/">třeba zde</a>).
</p>
<h3>Kdo to tebe kamenem, ty po něm mekem</h3>
<p>Konečně máte počítač, který se nemusíte stydět vytáhnout v kavárně a na konferenci! Gratuluju! Teď už zbývá jen si ho oblíbit. Nevím jak vy, ale já si musím na každou novou věc zvykat. Ať už je to telefon, auto nebo počítač, ze začátku mi přijde "takovej divnej". Prvotní rozpačitý pocit se postupně mění v oblibu, ale trvá mi většinou několik měsíců, než novou věc přijmu jako "moji". S macbůkem stačilo pár dnů. Je to prostě hezká a bytelná věc:)
</p>
<p>
Jedna z věcí, která mi ale dodnes vadí, je velmi ostrá přední hrana, tam kam si při psaní pokládám zápěstí. Apple prostě dává přednost designu před použitelností. Ale naštěstí existuje návod, jak tento <a href="http://youtu.be/NnGAlf1hjs4">problém odstranit</a> (bez záruky)! No řekněte, který jiný notebook můžete strojařsky obrábět?
</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/NnGAlf1hjs4" frameborder="0" allowfullscreen></iframe>
<p><b>Edit 2013-08-16</b></p>
<p>Klávesnice na macbůku má vlevo dole v rohu klávesu Fn. To je docela nepříjemné, pokud používáte externí klávesnici, kde je na stejném místě Ctrl. Pokud si navyknete na klávesové zkratky s Ctrl (a třeba IntelliJ jich má požehnaně), pak musíte na notebooku a externí klávesnici šahat na jiná místa. Nedávno jsem narazil na program <a href="https://pqrs.org/macosx/keyremap4macbook/index.html.en">KeyRemap4MacBook</a>, který umožňuje na Fn namapovat Ctrl, navíc to dělá inteligentně, takže si nerozbijete funkční klávesy jako ovládání hlasitosti. Tento a další triky najdete ve skvělém <a href="http://stevelosh.com/blog/2012/10/a-modern-space-cadet/">článku o klávesnicích</a>.</p>
<p><b>Edit 2015-08-27:</b></p>
<p>Na posouvání oken teď používám <a href="http://spectacleapp.com/">Spectacle</a>. Klávesy už nepřemapovávám, místo full-size klávesnice, kde je přehozený Fn a Ctrl jsem si pořídil menší klávesnici s přesně stejným rozložením, jako má macbook. Český programátorský layout přestal po nějakém updatu fungovat, tak používám normální U.S. a Českou QWERTY klávesnici, přidal jsem si akorát klávesovou zkratku pro jejich přepínání. Díky tomu teď častěji píšu česky:)</p>
Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com3tag:blogger.com,1999:blog-3058726084145190258.post-20076935900216889372013-03-30T18:45:00.002+01:002013-03-30T18:45:32.724+01:00Jak udělat kráter Meteorem<p>
<a href="http://meteor.com/">Meteor</a> je moc príma hračka, která dost zjednodušuje vývoj webových aplikací. Zatím se hodí jen na prototyping a na pokusy na lidech, ale <a href="http://meteor.com/about/mission">ambice autorů</a> jsou vysoké a držím jim palce. Zkuste si to taky, je to jednoduché a zábavné!
</p>
<a name='more'></a>
<p>
Inspirován kolegou <a href="http://www.youtube.com/watch?v=GWOt9nu4afQ">Honzou Rudovským</a> jsem si připravil krátké představení Meteoru pro studenty naší obecní univerzity, které proběhlo v rámci akce HIT Kariéra.
</p>
<br/>
<iframe width="640" height="480" src="http://www.youtube.com/embed/I9K8a9oOSRs" frameborder="0" allowfullscreen></iframe>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com0tag:blogger.com,1999:blog-3058726084145190258.post-63015108697707646352013-02-17T13:20:00.000+01:002013-02-17T21:03:47.981+01:00Jak správně pojmenovat test<p>
Na názvu testu záleží! Může se to zdát jako nepodstatná banalita, ale není. Správná metoda pojmenovávání testů zlepší čitelnost testů a pomáhá psát čistší kód. Názvy vašich testů prozrazují, jaký máte přístup k testování.
</p>
<a name='more'></a>
<p>
Vymýšlet názvy tříd a metod je těžké, dokonce tak těžké, že to zmiňuje i nejklasičtější anektoda o informatice: <i>"There are only two hard problems in Computer Science: cache invalidation, naming things, and off-by-one errors."</i> Když vám budou staří praktici tvrdit že "je to jen label" a že prý je důležitější obsah, nevěřte jim. Názvy tříd a metod jsou jako směrové tabule u silnice: Pokud jsou špatné, budete bloudit. Však se taky podívejte na jejich kód plný Helperů, Managerů, Executorů a Utilsů.
</p>
<p>
Existuje několik způsobů jak pojmenovat test:
</p>
<h3>Chaotické začátky</h3>
<p>
První test, který jste v životě napsali, se dost možná jmenoval <span class="code">test1</span>. Je dost pravděpodobné, že druhý a třetí byl <span class="code">test2</span> a <span class="code">test3</span>. Všichni jsme tak začínali. Na tom není nic špatného, pokud takové testy nakonec smažete. Někdo se ale v této fázi zasekne. V těžších případech to může dopadnout takhle:
<pre class="brush: java;">public class ExecutorManagerTest {
@Test
public void testWorks1() { ... }
@Test
public void testFail() { ... }
}
</pre>
Typickým znakem je, že název testovací metody neříká vůbec nic o tom, co se testuje. Obsah většinou nestojí za řeč - ještě jsem neviděl dobře napsaný test s podobným názvem.
</p>
<h3>Co metoda, to test</h3>
<p>
V dalším logickém stadiu skládáme název testu z názvu testované metody a předpony test:
<pre class="brush: java;">public class StringTest {
@Test
public void testIndexOf() { ... }
@Test
public void testReplace() { ... }
}
</pre>
Ze začátku to vypadá logicky, každou metodu chci přece otestovat. Jenže chování metod vykazuje více rysů než jeden a mění se podle hodnot vstupních parametrů, takže v testu často najdeme nejen víc assertů, ale někdy také více volání testované metody:
<pre class="brush: java;"> @Test
public void testIndexOf() {
// tfuj - do not use this!
int i;
i = "cukr kava limonada".indexOf("kava");
assertEquals(5, i);
i = "cukr kava limonada".indexOf("rum");
assertEquals(-1, i);
i = "kafe kafe kafe".indexOf("kafe", 3);
assertEquals(5, i);
}
</pre>
Takové testy jsou dlouhé, špatně čitelné a na sobě závislé, protože jedna testovací metoda ve skutečnosti obsahuje několik testů. Lze se také setkat s jednou kuriózní variací, když existuje více přetížení jedné metody (česky overloadů). Pak lze vidět testy jako <span class="code">testIndexOfString()</span> a <span class="code">testIndexOfStringInt()</span>.
</p>
<p>
Ano, v raných dobách testovacích se takto testy opravdu psaly. Je to ale způsob nemotorný, protože název testu nic neříká o tom, co se vlastně testuje. Je to způsob sporný, protože když budete striktní a budete vyžadovat ke každé metodě test, budete se muset nějak vypořádat s privátními metodami. Dokonce si myslím, že dříve rozšířený názor, že testovatelnost jde na úkor zapouzdření, byl způsoben právě zoufalou snahou napsat ke každé metodě, privátní nevyjímaje, test.
</p>
<h3>Formalizované metody</h3>
<p>
Název testu se dá také tvořit z nějaké šablony. Velmi známý je např. standard, se kterým přišel Roy Osherove, a používá šablonu <a href="http://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html">UnitOfWork_StateUnderTest_ExpectedBehavior</a>, volně přeloženo do češtiny CoTestuju_VychoziPodminky_OcekavaneChovaniNeboVysledek:
<pre class="brush: java;">public class StringTest {
@Test
public void indexOf_containsSearchedString_returnsCorrectIndex() { ... }
@Test
public void indexOf_doesNotContainSearchedString_returnsMinusOne() { ... }
}
</pre>
Tato metoda má několik výhod. Dává vám jasný návod, jak tvořit název testu. Díky tomu nemusíte pokaždé tápat, jednoduše použijete šablonu. Názvy testů jsou velmi konzistentní a pro toho, kdo zná šablonu, i lehce čitelné. Počítá se přitom s tím, že u jedné metody je potřeba otestovat několik vstupních stavů a různých chování, takže testy mohou být krátké, zaměřené na problém (focused) a v ideálním případě obsahují jen jeden assert.
</p>
<p>
Nevýhoda se skrývá v samotném principu této metody. Díky tomu, že nemusíte o názvu testu moc přemýšlet, vás nic nenutí přemýšlet ani o tom, co vlastně má testovaná třída dělat a jestli toho náhodou nedělá moc. Prostě bušíte testy pro všechny veřejné metody, jde vám to od ruky a nepřijde vám divný, že test má najednou 50 metod a 1000+ řádků.
</p>
<h3>Test jako živá dokumentace</h3>
<p>
Zastánci TDD tvrdí, že nejlepší dokumentací kódu jsou unit testy. Popisují způsob užití tříd, navíc je to dokumentace živá, která nemůže zastarat - v takovém případě by "spadl build" na continuous integration serveru (pokud nemáte CI server a nespouštíte testy po každém commitu, pak nechápu proč testy píšete). Jenže když se podíváte na testy "běžné kvality", bývá to dokumentace dost nečitelná. Jak to změnit?
</p>
<p>
<strong>Pište názvy testů tak, jako by to byl seznam fíčur testované třídy.</strong> Každý test je jako věta, která předpokládá název testované třídy jako podnět. Příklad:
<pre class="brush: java;">public class ListTest {
@Test
public void holdsItemsInTheOrderTheyWereAdded() { ... }
@Test
public void canHoldMultipleReferencesToTheSameItem() { ... }
@Test
public void throwsAnExceptionWhenRemovingAnItemItDoesntHold() { ... }
}
// Example from <a href="http://www.growing-object-oriented-software.com/">GOOS book</a>
</pre>
Výhodou této metody je čitelnost. Každý test se dá přečíst jako normální anglická věta: "List holds items in the order they were added". Stačí přečíst názvy testů abychom věděli, co má testovaná třída dělat. Každé IDE má příkaz "collapse all", který schová kód testů a nechá zobrazené jen názvy (v IDEA je to cmd shift -). Existují dokonce generátory, které z takto zapsaných testů vygenerují dokumentaci v HTML formátu.
</p>
<p>
Je tu ještě jedna, na první pohled méně zjevná výhoda. Když považujete test za seznam fíčur testované třídy, nutí vás to stále přemýšlet, co ještě je její zodpovědnost a co už ne. Jinými slovy, usnadňuje to dodržování <a href="http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)">single responsibility principle</a>. Stačí názvům testů věnovat dostatečnou pozornost a ony vám samy napoví, kdy už toho třída dělá moc a měli byste se zamyslet, jak kód lépe rozdělit.
</p>
<p>
Tato konvence se jmenuje <a href="http://en.wikipedia.org/wiki/TestDox">TestDox</a> (někdy AgileDox). Díky tomu, že obrací vaši pozornost na design testované třídy, pomáhá psát čitelnější a udržovatelnější kód. Není náhoda, že novější testovací knihovny upřednostňují tento způsob. V javascriptu je to <a href="http://pivotal.github.com/jasmine/">celkem</a> <a href="http://visionmedia.github.com/mocha/">běžná</a> <a href="http://vowsjs.org/">věc</a>.
</p>
<h3>Závěrem: o prefixech</h3>
<p>
V Javě je zvykem, že všechny testovací metody začínají prefixem "test". Je to historický relikt starých verzí JUnitu z doby, kdy v Javě nebyly anotace. Nový JUnit (od verze 4) ani TestNG nic takového nevyžadují, tak to prosím nedělejte. Zvlášť ošklivě působí prefix test ve spojení s anotací: <span class="code">@Test public void testBlah()</span>. Stejně tak se šíří zvyk používat prefix <a href="https://github.com/mockito/mockito/blob/master/test/org/mockito/verification/TimeoutTest.java">"should"</a>. Není problém si tímto slovem občas pomoct, ale vyžadovat to jako konvenci je podle mě zbytečné. Připomíná to <a href="http://en.wikipedia.org/wiki/Hungarian_notation">Maďarskou notaci</a> dávno zapomenuté minulosti.
</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com12tag:blogger.com,1999:blog-3058726084145190258.post-36127987444334019562012-10-12T13:54:00.000+02:002012-10-12T14:05:37.624+02:00TDD Outside-in v Ostravě<p>Předevčírem jsem na <a href="http://jug-ostrava.blogspot.cz/">JUG Ostrava</a> mluvil o unit testech a test-driven vývoji. Na rozdíl od WebExpa jsme měli víc času, a tak jsem se dostal i k tomu, proč nejsou unit testy dobré k odhalování chyb, co vám unit testy říkají o testovaném kódu a v čem je jejich nejsilnější stránka.
</p>
<a name='more'></a>
<p>Záznam z přednášky už je na webu (děkuji <a href="http://ondrej-kvasnovsky.blogspot.cz/">Ondrovi Kvasnovskému</a> za bleskové zpracování). Pokud jste viděli moji přednášku na WebExpu, přetočte na čas 26:40, kde začíná druhá část, na kterou se na WebExpu nedostalo.
</p>
<p>Odkazy k přednášce najdete v mém <a href="http://blog.kolman.cz/2012/09/odkazy-k-prednasce-o-tdd-outside-in.html">předchozím postu</a>. Na závěr jsem v diskuzi zmiňoval <a href="http://channel9.msdn.com/Events/Patterns-Practices-Symposium-Online/Patterns-Practices-Symposium-Online-2012/Closing-Keynote-Big-Transitions-in-Small-Steps">toto video</a> od Kenta Becka, ve kterém vysvětluje, jak dělat velké změny po malých a bezpečných krocích.
</p>
<p>V přenášce jsem zapomněl zmínit jednu věc. Pokud píšete unit testy zpětně, tedy až když máte hotový kód, přicházíte o tu největší srandu. Přínosy unit testů se nejvíc projeví právě když jste test-driven, čili píšete nejdřív testy, pak kód. Já jsem stále víc přesvědčen o tom, že psát unit testy zpětně je zbytečné a že lidé, kteří tak programují, hledají u unit testů něco, co jim nemůžou poskytnout. Pokud si myslíte že to nejde nebo že to neumíte, tak trénujte! Nikdo učený z nebe nespadl a stojí za to se to naučit.
</p>
<iframe src="http://player.vimeo.com/video/51208668?title=1&byline=1&portrait=1" width="665" height="499" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe> <p><a href="http://vimeo.com/51208668">Test Driven Development - Outside-in, Daniel Kolman</a> from <a href="http://vimeo.com/jugostrava">Java User Group - Ostrava</a> on <a href="http://vimeo.com">Vimeo</a>.</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com6tag:blogger.com,1999:blog-3058726084145190258.post-64643952102492376132012-09-21T15:20:00.000+02:002012-09-21T15:20:00.787+02:00Odkazy k přednášce o TDD Outside-in<p>Pokud vás zaujala moje přednáška na WebExpu, mám pro vás několik tipů, kde hledat další informace.</p>
<a name='more'></a>
<p>Nejlepší věc, kterou pro sebe jako programátoři můžete udělat, je přečíst si knížku <a href="http://www.growing-object-oriented-software.com/">Growing Object-Oriented Software Guided by Tests</a>. Žádná jiná kniha neměla takový vliv na můj styl programování. Dozvíte se v ní, jak udělat testy čitelnější, co vám testy říkají o testovaném kódu a ukázku kompletního TDD cyklu, včetně end-to-end testů.</p>
<p>Špatné unit testy jsou někdy horší než žádné unit testy. Bohužel, psát špatné unit testy je velmi jednoduché, zvlášť pokud si stále ještě myslíte, že cílem unit testů je hledání chyb. Článek <a href="http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/">Writing Great Unit Tests: Best and Worst Practices</a> od Stevena Sandersona se pokouší vysvětlit, jak to s těmi testy je a přidá pár tipů, jak psát dobře.</p>
<p>Pokud máte rádi teorii, přečtěte si článek <a href="http://martinfowler.com/articles/mocksArentStubs.html">Mocks Aren't Stubs</a> od Martina Fowlera. Nejzajímavější je část, která se věnuje rozdílu mezi "klasickým" a "mockistickým" způsobem testování a jejich vlivu na to, zda testujeme stav nebo chování.</p>
<p>Kód, který se dobře testuje, je také většinou dobře strukturovaný čistý kód. Jak takový kód psát se dozvíte v knize <a href="http://www.amazon.com/gp/product/0132350882/">Clean Code</a>. Napsal ji Uncle Bob Martin, který také na toto téma natočil <a href="http://www.cleancoders.com/">sérii podcastů</a>. Velmi dobrá je také příručka <a href="http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf">Guide: Writing Testable Code [pdf]</a> od Miško Heveryho.</p>
<p>A nejdůležitější je trénovat! Co načtete ve výše uvedených zdrojích si nejdřív vyzkoušejte bokem na nějakém zkušebním kusu kódu. Pokusit se psát dobré unit testy na staré spaghetti-like aplikaci je to nejtěžší, co můžete udělat. Nebo se zúčastněte nějakého <a href="http://coderetreat.cz/">code retreatu</a>, kde si kromě testování vyzkoušíte i párové programování a poznáte zajímavé lidi.</p>
Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com1tag:blogger.com,1999:blog-3058726084145190258.post-16993320608858305962012-04-18T23:54:00.001+02:002012-04-18T23:54:50.912+02:00Jak na integrační testy s databází<p>Nejlepší integrační test je žádný integrační test. Jenže na okrajích našeho systému, tam kde naše aplikace komunikuje s ostatními aplikacemi, je integrační test potřeba. A nebo pracujeme s hnusným starým kódem, ze kterého je těžké odstranit závislosti na databázi a nemáme čas to předělávat. A protože nejčastější případ je integrační test s databází, mám pro vás pár zkušeností, které se osvědčily.</p> <a name='more'></a> <p>Testy s databází mají spoustu nevýhod, které jsou všeobecně známé, takže je zmíním jen ve stručnosti: Jsou pomalé, vyžadují komplexní setup (práva, connection string, databáze se správným schema, testovací data...), jsou závislé na prostředí (databázový server), nejdou paralelizovat a není jednoduché je napsat dobře (zejména se zapomíná na vzájemnou izolaci testů).</p> <p>Někdy je ale použít musíte. Znám dva dobré důvody, kdy je použít:</p> <ol> <li>Vaše aplikace spoléhá na abstraktní "datovou vrstvu". Pak je dobré každou operaci, kterou má datová vrstva vykonávat, otestovat integračním testem, abychom měli jistotu, že to bude fungovat se skutečnou databází. Příklad: V datové vrstvě je abstraktní třída RepositoryBase, která implementuje rozhraní IRepository a umí provádět základní operace jako List, Add, Remove a Query. Vyplatí se otestovat, že tato base třída je skutečně schopná tyto operace vykonávat nad reálnou databází. Testy doménové vrstvy pak můžeme v klidu psát s mockovanou IRepository.</li> <li>Přístup k datům je špatně vyřešen a nejde jednoduše zamockovat. Došlo vám to pozdě anebo jste přišli k hotové věci a aplikace už je tak rozsáhlá, že nejste schopni to v rozumném čase opravit. V takovém případě je lepší psát integrační testy než žádné testy. Měli byste ale současně vymyslet, jak se postupným refaktoringem propracovat k lepšímu řešení.</li></ol> <p>Až budete přecházet na novou verzi databázového serveru nebo ORM knihovny, kvalitní integrační testy vám ušetří mnoho času a nervů.</p> <h3>Izolace testů</h3> <p>Základní pravidlo, které je nutné dodržet, pakliže si chceme udržet mentální zdraví, je, že se testy nesmí navzájem ovlivňovat. U integračních testů to znamená, že <strong>výchozí stav databáze musí být na začátku každého testu stejný</strong> a že <strong>v jeden okamžik může nad jednou databází běžet maximálně jeden test</strong>.</p> <p>Testy proto spouštíme nad práznou databází, která může obsahovat jen statická data. Toho můžeme dosáhnout různými způsoby:</p> <ul> <li>Máme k dispozici backup prázdné databáze a před každým testem provedeme restore. Tento přístup by v zásadě mohl fungovat, v praxi to ale bývá dost složité a pomalé.</li> <li>Každý test pouštíme v transakci, kterou na konci rollbackujeme. Toto je častý přístup, ale má několik nevýhod: Nenajde chyby, které nastanou až při commitu transakce, nemůžete testovat kód vyžadující více transakcí a pokud něco selže, nemůžete se podívat do databáze na data, která kód vytvořil. Například ověření hesla uživatele provádíme vždy v samostatné transakci, která je commitnuta i v případě, že operace celkově selže. To proto, že při zadání špatného hesla chceme inkrementovat <em>invalidLoginAttemptCount</em> a rollback by nám tuto změnu zahodil.</li> <li>Na začátku každého testu se spustí SQL skript, který vymaže všechny řádky ze všech tabulek. Je to velmi rychlé, testovaný kód může používat transakce jak se mu zlíbí a po neúspěšném testu lze v databázi analyzovat, co se vlastně stalo. Jediné na co je třeba dávat pozor, je pořadí, ve kterém se tabulky promazávají, aby nedošlo k porušení referenční integrity. Když už se vám to ale stane, velmi rychle na to přijdete – setup testu selže. Tento přístup se nám osvědčil nejvíce.</li> <li>Každý test si vytvoří svoji vlastní databázi. V praxi jsem se s tím nesetkal, ale pokud by to nebylo moc pomalé, byla by to ideální volba – není lepší způsob, jak zajistit izolaci testů.</li></ul> <p>Tím jsme i odpověděli na otázku, proč mazat data před testem a ne po testu – abychom měli v případě selhání testu možnost podívat se do databáze na výsledná data.</p> <h3>Setup prostředí</h3> <p>Při startu integračního testu je potřeba nastavit prostředí podobně, jako je to v reálném produkčním prostředí. Například je nutné nakonfigurovat datovou vrstvu správným connection stringem a dalšími vlastnostmi, které jsou uloženy v konfiguračním souboru. Abychom dosáhli maximální podobnosti setupu testů a produkčního prostředí, je dobré mít v každé vrstvě bootstrapper, který se stará o její nastartování. Například v datové vrstvě je třída DataBootstrapper, která vytvoří NHibernate session factory podle údajů z konfiguračního souboru. Ideální pak je, když je čtení z konfiguračního souboru odstíněné nějakým rozhraním, aby v testu nebyl žádný konfigurační soubor potřeba. Nám se to zatím nepovedlo, takže máme jeden app.config který linkujeme do všech projektů, kde probíhají integrační testy.</p> <h3>Setup testovacích dat</h3> <p>Každý test začíná s prázdnou databází a musí si nejprve vytvořit data, nad kterými bude operovat. Nejlepší je použít k tomu test data builder objekty, o kterých jsem <a href="http://blog.kolman.cz/2012/04/jak-zjednodusit-testy-s-buildery.html">psal minule</a>. Pokud je testovaná situace složitá a objektů, které je potřeba vytvořit je mnoho, vyplatí se setup dat sdružit do něčeho, čemu říkáme scénáře. Úkolem scénáře je vytvořit nějakou konkrétní situaci, například "uživatel s nákupním košíkem, který obsahuje produkt, jemuž se od přidání do košíku změnila cena". </p> <h3>Databáze</h3> <p>Kde vlastně vzít databázi, nad kterou testy spouštíme? Nejjednodušší je použít nějakou předem existující databázi. Například náš build script nejdříve zkompiluje kód, pak pomocí SQL skriptů vytvoří prázdnou databázi a nakonec spustí testy. Testy proto mohou spoléhat na to, že databáze se správnou strukturou už existuje.</p> <p>Ideální je, když si build nebo test sám vytvoří vše co potřebuje. Nejlepší by bylo použít embeddovanou databázi, která ukládá data do souboru, například SQL Server Compact. My jsme se k tomu bohužel ještě nedostali, takže vytváříme na normálním SQL Serveru databázi s nějakým jménem, kterou pak použije i test. Má to ale jednu velkou nevýhodu: Když uděláte branch, nesmíte zapomenout změnit název databáze! Jinak poběží testy trunku i branche na jedné databázi, což porušuje izolaci testů a povede k velmi matoucím chybám.</p> <h3>Závěr</h3> <p>Ať už budete v testu pracovat s databází jakkoliv, myslete vždy na to, že testy se nesmějí navzájem ovlivňovat.</p> Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com4tag:blogger.com,1999:blog-3058726084145190258.post-10432118080349528992012-04-10T23:02:00.001+02:002012-04-10T23:19:13.632+02:00Jak zjednodušit testy s buildery testovacích dat<p>Dlouhý a nepřehledný setup může zabít vaše testy. Čím delší setup, tím hůř se test čte a tím víc je náchylný na rozbití při nesouvisejících změnách. Buildery testovacích dat jsou užitečná pomůcka, která pomáhá setup testu zjednodušit. </p> <a name='more'></a> <p>Představme si hypotetickou situaci: Chceme otestovat, že objednávka nejde odeslat, pokud je celková cena vyšší, než zákazníkův předplacený kredit. Vytvořit všechny objekty pro takový test může být dost nepřehledné:</p><pre class="brush: csharp;">[Test]
public void OrderCannotBePlacedWhenCustomerBalanceIsLow()
{
var order = new Order()
{
Customer = new Customer()
{
Balance = 100,
}
};
order.AddItem(new OrderItem()
{
Price = 101
});
...
}
</pre>
<p>A to jsme zatím nastavili jen vlastnosti, které nás zajímají v testu. Často je ale nutné nastavit i další vlastnosti, které sice nejsou zajímavé z hlediska testu, který právě píšeme, ale bez nich test zhavaruje (typicky jsou to validace a databázové constraints (<em>jak se to sakra řekne česky?</em>) u integračních testů):</p><pre class="brush: csharp;">[Test]
public void OrderCannotBePlacedWhenCustomerBalanceIsLow()
{
var order = new Order()
{
Customer = new Customer()
{
Balance = 100,
// zákazník bez loginu není validní,
// login navíc musí být unikátní
LoginName = "neni_dulezite",
// zákazník musí patřit do společnosti
Company = new Company()
{
...
}
}
};
order.AddItem(new OrderItem()
{
Price = 101,
// položka musí mít počet kusů, i když je to pro tento
// test zcela nepodstatné
Items = 1,
// výchozí hodnota datumu v C# nejde uložit do MSSQL
Ordered = DateTime.Now,
// každá položka objednávky musí ukazovat na produkt
Product = new Product()
});
...
}
</pre>
<p>Takový test se hodně špatně čte. Vlastnosti důležité pro test zcela splývají s vlastnostmi, které jsou potřeba jen proto, aby kód prošel. Navíc je to hodně křehké a pokud máte takových testů hodně, pak si při refactoringu užijete hodně srandy (<em><a href="http://youtu.be/kJp2XAWma_I">sarcasm</a></em>).</p>
<p>Když použijeme buildery testovacích dat (test data builders), bude to vypadat o dost lépe:</p><pre class="brush: csharp;">[Test]
public void OrderCannotBePlacedWhenCustomerBalanceIsLow()
{
var order = new OrderBuilder()
.WithTotalPrice(101)
.WithCustomer(
new CustomerBuilder().WithBalance(100).Build()
)
.Build();
...
}
</pre>
<p>Test data builder je pomocná třída, jejímž úkolem je vytvořit validní objekt daného typu. Jeho metody mají fluent interface a umožňují nastavit právě a jen ty vlastnosti, které nás v testu zajímají. Většinou mají dost jednoduchou strukturu, například CustomerBuilder z předchozího příkladu může vypadat třeba takto:</p><pre class="brush: csharp;">public class CustomerBuilder
{
static int _counter = 0;
string _loginName;
public CustomerBuilder WithLoginName(string loginName)
{
_loginName = loginName;
return this;
}
decimal _balance = 0;
public CustomerBuilder WithBalance(decimal balance)
{
_balance = balance;
return this;
}
Company _company;
public Company WithCompany(Company company)
{
_company = company;
return this;
}
public Customer Build()
{
return new Customer()
{
Balance = _balance,
LoginName = _loginName ?? "customer" + (_counter++),
Company = _company ?? new CompanyBuilder().Build(),
Items = 1,
Ordered = DateTime.Now
};
}
}
</pre>
<p>Pro každou vlastnost, kterou chceme nastavovat z testu, obsahuje test data builder jednu metodu. Builder ale může obsahovat i metody, které nastavují víc než jednu vlastnost nebo vztah. Díky tomu, že takový setup uzavřeme do metody, dáme mu jméno, které v testu lépe vyjadřuje náš záměr. Například OrderBuilder obsahuje metodu WithTotalPrice(), i když Order žádnou takovou vlastnost nemá a celková cena se počítá jako součet všech položek objednávky:</p><pre class="brush: csharp;">public class OrderBuilder
{
List<OrderItem> _items = new List<OrderItem>();
public OrderBuilder WithTotalPrice(decimal totalPrice)
{
_items.Add(new OrderItemBuilder().WithPrice(totalPrice).Build());
return this;
}
public Order Build()
{
var order = new Order();
foreach (var item in _items)
{
order.AddItem(item);
}
return order;
}
}
</pre>
<p>V našich projektech používáme tyto buildery v testech pro vytváření doménových objektů a DTO objektů. Protože máme doménu v <a href="http://blog.kolman.cz/2009/03/model-driven-development-v-praxi_5498.html">DSL modelu</a>, jsme dokonce schopni pro každou entitu generovat základní kostru builderu, která obsahuje pro každou vlastnost metodu (např. pokud má třída User vlastnost Name pak vygenerovaný UserBuilder bude mít metodu WithName). Tam, kde není možné buildery generovat, píšeme je postupně podle potřeby.</p>
<p>Naše buildery dokonce poznají, zda jsou použity v unit testu nebo v integračním testu s databází (vytváří je totiž base třída testu). V druhém případě pak vytvořené objekty rovnou persistují do databáze, takže nepotřebujeme žádné šílenosti jako SQL setup script pro každý test nebo databázi předem naplněnou nějakými testovacími daty. Každý integrační test začíná nad prázdnou databází a pomocí těchto builderů si vytvoří taková data, jaká potřebuje (ale o tom třeba někdy příště).</p>
<p>Test data builders jsou užitečná věc, která zpřehledňuje testy, zjednodušuje jejich psaní a činí je odolnější vůči změnám, protože dost často se změna projeví jen v builderu.</p>
<p>Další informace a triky najdete v knížce <a href="http://www.growing-object-oriented-software.com/">Growing Object-Oriented Software Guided by Tests</a>. Pokud jste ji ještě nečetli, je to chyba kterou byste měli co nejdříve napravit, protože je to nejlepší knížka o testování, kterou znám:-) Příklady má v Javě, ale .NET je vlastně skoro to samé, takže jdou s mírnými úpravami použít i v C#. Tato kniha měla opravdu velký a praktický dopad na způsob, jakým dnes píšeme testy.</p> Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com2tag:blogger.com,1999:blog-3058726084145190258.post-29934325237718052372012-04-06T22:51:00.001+02:002013-11-22T14:13:52.336+01:00TDD as if You Meant It<p>Tento přístup k TDD se pokouší být jakýmsi "pravým" TDD, které se vrací ke kořenům. Začal ho propagovat (alespoň pokud je mi známo) Keith Braithwaite na různých konferencích (třeba <a href="http://www.infoq.com/presentations/TDD-as-if-You-Meant-It">zde</a>) a já jsem se s ním poprvé seznámil na Code Retreat v Berlíně. Z nějakého důvodu se tato technika na Code Retreatech často praktikuje, zřejmě proto, že má potenciál dostat z komfortní zóny i člověka, který pravidelně praktikuje TDD a donutit ho myslet jinak, než je zvyklý.</p> <a name='more'></a> <p><em>Disclaimer: Nemám s touto technikou moc velké zkušenosti, protože mi prostě nepřirostla k srdci. Možná mi něco nedochází, ale přijde mi, že kód napsaný tímto způsobem má jisté nedostatky (o tom níže). Budu rád když mě opravíte a napíšete mi, co jsem pochopil špatně.</em></p> <p><strong>TDD as if You Meant It</strong> je založena na striktním dodržování jednoduchých <a href="http://cumulative-hypotheses.org/2011/08/30/tdd-as-if-you-meant-it/">pravidel</a>:</p> <ol> <li>Napište <em>právě jeden</em> nový test, nejmenší možný, kterým se přiblížíte k řešení. <li>Ověřte, že test neprojde. <li><em>Přímo v testovací metodě </em>napište nejjednodušší implementaci, aby test prošel. <li>Refaktorujte, odstraňte duplicitu, vylepšete návrh podle potřeby. Přitom striktně dodržujte, že: <ol> <li>Novou metodu lze vytvořit pouze během fáze 4. a to tak, že buď: <ol> <li>Vytvoříte metodu přímo v testovací třídě tak, že do ní přesunete kód z testu (extract method), nebo <li>Pokud musíte, přesunete kód do již existující metody.</li></ol> <li>Novou třídu lze vytvořit pouze během fáze 4. a to tak, že do nové třídy přesunete již existující metody z testovací třídy.</li></ol></li></ol> <p>Ukážeme si tento postup na zadání z <a href="http://blog.kolman.cz/2012/03/tdd-outside-in.html">minulého příspěvku</a>. Předem bych rád upozornil, že programování není deterministická činnost, takže to, co zde uvidíte, není jediná možná cesta.</p> <h3>Test</h3> <p>Zkusíme implementovat první pravidlo: Mravenec se na bílé buňce otočí doprava, změní barvu buňky a popoleze o jednu buňku vpřed. Začneme tedy tím, že napíšeme test, že se mravenec na bílé buňce otočí doprava:</p><pre class="brush: csharp;">[Test]
public void AntTurnsRightOnWhiteCell()
{
var antOnWhiteCell = true;
var antOrientation = Orientation.North;
// implementaci doplníme v kroku 3
Assert.That(antOrientation, Is.EqualTo(Orientation.East));
}
</pre>
<p>Test neprojde, ani když doplníme enum Orientation, které je nutné k úspěšné kompilaci. Splnili jsme proto bod 2 a můžeme přistoupit k implementaci, kterou napíšeme přímo do testovací metody:</p><pre class="brush: csharp;">[Test]
public void AntTurnsRightOnWhiteCell()
{
var antOnWhiteCell = true;
var antOrientation = Orientation.North;
antOrientation = Orientation.East;
Assert.That(antOrientation, Is.EqualTo(Orientation.East));
}
</pre>
<p>Tím jsme splnili bod 3. Napsat takhle stupidní implementaci může vypadat, že jsme archetypální ajťáci a pravidlo "<a href="http://www.extremeprogramming.org/rules/simple.html">do the simplest thing that could possibly work</a>" chápeme příliš doslovně nebo naopak příliš sarkasticky. No, aspoň je teď zjevné, že náš test <strike>stojí za h*vn*</strike> pokrývá malou část problému. Doplníme proto i ostatní výchozí orientace. Situace si přímo říká o parametrický test:</p><pre class="brush: csharp;">[TestCase(Orientation.North, Orientation.East)]
[TestCase(Orientation.East, Orientation.South)]
[TestCase(Orientation.South, Orientation.West)]
[TestCase(Orientation.West, Orientation.North)]
public void AntTurnsRightOnWhiteCell(Orientation current, Orientation expectedResult)
{
var antOnWhiteCell = true;
var antOrientation = current;
antOrientation = Orientation.East;
Assert.That(antOrientation, Is.EqualTo(expectedResult));
}
</pre>
<h3>Implementace přímo v testu</h3>
<p>Test teď samozřejmě neprojde (vrátili jsme se do bodu 2), takže musíme přepsat implementaci. Využijeme toho, že enumy v C# jsou v podstatě jen pojmenované číselné hodnoty a že se lze dostat mimo rozsah popsaný prvky enumu. Když jsou prvky enumu ve správném pořadí, můžeme proměnnou typu enum inkrementovat, jako by to bylo číslo:</p><pre class="brush: csharp;">[TestCase(Orientation.North, Orientation.East)]
[TestCase(Orientation.East, Orientation.South)]
[TestCase(Orientation.South, Orientation.West)]
[TestCase(Orientation.West, Orientation.North)]
public void AntTurnsRightOnWhiteCell(Orientation current, Orientation expectedResult)
{
var antOnWhiteCell = true;
var antOrientation = current;
antOrientation++;
if (antOrientation > Orientation.West)
antOrientation = Orientation.North;
Assert.That(antOrientation, Is.EqualTo(expectedResult));
}
public enum Orientation
{
North,
East,
South,
West
}</pre>
<h3>Extrahování metod</h3>
<p>Tím jsme splnili podmínky bodu 3. Můžeme přistoupit k bodu 4, refactoring. Vypadá to, že jsme napsali obecnou implementaci otočení směru o 90 stupňů vpravo, tak ji extrahujeme do metody, která bude v testovací třídě:</p><pre class="brush: csharp;">[TestCase(Orientation.North, Orientation.East)]
[TestCase(Orientation.East, Orientation.South)]
[TestCase(Orientation.South, Orientation.West)]
[TestCase(Orientation.West, Orientation.North)]
public void AntTurnsRightOnWhiteCell(Orientation current, Orientation expectedResult)
{
var antOnWhiteCell = true;
var antOrientation = current;
antOrientation = TurnRight(antOrientation);
Assert.That(antOrientation, Is.EqualTo(expectedResult));
}
static Orientation TurnRight(Orientation orientation)
{
orientation++;
if (orientation > Orientation.West)
orientation = Orientation.North;
return orientation;
}
</pre>
<p>Analogicky bychom postupovali pro TurnLeft.</p>
<p>Možná jste si všimli jedné podivné věci. Chceme testovat, zda se mravenec otočí doprava na bílé buňce, ale proměnnou antOnWhiteCell jsme zatím nikde nepoužili. Přišli jsme totiž na to, že abychom mohli otestovat, že se mravenec na bílé buňce otočí doprava, musíme se nejdřív posunout o úroveň abstrakce níž a umět otáčení doprava. Náš test by se dost dobře mohl jmenovat třeba CanTurnRight a proměnnou antOnWhiteCell vůbec nedeklarovat. A to považuju za jeden z problémů <strong>TDD as if You Meant It</strong>: Nutí váš mozek <strong>přepnout na jinou úroveň abstrakce</strong> a řešit detaily implementace dříve, než vyřešíte celý problém na jedné úrovni abstrakce. Místo abychom vyřešili celé pravidlo "mravenec se na bílé buňce otočí doprava", byli jsme donuceni implementovat otáčení samotné a (implicitně) také rozhodnout, jak budeme reprezentovat směr mravence (rozhodli jsme se pro enum, ale mohl by to být třeba vektor – to je ale rozhodnutí, které preferuju odkládat na co nejpozdější okamžik).</p>
<p>Pro srovnání se podívejte na konec předchozího článku o <a href="http://blog.kolman.cz/2012/03/tdd-outside-in.html">TDD Outside-in</a>. Výsledný test zůstává na jedné úrovni abstrakce a nebylo potřeba řešit jak bude reprezentovaná orientace, pole buněk nebo barva. Dokonce i třídu Position jsme bývali mohli nahradit nějakým rozhraním, a nemuseli jsme řešit ani to, o jaký souřadnicový systém se jedná.</p>
<p>Nyní se můžeme zamyslet nad tím, zda by stálo za to sloučit některé metody do nějaké nové třídy. Vypadá to, že jsme vyřešili problém orientace a otáčení, a tak bychom mohli metody TurnRight a TurnLeft přesunout do nějaké třídy, která by tento problém izolovala. Zkusíme vytvořit třeba třídu Direction:</p><pre class="brush: csharp;">public class Direction
{
public static Orientation TurnRight(Orientation orientation)
{
orientation++;
if (orientation > Orientation.West)
orientation = Orientation.North;
return orientation;
}
public static Orientation TurnLeft(Orientation orientation)
{
orientation--;
if (orientation < Orientation.North)
orientation = Orientation.West;
return orientation;
}
}
</pre>
<p>Když se budeme držet bodu 4.2 a přesuneme metody do nové třídy, vznikne něco, co spíš než objekt připomíná "helper" nebo "util", tj. třídu která nemá stav a obsahuje metody které jsou statické. Vše co potřebují dostávají v parametrech. A to je další věc, která se mi na <strong>TDD as if You Meant It</strong> nelíbí. Tato technika má tendenci vytvářet třídy, které jsou bezstavové (nebo mají stavu velmi málo) a metody, které mají mnoho parametrů. Přijde mi to hrozně neobjektové, mám radši třídy, které mají stav a metody s co nejmenším počtem parametrů. Osobně bych to napsal takhle, ale nevím jestli by mi to Keith Braithwaite dovolil: </p><pre class="brush: csharp;">public class Direction
{
readonly Orientation _orientation;
public Orientation Orientation
{
get { return _orientation; }
}
public Direction(Orientation orientation)
{
_orientation = orientation;
}
public Direction TurnRight()
{
var orientation = _orientation + 1;
if (orientation > Orientation.West)
orientation = Orientation.North;
return new Direction(orientation);
}
public Direction TurnLeft()
{
var orientation = _orientation - 1;
if (orientation < Orientation.North)
orientation = Orientation.West;
return new Direction(orientation);
}
}</pre>
<p>Nyní třída Direction vypadá o dost lépe, ale zase to vyžaduje poměrně velké úpravy testovacího kódu. </p>
<h3>Shrnutí</h3>
<p>TDD as if You Meant It je nepochybně technika zajímavá a stojí za to vyzkoušet. Nevěřím ale, že by šla tímto způsobem napsat celá aplikace od začátku do konce. Tendence k bezstavovým třídám a metodám s velkým počtem parametrů je ošklivá, ale dá se potlačit tím, že budete nově vzniklé třídy poctivě refaktorovat.</p>
<p>Testy psané touto technikou nejsou úplně čisté unit testy. Příklad v tomto článku byl příliš krátký, takže to nebylo patrné, ale představte si třeba, že bychom pokračovali s testem, že se mravenec na bílém poli otočí doprava (tentokrát už doopravdy). Test by vypadal nějak takto: </p><pre class="brush: csharp;">[TestCase(true, Orientation.East)]
[TestCase(false, Orientation.West)]
public void TurnsToCorrectDirection(bool isOnWhiteCell, Orientation expectedResult)
{
var direction = new Direction(Orientation.North);
direction = isOnWhiteCell
? direction.TurnRight()
: direction.TurnLeft();
Assert.That(direction.Orientation, Is.EqualTo(expectedResult));
}
</pre>
<p>Je tohle unit test? Striktně vzato není, protože kromě otáčení podle barvy buňky testujeme implicitně i to, že se Direction umí správně otočit doprava i doleva. Mohli bychom sice Direction schovat za rozhraní, které bychom mockovali a v tomto testu testovali chování místo stavu (viz vsuvka 2 z minulého <a href="http://blog.kolman.cz/2012/03/tdd-outside-in.html">postu</a>), ale TDD as if You Meant It nás k tomu nijak nenavádí.</p>
<p>Na co se mi tato technika osvědčila jsou explorativní testy – když předem nevíte, jak něco bude fungovat, a potřebujete si to na malém kusu kódu vyzkoušet. V tu chvíli nemá smysl se pokoušet definovat třídy a jejich zodpovědnosti, je lepší nejdřív pochopit, jaké jsou možnosti a jak vůbec lze problém řešit. Jakmile to ale zjistím, vracím se k TDD Outside-in.</p> Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com5tag:blogger.com,1999:blog-3058726084145190258.post-62089467316504600612012-03-22T00:37:00.000+01:002012-03-22T00:40:45.139+01:00TDD Outside-in<p>Moje oblíbená technika psaní unit testů je začít zvenku, od toho, jak se bude vaše třída volat. Je to jako kdyby jste navrhovali veřejnou knihovnu – chcete aby měla rozumné rozhraní a šla dobře používat. Tento přístup klade důraz na návrh tříd a jejich spolupráce. Implementace je až druhořadá záležitost, protože jde vždycky změnit, pokud nebude vyhovovat např. výkonnostně. V dnešním článku bych vám chtěl na příkladu Langtonova mravence ukázat, jak to funguje a jaké má tento přístup výhody a nevýhody.</p> <a name='more'></a> <p>Langtonův mravenec je zero-player game skládající se z nekonečné 2D plochy černých a bílých buněk a jednoho směrově orientovaného mravence. V každé iteraci (kroku) mravenec udělá:</p> <ul> <li>Pokud stojí na bílé buňce, otočí se doprava, změní barvu buňky a popoleze o buňku vpřed. <li>Pokud stojí na černé buňce, otočí se doleva, změní barvu buňky a popoleze o buňku vpřed.</li></ul> <p>Více informací o tomto problému a animaci prvních 200 kroků najdete na <a href="http://en.wikipedia.org/wiki/Langton's_ant">wikipedii</a>.</p> <p>Řekněme že máme napsat program, který vypíše seznam černých buněk po zadaném počtu iterací. Pokud píšeme kód stylem <strong>TDD Outside-in</strong>, musíme nejdřív vymyslet, co bude reprezentovat náš program a jak se to bude volat. Mohlo by to vypadat třeba takhle:</p><pre class="brush: csharp;">var game = new Game();
game.Tick();
var blackCells = game.GetBlackCells();</pre>
<p>Třída Game bude implementovat pravidla hry. Metoda Tick() provede jednu iteraci hry. Nakonec metoda GetBlackCells() vrátí seznam černých buněk. Mravenec začíná na bílé nekonečné ploše, takže předpokládáme, že černých buněk bude vždy méně než bílých. Když budeme chtít dostat výsledek po více než jedné iteraci, zavoláme game.Tick() víckrát.</p>
<p>Takže náš první test by mohl vypadat nějak takhle:</p><pre class="brush: csharp;">public class GameTest
{
[Test]
public void ReturnsCorrectResultAfterOneIteration()
{
var game = new Game();
game.Tick();
var blackCells = game.GetBlackCells();
Assert.That(blackCells, Is.EquivalentTo(new[] {new Position(0, 0)}));
}
}
</pre>
<p>Z testu vyplývá, že jsme se rozhodli pozici na ploše reprezentovat třídou Position, která bude mít dvě souřadnice. Position bude immutable (neměnný) value object, tedy bude obsahovat data, ale žádné chování. Nyní můžeme vytvořit třídy Game a Position, aby šel test vůbec zkompilovat:</p><pre class="brush: csharp;">public class Game
{
public void Tick()
{
throw new NotImplementedException();
}
public IEnumerable<Position> GetBlackCells()
{
throw new NotImplementedException();
}
}
public class Position
{
public int X { get; private set; }
public int Y { get; private set; }
public Position(int x, int y)
{
X = x;
Y = y;
}
}
</pre>
<p>A zde je vidět první problém outside-in přístupu: Test nás donutil vytvořit hned dvě třídy najednou. A nejen to, navíc implicitně předpokládá, že pro třídu Position bude platit value equality, takže dva různé objekty Position se stejnými souřadnicemi se budou rovnat. To ale zatím není pravda. Musíme proto napsat další test:</p><pre class="brush: csharp;">public class PositionTest
{
[Test]
public void TwoObjectsWithSameCoordinatesAreEqual()
{
var a = new Position(1, 2);
var b = new Position(1, 2);
Assert.That(a, Is.EqualTo(b));
}
}</pre>
<p>Pro stručnost implementaci value equality třídy Position vynechám, kompletní příklad najdete na <a href="https://github.com/kolman/LangtonsAnt/tree/master/CSharp">githubu</a>. </p>
<h3>Vsuvka 1: Který Red je správný Red?</h3>
<p>Nyní můžeme spustit náš první test, který dopadne takto:</p><pre class="brush: plain;">System.NotImplementedException : The method or operation is not implemented.
</pre>
<p>Hurá! Máme padající test. Jenže chceme, aby padal zrovna tímto způsobem? Základem TDD je ověřit si, že test který napíšeme spadne. Jenže stejně důležité je, aby padal požadovaným způsobem a aby hláška z pádu testu odpovídala tomu, co testujeme a byla srozumitelná. V testu ReturnsCorrectResultAfterOneIteration() nás zajímá, jestli Game vrací správné černé buňky, a ne jestli je nebo není implemetovaná. Upravíme proto třídu Game:</p><pre class="brush: csharp;">public class Game
{
public void Tick()
{
}
public IEnumerable<Position> GetBlackCells()
{
return new Position[0];
}
}</pre>
<p>Test nyní skončí s chybou:</p><pre class="brush: plain;">Expected: equivalent to < <TddOutsideIn.Position> >
But was: <empty>
</pre>
<p>To je o trochu lepší, protože to popisuje výsledek, ale hláška ještě není úplně srozumitelná, protože nevíme, jaký objekt Position jsme to vlastně očekávali. V třídě Position proto doplníme metodu ToString(), aby vracela souřadnice buňky:</p><pre class="brush: csharp;">public override string ToString()
{
return string.Format("[{0}, {1}]", X, Y);
}
</pre>
<p>Když nyní spustíme test znovu, dostaneme konečně rozumnou hlášku:</p><pre class="brush: plain;">Expected: equivalent to < <[0, 0]> >
But was: <empty>
</pre>
<p>Hurá! Máme padající test, který padá očekávaným způsobem a vypisuje srozumitelnou hlášku. </p>
<h3>Green</h3>
<p>Můžeme proto přistoupit k implementaci třídy Game. Neměli bychom přitom ale dělat víc, než kolik nám nařizuje test:</p><pre class="brush: csharp;">public class Game
{
public void Tick()
{
}
public IEnumerable<Position> GetBlackCells()
{
return new[] { new Position(0, 0) };
}
}
</pre>
<p>Poněkud zjednodušující, můžete namítnout. Ale test projde, a to je hlavní. Nyní je potřeba napsat další test, abychom měli jistotu, že třída Game vrací správné výsledky i při jiném počtu iterací než jedna.</p>
<h3>Vsuvka 2: State Verification vs. Behavior Verification</h3>
<p>Předtím si ale musíme udělat malou teoretickou odbočku. Testy se dají psát dvěma odlišnými způsoby, které se liší tím, co ověřujeme. V našem prvním testu se ověřuje <strong>stav</strong>. Nejdříve se nějak nakonfiguruje prostředí (Game), pak se provede testovaná akce (Tick), a nakonec se ověří, že se prostředí změnilo do kýženého stavu (seznam černých buněk odpovídá očekávání). Kdybychom chtěli dál pokračovat tímto způsobem, museli bychom identifikovat všechny zajímavé konfigurace prostředí, které chceme testovat. V našem případě by to byl mravenec stojící na bílé nebo černé buňce a otočený do jednoho ze čtyř směrů, tj. osm možností. Třídě Game bychom řekli, kde stojí mravenec, na jaké buňce a jak je otočený, pak zavolali metodu Tick() a nakonec ověřili zda je mravenec na správné buňce, má správnou orientaci a barva buněk odpovídá zadaným pravidlům. To je ale jen jednoduchý problém Langtonova mravence, v reálném světě může být možností konfigurace nekonečně.</p>
<p>Až dosud jsme v testu ověřovali stav. Můžeme ale také ověřovat <strong>chování</strong>. Místo abychom kontrolovali, jak se změnilo prostředí po spuštění metody Tick(), radši se zeptáme, co třída Game <em>dělá</em> během metody Tick(). Nutným předpokladem je identifikace spolupracujících objektů (collaborators), které nahradíme dvojníkem (test double). V našem případě jsme se rozhodli, že Game má implementovat zadaná pravidla hry. Podle pravidel má hra otáčet mravencem podle barvy buňky na které stojí, posouvat ho a měnit barvu buněk. Řekněme, že kolaborátoři třídy Game budou Ant, který bude mít orientaci a polohu, a Board, který bude reprezentovat hrací pole s černými a bílými buňkami. Místo abychom se ptali na stav mravence a hrací plochy na konci testu, budeme ověřovat, že Game na nich zavolá správné metody. K tomu nám pomůžou testovací dvojníci, které v testu použijeme místo skutečných kolaborátorů.</p>
<p>Spolupracující objekty (collaborators) můžeme nahradit <a href="http://www.martinfowler.com/bliki/TestDouble.html">několika typy dvojníků</a>. Pro TDD Outside-in jsou zvlášť vhodné mock objekty vytvářené nějakým šikovným mockovacím frameworkem. Díky nim se můžeme plně soustředit na návrh rozhraní spolupracujících objektů a nechat implementaci na později.</p>
<h3>Game a spol.</h3>
<p>Jak by v našem případě vypadal test, který ověřuje chování místo stavu? Nejdřív je nutné vymyslet kolaborátory třídy Game a jejich rozhraní. Zkusíme nyní otestovat, že se mravenec otočí na bílé buňce doprava. Game tedy musí vědět, zda mravenec stojí na bílé buňce, a pokud ano, otočit ho doprava. Musíme proto vytvořit mock objekty Ant a Board a podstrčit je Game:</p><pre class="brush: csharp;">[Test]
public void TurnsAntRightOnWhiteCell()
{
var ant = new Mock<IAnt>();
var board = new Mock<IBoard>();
var currentPosition = new Position(0, 0);
ant.SetupGet(a => a.CurrentPosition).Returns(currentPosition);
board.Setup(b => b.IsBlack(currentPosition)).Returns(false);
var game = new Game(ant.Object, board.Object);
game.Tick();
ant.Verify(a => a.TurnRight());
}
</pre>
<p>K mockování je zde použit framework <a href="http://code.google.com/p/moq/">Moq</a>. Tento test definuje, že Ant bude mít property CurrentPosition a metodu TurnRight(), Board metodu IsBlack(), která vrátí, zda je určitá buňka černá. Po zavolání game.Tick() se verifikuje, zda byla zavolána metoda ant.TurnRight(). Ant i Board jsou reprezentované pomocí rozhraní, takže se můžeme soustředit pouze na jejich interakci s třídou Game. K tomu, aby test prošel, nemusíme rozhraní IAnt ani IBoard vůbec implementovat – to za nás obstará mock framework.</p>
<p>Obdobně bychom otestovali otočku doleva, implementace v třídě Game může vypadat takto:</p><pre class="brush: csharp;">public class Game
{
readonly IAnt _ant;
readonly IBoard _board;
public Game(IAnt ant, IBoard board)
{
_ant = ant;
_board = board;
}
public void Tick()
{
if (_board.IsBlack(_ant.CurrentPosition))
_ant.TurnLeft();
else
_ant.TurnRight();
}
...
}
</pre>
<p>Mimochodem, když už máme IBoard, jehož zodpovědností je držet informace o stavu hrací plochy, pak už nedává smysl, aby metoda GetBlackCells() byla v třídě Game. Game obsahuje jen pravidla hry, a objektu IBoard se ptá na barvu buněk a říká mu, u kterých buněk má změnit barvu. Metodu GetBlackCells() jsme proto přesunuli do IBoard. </p>
<p>Další pravidlo říká, že mravenec se má v každé iteraci posunout o buňku vpřed:</p><pre class="brush: csharp;">[Test]
public void MovesAntForward()
{
var ant = new Mock<IAnt>();
var board = new Mock<IBoard>();
var game = new Game(ant.Object, board.Object);
game.Tick();
ant.Verify(a => a.MoveForward());
}
</pre>
<p>Framework Moq automaticky přijímá všechna nenakonfigurovaná volání, a pokud metody vrací nějakou návratovou hodnotu, vrátí výchozí hodnotu pro daný typ. Díky tomu tento test projde, i když nejsou nakonfigurovaná volání ant.CurrentPosition a board.IsBlack(), stačí do metody Game.Tick() přidat volání _ant.MoveForward(). Na toto výchozí chování se ale nemusíme spoléhat, můžeme vše co je potřeba nastavit v setupu testu, tak jak to uděláme v dalším kroku.</p>
<p>Naše testy nyní obsahují spoustu duplicitních řádků. Setup testů je navíc dost dlouhý a obtížně čitelný. Zkusíme proto test zrefaktorovat. Vše co může být společné pro všechny testy přesuneme do setup metody:</p><pre class="brush: csharp;">public class GameTest
{
readonly Position _currentPosition = new Position(0, 0);
Mock<IAnt> _ant;
Mock<IBoard> _board;
Game _game;
[SetUp]
public void SetUp()
{
_ant = new Mock<IAnt>();
_board = new Mock<IBoard>();
_ant.SetupGet(a => a.CurrentPosition).Returns(_currentPosition);
_game = new Game(_ant.Object, _board.Object);
}
[Test]
public void TurnsAntRightOnWhiteCell()
{
_board.Setup(b => b.IsBlack(_currentPosition)).Returns(false);
_game.Tick();
_ant.Verify(a => a.TurnRight());
}
[Test]
public void TurnsAntLeftOnBlackCell()
{
_board.Setup(b => b.IsBlack(_currentPosition)).Returns(true);
_game.Tick();
_ant.Verify(a => a.TurnLeft());
}
[Test]
public void MovesAntForward()
{
_game.Tick();
_ant.Verify(a => a.MoveForward());
}
}
</pre>
<p>Testy jsou nyní velmi krátké a jednoduché, a měli bychom se snažit, aby to tak vždy zůstalo. </p>
<h3>Shrnutí</h3>
<p>Metoda TDD Outside-in umožňuje soustředit se na návrh tříd, jejich rozhraní a spolupráce. Díky definici spolupracujících objektů a jejich nahrazení v testu mockem umožňuje začít "zvenku", od použití třídy v klientském kódu.</p>
<p>Všimněte si, že Game vůbec neřeší, co konkrétně znamená "posunout se o buňku vpřed" nebo "otočit se doprava". To není zodpovědnost třídy Game, ta má pouze implementovat zadaná pravidla hry. Přístup outside-in nám umožnil implementovat a otestovat tato pravidla dříve, než jsme museli řešit posun v 2D poli a změnu směru o 90 stupňů.</p>
<p>Testovací styl, při němž se ověřuje chování místo stavu, neustále nutí přemýšlet o tom, co která třída má na starosti. Pokud je test moc dlouhý nebo složitý, je to známkou toho, že máme třídy moc velké a mají na starosti víc než jednu věc.</p>
<p>Má to však i svoje mouchy – třeba než napíšete první test, musíte udělat hodně rozhodnutí o tom, jak budou vaše třídy vypadat: Že bude existovat objekt Game, že bude iterovat hru metodou Tick, že potřebuje dva kolaborátory, kteým jsme předem vyhradili nějakou konkrétní zodpovědnost. Implicitně jsme se také rozhodli, že ani IBoard ani IAnt nebude immutable, protože Game v každé iteraci změní jejich stav.</p>
<p>Díky tomu vnášíte do návrhu svoje vlastní neuvědomělé předsudky o tom, jak má struktura tříd vypadat. Zbavit se jich můžeme metodou "TDD As if you meant it", na kterou se podíváme příště.</p>
<p>Pokud chcete vědět víc o stylech testování a mock objektech, doporučuju článek Martina Fowlera <a href="http://martinfowler.com/articles/mocksArentStubs.html">Mocks Aren't Stubs</a>. Kompletní implementaci Langtonova mravence v C# je na <a href="https://github.com/kolman/LangtonsAnt/tree/master/CSharp">githubu</a>.</p> Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com3tag:blogger.com,1999:blog-3058726084145190258.post-67571589865133527952012-03-06T22:58:00.001+01:002012-03-06T22:58:59.705+01:00Jak jsem prováděl pokusy na lidech<p>V naší firmě probíhá pravidelně každého půl roku programátorský test. O smyslu takového počínání by bylo nepochybně možné pochybovat, nicméně letos na jaře vyšla příprava zadání na mě, takže jsem se mimořádně místo remcání soustředil na to, jak připravit zajímavou úlohu, která by mým drahým kolegům přinesla kromě obvyklého bodového hodnocení také nějaký hraniční zážitek.</p> <a name='more'></a> <p>Takže co jsem si na své nic netušící kolegy vymyslel? Dost jednoduchý úkol: Implementovat <a href="http://en.wikipedia.org/wiki/Langton's_ant">Langtonova mravence</a>, respektive program, který vypíše seznam černých buněk po zadaném počtu kroků. Jedinou komplikací byl seznam povolených jazyků: <a href="http://confluence.jetbrains.net/display/Kotlin/Welcome">Kotlin</a>, <a href="http://ceylon-lang.org/">Ceylon</a>, <a href="http://fantom.org/">Fantom</a>, <a href="http://golang.org/">Go</a> nebo <a href="http://www.dartlang.org/">Dart</a>. Aby to tolik nebolelo, připravil jsem virtuální stroj (VMware image Linux Mint), na kterém bylo rozchozené IDE pro všech pět jazyků (bohužel mi ale nedošlo, že ráno před testem nestihnu těch 5GB nahrát do Olomouce, takže tamní kolegové museli vystačit s online prostředím).</p> <p>Langtonův mravenec je jednoduchá zero-player game: Na nekonečné ploše, která se skládá z bílých a černých buněk, žije mravenec. V každém kroku mravenec udělá:</p> <ul> <li>Pokud stojí na bílé buňce, otočí se doprava, změní barvu buňky a popoleze o buňku vpřed. <li>Pokud stojí na černé buňce, otočí se doleva, změní barvu buňky a popoleze o buňku vpřed.</li></ul> <p>Další zajímavé informace o tomto problému a názornou animaci prvních 200 kroků najdete na <a href="http://en.wikipedia.org/wiki/Langton's_ant">wikipedii</a>.</p> <p>Proč se učit nový jazyk? Je to jako individuální turistika "na blind", když vezmete batoh a letenku a jinak nic neplánujete. Sice nemáte jistotu kde skončíte, co uvidíte a koho potkáte, ale je to dobrodružství, poznáte cílovou zemi lépe než s cestovkou, rozšíříte si obzory a někdy se dozvíte i něco o sobě. Navíc to není tak nereálná situace: Klidně se může stát, že přijde nový projekt pro platformu kterou neznáte, a vy se budete rozhodovat, jestli jít do nového prostředí (jazyka který neznáte) nebo do nového prostředí (do jiné firmy).</p> <p>Proč jsem vybral zrovna těchto pět jazyků? Chtěl jsem, abychom všichni byli na stejné lodi, a proto jsem vyloučil jazyky známé, staré a oblíbené v komunitách (u nás ve firmě jsme multiplatformní). Také jsem hledal jazyk, který lze (alespoň teoreticky) použít v reálné aplikaci, nebo který má aspoň do budoucna, dle mého skromného názoru, šanci na rozšíření.</p> <p><a href="http://confluence.jetbrains.net/display/Kotlin/Welcome">Kotlin</a> je nový staticky typovaný jazyk, který se kompiluje do JVM bytekódu nebo javascriptu. Dávám mu dost šancí na úspěch, zejména proto, že je od JetBrains, tvůrců pokročilých IDE IntelliJ Idea a ReSharper. Pokud dotáhnou IDE Kotlinu na stejnou úroveň, bude to zajímavá alternativa Javy.</p> <p>Jazyk <a href="http://ceylon-lang.org/">Ceylon</a> také kompiluje do JVM a stojí za ním silný hráč, Red Hat. Je staticky typovaný a jeho type systém <a href="http://ceylon-lang.org/documentation/1.0/tour/types/">stojí za pozornost</a>.</p> <p><a href="http://fantom.org/">Fantom</a> zatím považuju spíš za kuriozitu, zajímavý je tím, že cílové platformy jsou JVM i .NET CLR. Je převážně staticky typovaný, ale povoluje některé vlastnosti dynamických jazyků, jako duck typing.</p> <p>Jazyk <a href="http://golang.org/">Go</a> od Google je "rychlý, staticky typovaný, kompilovaný jazyk, který ale působí jako dynamicky typovaný, interpretovaný jazyk":-) Je to nejzajímavější jazyk z vybrané pětice a je škoda, že si ho vybral jen jeden kolega. Tak například mezi jeho základními typy je komplexní číslo (complex64 a complex128). Nemá třídy, ale má typy, "struct" a rozhraní (které připomínají duck typing). Ke všem vaším typům můžete definovat metody, ale dělá se to "zvenku", podobně jako u prototypů v javascriptu. Má ukazatele, ale nemá ukazatelovou aritmetiku. A má hned několik konceptů a klíčových slov pro zjednodušení konkurence v paralelním programování.</p> <p><a href="http://www.dartlang.org/">Dart</a> má ambice nahradit javascript, ale možná v něm budeme psát i serverový kód. Pro renegáty z C# a Javy je to velmi příjemný a povědomý jazyk, který spojuje výhody staticky a dynamicky typovaných jazyků.</p> <p>A jak to dopadlo? Zatím jsem test nestihl vyhodnotit, ale už vím že většina kolegů si vybrala Dart a Kotlin. Někteří to rovnou vzdali, někteří přijmuli výzvu s nadšením a někteří mi vyhrožovali fyzickým násilím (jenom jako) (teda doufám). Když nic jiného, letošní test si budou všichni dlouho pamatovat:-)</p> Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com0tag:blogger.com,1999:blog-3058726084145190258.post-79349245920405060432012-02-16T12:07:00.002+01:002012-02-20T15:23:31.072+01:00ASP.NET MVC na FIM UHK J6 v rámci OWE<p>Aneb zkratek není nikdy dost:-) Ale vážně, v pondělí 20. února budu mít přednášku o ASP.NET MVC na Univerzitě v Hradci Králové. Probereme čím se liší MVC od klasického přístupu tvorby webových stránek, základy ASP.NET MVC a jak v něm psát hezký testovatelný kód. Ukážeme si také některé nástroje, které podporují Test Driven Development v C#. Přednáška je otevřená pro veřejnost a začíná v 8:15 v místnosti J6, vchod zde: <a title="http://www.mapy.cz/s/35ae" href="http://www.mapy.cz/s/35ae">http://www.mapy.cz/s/35ae</a></p><br />
<p><strong>Video a slajdy už jsou na webu!</strong></p><a name='more'></a><br />
<p>Video: je to nesestříhaný surový záznam, takže prosím omluvte různá zadrhnutí a přeřeknutí. Na sestříhání nemám čas:-) Pokud ASP.NET MVC znáte a zajímá vás jen testování, skočte na čas 44:10.</p><br />
<iframe width="640" height="480" src="http://www.youtube.com/embed/wUfyUVheLUQ" frameborder="0" allowfullscreen></iframe><br />
<br />
<p>Slajdy <a href="http://slidesha.re/ygO0Tp">najdete na Slideshare</a>, ale nejsou asi příliš zajímavé, důraz byl na ukázky v kódu.</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com2tag:blogger.com,1999:blog-3058726084145190258.post-38838231288460093352012-01-15T08:57:00.001+01:002012-01-15T09:04:45.077+01:00Dart, Frog, Fling, Toss, Tip a spol.<p>Kolem nového jazyka Dart vzniklo několik podpůrných (vesměs testovacích) projektů s kryptickými kódovými názvy. Nechť vám tento malý výkladový slovníček pojmů pomáhá k lepší orientaci v těchto nových krajinách.</p> <a name='more'></a> <ul> <li><strong>Dartisan, Dartist, Dartian:</strong> Člověk programující v jazyce Dart.</li> <li><strong>DartC:</strong> Původní Dart-to-JavaScript transpiller, napsaný v Javě. Proslavil se tím, že dokáže z Hello World v Dartu vygenerovat <a href="https://gist.github.com/1277224">17272 řádků</a> JavaScriptu. Nutno ovšem říci, že posměšné komentáře na adresu velikosti vygenerovaného JavaScriptu nejsou úplně na místě, protože se jedná o velmi ranou verzi Dartu, kde jde o ověření konceptu a ne o výkonnost. Konečným cílem je navíc spouštět Dart přímo v prohlížeči bez kompilace do JavaScriptu. Pokud použijete parametr –optimize, dartc vyprodukuje "jenom" necelých dva tisíce řádků.</li> <li><strong>Dart SDK:</strong> Předkompilovaný runtime Dartu pro Windows, Linux a Mac. Obsahuje dart.exe (samotná virtuální mašina) a Frog, kompilátor do JavaScriptu. Stáhnout si ho můžete ze serveru průběžných buildů <a href="http://gsdview.appspot.com/dart-editor-archive-continuous/latest/">zde</a> (dart-win32.zip).</li> <li><strong>Frog:</strong> Novější Dart-to-JavaScript transpiller, napsaný v Dartu. Je <a href="http://turbomanage.wordpress.com/2011/11/02/dart-to-js-compilation-with-frog/">rychlejší a efektivnější</a> než DartC a ve výchozím nastavením generuje optimalizovaný výstup (na rozdíl od DartC). Má to být takový "default" kompilátor pro Dart. Součástí Dart SDK je frogc, který kompiluje Dart do JavaScriptu. Ve zdrojových kódech je ale i <strong>minfrog</strong> (dříve frogsh), který po kompilaci dokáže vygenerovaný JavaScript spustit pomocí node.js.</li> <li><strong>Leg:</strong> Experimentální doplněk kompilátoru Frog, jehož úkolem je prozkoumat potenciálně zajímavé věci jako inference typů, podpora pokročilých editačních nástrojů, adaptivní kompilace na klientu apod. Pokud něco Leg neumí, použije Frog. Více se dozvíte v <a href="http://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/frog/leg/README.txt">README.txt</a>. Momentálně to pro Dartisany není až tak zajímavá věc, ale skrývá v sobě potenciál, že se úroveň IDE a nástrojů pro Dart přiblíží zavedeným statickým jazykům.</li> <li><strong>Fling:</strong> Jednoduchý HTTP server napsaný v Dartu, který ale deleguje většinu funkcionality na jádro napsané v Javě. Umí spustit Dart na serveru a servírovat statický obsah. Neumí ale automaticky kompilovat klientské .dart soubory na .js, k tomu je potřeba mezikrok (původně v Pythonu). Dnes už jde zřejmě o zastaralý koncept, kdyby vás zajímalo víc podrobností, návod <a href="http://dartwatch.com/index.php/2011/10/fling-a-dart/">naleznete zde</a>.</li> <li><strong>Toss:</strong> Server, který dokáže v HTML stránkách najít script tag odkazující na dart (<script type=”application/dart” src=”myApp.dart” />), za běhu zkompiluje daný soubor do JavaScriptu a výsledek vloží do posílané HTML stránky. Zjednodušuje vývojářské workflow na Ctrl+S, Alt+Tab, F5, odpadá nutnost spouštět po změně .dart souboru kompilátor. Toss využívá Frog ke kompilaci a spoléhá na funkce node.js, takže je nutné ho spustit pomocí minfrog, i když je napsaný v Dartu. Návod jak na to <a href="http://turbomanage.wordpress.com/2011/12/09/dart-dev-mode-cometh/">naleznete zde</a>.</li> <li><strong>Tip:</strong> Malá aplikace postavená nad Toss, která umožňuje editovat kód v Dartu přímo v prohlížeči. Zajímavé je, že pro kompilaci Dartu do JavaScriptu nepotřebuje server, protože používá Frog přímo v browseru. Také obsahuje zárodek příkazové řádky (REPL). Více <a href="http://dartwatch.com/index.php/2011/12/frog-toss-tip-and-in-browser-compiling/">najdete zde</a>.</li> <li><strong>Dartium:</strong> Experimentální verze prohlížeče Chrome (AKA Chromium), který umí spouštět skripty napsané v Dartu nativně. Dost bylo JavaScriptu! Zatím si ho ovšem musíte <a href="http://code.google.com/p/dart/wiki/BuildingDartium">zkompilovat</a> sami, pravděpodobně to je možné jen na Linuxu a Macu. Ano, hardcore weboví vývojáři si před začátkem projektu kompilují webový prohlížeč sami, ze zdrojáků (hned poté, co mají zkompilované jádro Linuxu:-)).</li> <li><strong>DART:</strong> <a href="http://www.dart.org/"><strike>Dallas Area Rapid Transit</strike></a>, ehm, teda vlastně kůl <a href="http://www.dartlang.org/">nový jazyk</a> od gůglu.</li></ul> Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com1tag:blogger.com,1999:blog-3058726084145190258.post-65976884282276159302012-01-14T14:44:00.001+01:002012-01-14T14:50:36.913+01:00Jednoduchý HTTP server v DARTu<p>Nedávno přibyly do projektu Dart dvě příjemné novinky: Kompilace virtual machine pro Windows v rámci průběžného buildu, a možnost spouštět skripty nad virtual machine přímo z vývojovéjo prostředí Dart Editoru. Pojďme tyto novinky vyzkoušet na jednoduchém HTTP serveru.</p> <a name='more'></a> <p>Nejdřív si nastavíme vývojové prostředí. Budeme potřebovat:</p> <ol> <li><strong>Dart SDK:</strong> Obsahuje předkompilovanou virtual machine, neboli runtime pro spouštění programů napsaných v Dartu. Můžeme si ho stáhnout ze stránky průběžných buildů: <a href="http://gsdview.appspot.com/dart-editor-archive-continuous/latest/">http://gsdview.appspot.com/dart-editor-archive-continuous/latest/</a>. <li><strong>Dart Editor:</strong> Doporučuju také stáhnout z průběžných buildů (stejná adresa jako u Dart SDK), protože oficiální stabilní verze, dostupná z <a href="http://www.dartlang.org/docs/getting-started/editor/index-win.html">dartlang.org</a>, ještě nerozlišuje typy projektů. Novější verze se při vytváření projektu zeptají, zda jde o webovou nebo "skriptovou" aplikaci. <em>Pozn: Ke spuštění příkladu v tomto postu Dart Editor nezbytně nepotřebujete, můžete použít libovolný textový editor a aplikaci spustit z příkazové řádky.</em> <li>Knihovnu <strong>http</strong>: Najdete ji ve zdrojových kódech Dartu, tvoří ji dva soubory (<em>html.dart</em> a <em>html_impl.dart</em>). Nejlépe je stáhnete pomocí Subversion (<em>svn checkout http://dart.googlecode.com/svn/branches/bleeding_edge/dart/samples/chat</em>), ale protože to jsou jen dva soubory, nedá moc práce je stáhnout <a href="https://code.google.com/p/dart/source/browse/#svn%2Fbranches%2Fbleeding_edge%2Fdart%2Fsamples%2Fchat">ručně z webu</a>.</li></ol> <p>Stáhněte si a rozbalte obojí někam na disk a spusťte Dart Editor. V menu Tools – Preferences – Launch je potřeba nastavit cestu na "Dart VM executable", který se nachází v Dart SDK ve složce bin.</p> <p>Nyní vytvořte nový projekt typu "Script" a spusťte jej pomocí ikony na panelu nástrojů:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiirs9bYUN0mQXMr2ObUSdIbmbR9kPmSun94oeqh56g39oaXnm3MDWVVQrAcyCv1xmOF8Inl9P_ATQg2NMIB82Zg3oWL6cPoxoc-hfHxdCRCbb0aTbO_o3hCo92SwtfSGZ10YwZwO8j0dg/s1600-h/darteditorscript%25255B2%25255D%25255B2%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="darteditorscript[2]" border="0" alt="darteditorscript[2]" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBuXKvVE7xYGQthXR9JBRqzRDfz7wEyWR4rP-fNLi4Yt8kpVJfkt5K0jam8K9EKiU8GWvsqFWbavwYhwk-mVJYBTLtemJOiS3ORg3vgR7Wq7riFkJmLdvK-vmcEEy6S0cN1wU7-HyN6Yc/?imgmax=800" width="244" height="181"></a></p> <p>Pokud v okně konzole vidíte "Hello World", gratulujeme, postupujete do dalšího kola.</p> <p>Nyní stačí naimportovat knihovnu http (použijte relativní cestu vzhledem k vašemu kódu) a pár řádků kódu:</p><pre class="brush: java;">#import("lib/http.dart");
void main() {
var server = new HTTPServer();
server.listen('127.0.0.1', 8080, processRequest);
print('Listening...');
}
void processRequest(HTTPRequest request, HTTPResponse response){
print("Request: ${request.method} ${request.uri}");
request.dataEnd = (String postData) => respond(postData, response);
}
void respond(String postData, HTTPResponse response) {
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.writeString('<h1>Hello, Dartisans!</h1>', null);
response.writeDone();
}
</pre>
<p>Spusťte aplikaci, navigujte váš oblíbený prohlížeč na adresu http://127.0.0.1:8080 a je to!</p> Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com0tag:blogger.com,1999:blog-3058726084145190258.post-64299696950728277272011-12-09T20:26:00.001+01:002011-12-10T11:46:01.032+01:00Dart: Lehký úvod pro programátory C#<p>Dart je kůl, pokud to ještě nevíte. Je to startovací droga dynamických jazyků pro programátory zvyklé na statické typování. Není tak dokonalý jako C#, ale i tak dosahuje vysoké úžasnosti a rozhodně je lepší než JavaScript. Ponechme nyní stranou předsudky a filosofické diskuse o užitečnosti nového jazyka a pojďme se podívat, jak Dart vypadá z pohledu programátora, který je léta navyklý užívat kód v C#. <br />
</p><a name='more'></a> <p>Úvodem je nutné uvést na pravou míru jeden zažitý předsudek: <strong>Dart není pouze náhrada JavaScriptu</strong>. Dart má potenciál být plnohodnotným serverovým jazykem. Virtuální mašinu pro Windows si můžete stáhnout z <a href="http://www.dartforce.com/dev/index.html">DartForce</a> (dart_bin.exe) a existuje už i runtime určený pro HTTP server, po vzoru node.js: <a href="http://dartwatch.com/index.php/2011/10/fling-a-dart/">Fling</a>. Dart je mladý (nebo mladá?) a tak je to všechno ještě v plenkách. Aplikaci v "produkční" kvalitě z toho nepostavíte, ale ukazuje to slibný směr, kterým se Google chce vydat: Jeden jazyk na serveru a v browseru (a doufejme že časem i na Androidu).</p><p>Nejjednodušší způsob, jak si vyzkoušet <em>plain vanilla</em> Dart, je online dartboard na <a href="http://try.dartlang.org/">try.dartlang.org</a>. Také si můžete stáhnout <a href="http://www.dartlang.org/docs/getting-started/editor/index-win.html">editor</a>, který umí jednoduché napovídání, navigaci, zvýraznění syntaxe a indikaci chyb při uložení souboru. Také dokáže zkonvertovat Dart na JavaScript a pustit váš program v browseru, ale také můžete psát jen .dart soubory a spouštět je z příkazové řádky ručně (vypadá to, že to <a href="http://dartwatch.com/index.php/2011/12/new-features-in-dart-editor/">již brzy bude umět přímo editor</a>).</p><p>Dart je sice dynamický jazyk, ale má typové anotace a během vývoje lze zapnout <em>checked mode</em>, ve kterém provádí interpret před spuštěním kódu statické typové kontroly (viz zaškrtávací volba "Checked Mode" na stránce <a href="http://try.dartlang.org">try.dartlang.org</a>).</p><h3>Základy</h3><p>Dart je jednoduchý a stručný, přitom má ale všechny rysy jazyka, které činí C# lepší než Java (#joke). Syntax je podobná Javě i C# a je velmi intuitivní. Dart je objektový jazyk, ale nevyžaduje třídy:</p><pre class="brush: csharp;">// Dart
void main() {
print('Hello World!');
}
// C#
using System;
namespace HelloWorld
{
class Hello
{
public static void Main()
{
Console.WriteLine("Hello World!");
}
}
}</pre><p>Jak je vidět, Dart nemá ani namespace, tedy možná zatím. Jednotlivé soubory se zdrojovým kódem lze seskupit do <em>library</em>, která má být základní jednotkou modularity. Při importu library můžete definovat nepovinný prefix, přes který budete přistupovat k typům v knihovně, a zabránit tak případným konfliktům s jinými knihovnami.</p><p>Dart zná jen dvě viditelnosti: <em>public</em> a <em>private</em>, které se zapisují konvencí – členy třídy, které jsou private, je nutné prefixovat podtržítkem. Na druhou stranu díky tomu odpadá velmi mnoho zbytečných klíčových slov, kód je stručnější a vynucuje se tak sdílený styl psaní kódu. <strong>Private v Dartu je to samé jako internal v C#</strong>, to znamená, že deklarace jsou viditelné uvnitř library.</p><p>Dart zná gettery a settery jako v C#, příjemné je, že se k nim přistupuje stejně jako k proměnným třídy, takže odpadá známé spartakiádní cvičení s backing fieldy nebo s autoproperty.</p><pre class="brush: csharp;">class User {
String name;
num _age;
num get age() => _age; // Getter
set age(num value) { // Setter
if (value < 0) throw 'Prenatal clients are not accepted';
_age = value;
}
}
void main() {
var user = new User();
user.name = 'Karl';
user.age = 42;
print('${user.name} is ${user.age} years old.'); // proměnné lze psát přímo do stringu
}</pre><p>Na tomto příkladu je také vidět pár dalších cukříků: Pokud je funkce jeden výraz, lze ji zapsat pomocí <em>=></em>, jako v getteru age. Na předposledním řádku si všimněte, že proměnné jsou použity přímo jako parametry ve stringu a díky <em>string interpolation</em> se při přiřazení vyhodnotí.</p><p>Zajímavý syntax-sugar je automatická inicializace proměnných třídy přímo z konstruktoru:</p><pre class="brush: csharp;">class Point {
final num x, y;
Point(num this.x, num this.y); // automaticky nastaví x a y
}
void main() {
var p = new Point(1,2);
print('[${p.x}, ${p.y}]'); // vypíše [1, 2]
}
</pre><p>Mezi další pokročilé fíčury známé z C# patří nepovinné parametry s defaultní hodnotou a anonymní funkce, z JavaScriptu pak jednoduchá deklarace polí:</p><pre class="brush: csharp;">void foo([num answer = 42]) { // nepovinný parametr
print('answer: $answer ');
}
void bar(void action(num x)) { // parametr typu funkce
action(123);
}
void main() {
foo();
foo(56);
bar((num x) { foo(x); }); // anonymní funkce
var names = ['Karl', 'Egon', 'Franz']; // pole
for(String name in names) { // foreach
print(name);
}
}
</pre><h3>Optional Typing a generika</h3><p>Klíčové slovo <strong><em>var</em> v Dartu odpovídá <em>dynamic</em> v C#</strong>. V C# jsme zvyklí, že proměnná definovaná pomocí var má statický typ, odvozený od hodnoty. V Dartu má sice takto vytvořená proměnná také typ odvozený od hodnoty, ale může se změnit přiřazením jiné hodnoty, stejně jako <em>dynamic</em> v C#; nakonec <em>Dynamic</em> je v Dartu definován jako typ. Pokud chcete, aby vás kompilátor varoval při nesprávném přiřazení, musíte specifikovat typ explicitně:</p><pre class="brush: csharp;">void main() {
var text = "x";
print(text is String); // true
text = 123; // tímto se změní typ proměnné text
print(text is String); // false
print(text is int); // true
int n = 123;
// následující řádek způsobí compile-time warning a runtime error:
n = "aaa"; // warning: String is not assignable to int
}
</pre><p>Dart také podporuje generika, a to rovnou s kovariancí! Bohužel se typ parametru nekontroluje staticky, ale až při běhu programu, kdy vyhodí výjimku:</p><pre class="brush: csharp;">void printAllMembers(Iterable<Object> list) {
for(var i in list) {
print(i);
}
}
main() {
var integers = new List<int>();
integers.add(1);
integers.add(2);
integers.add(3);
integers.add('string'); // runtime error
print(integers is List<num>); // true
print(integers is List<Object>); // true
print(integers is List<String>); // false
printAllMembers(integers);
List<Object> objects = integers;
objects.add(4);
objects.add('a'); // runtime error
List<int> ints = new List<num>(); // runtime error
}
// PS: Object <- num <- int</pre><p>A teď pozor, výchozím typovým parametrem, pokud není zadán, je <em>Dynamic</em>. A protože se v Dartu dá vše přetypovat na Dynamic, platí následující, i když to není nám, co jsme odkojeni staticky typovanými jazyky, hned zřejmé: </p><pre class="brush: csharp;">main() {
print(123 is Dynamic); // true
print("tralala" is Dynamic); // true
List<int> numbers = new List<int>();
print(numbers is List<Dynamic>); // true
print(numbers is List); // true
print(new List() is List<Dynamic>); // synonymum
print(new List() is List<int>); // true!
}
</pre><p>Díky tomu můžete předat netypované pole metodě z knihovny, která v parametru vyžaduje typovaný seznam. </p><h3>Pojmenované konstruktory</h3><p>Dart má také inovativní prvky, které jsem nikdy v jiném jazyku neviděl (což neznamená že to v žádném nikdy nebylo). Dart nemá možnost přetížit deklaraci metody (overload), což může vadit u konstruktorů. Američtí inženýři si s tímto problémem poradili pomocí pojmenovaných konstruktorů:</p><pre class="brush: csharp;">class TimeSpan {
final num seconds;
TimeSpan(num this.seconds);
TimeSpan.fromMinutes(num minutes) : this(minutes*60);
TimeSpan.fromHours(num hours) : this.fromMinutes(hours*60);
}
main() {
print(new TimeSpan(10).seconds); // 10
print(new TimeSpan.fromMinutes(10).seconds); // 60
print(new TimeSpan.fromHours(10).seconds); // 36000
}
</pre><h3>Factories</h3><p>Slibná vlastnost jsou factories. Například takto lze v Dartu napsat singleton:</p><pre class="brush: csharp;">class Hello {
static Hello _instance;
factory Hello() { // factory constructor
if(_instance==null) {
_instance = new Hello._internal();
}
return _instance;
}
Hello._internal();
}
main() {
var test1 = new Hello(); // z pohledu klienta žádná změna
var test2 = new Hello();
print(test1===test2); // true
}
</pre><p>Všimněte si, že není potřeba měnit použití třídy, takže když se rozhodnete, že u existující aplikace změníte lifetime scope některé třídy, nemusíte opravovat všechna místa, kde se instancuje. Kromě factory constructoru lze použít i factory třídu. Toho se hojně využívá u rozhraní, kde lze tímto způsobem deklarovat default implementaci. Například List není třída, ale interface s factory třídou, takže je možné ho přímo instancovat operátorem new:</p><pre class="brush: java;">interface List<E> extends Collection<E> factory ListFactory { ... }
class ListFactory {
factory List<E>([int length = null]) {
...
return new ListImplementation(...);
}
}
var list = new List<int>();</pre><h3>The Future Is Dart</h3><p>Dart je jazykem budoucnosti (teda okrem prípadov keď nie). Ale nechme si filosofování o užitečnosti a životaschopnosti Dartu někam k pivu. Pokud vás tento jazyk zaujal tak jako mě, mám pro vás pár základních odkazů na zdroje, ze kterých jsem čerpal:</p><p>Základním zdrojem pro začátečníky je <a href="http://www.dartlang.org">dartlang.org</a>, kde najdete technical overview, <a href="http://try.dartlang.org/">dartboard</a> pro vyzkoušení Dartu online a v sekci Articles několik zajímavých článků. Zajímavý je <a href="http://www.dartlang.org/articles/idiomatic-dart/">Idiomatic Dart</a> o konvencích, jak psát v Dartu jako v Dartu a ne jako v JavaScriptu, C#, Javě.</p><p>Pokud vás zajímá vývoj pro server, na <a href="http://dartwatch.com/index.php/exploring-dart/">DartWatch</a> je série článků, jak vytvořit HTTP server a klientskou aplikaci.</p><p>Pokud chcete sledovat vývoj jazyka přímo u zdroje, na <a href="http://code.google.com/p/dart/w/list">Dart Wiki</a> je návod jak si nastavit vývojové prostředí (na DartWatch je <a href="http://dartwatch.com/index.php/guides/">podrobný tutorial</a>, ale ten postupně začíná být out-of-date. Diskuzní skupinu <a href="https://groups.google.com/a/dartlang.org/group/misc/topics">naleznete zde</a>.</p>Daniel Kolmanhttp://www.blogger.com/profile/12541334749939254340noreply@blogger.com2