). Když následně mluvíme o prvcích, máme na mysli konkrétní realizace značek v paměti prohlížeče. V zápisu zdrojového kódu je proto kupříkladu prvek odstavce zapisován pomocí značky . Jazyky s klasickým (třídním) objektovým modelem jednoznačně definují pojmy funkce a metoda. V JavaScriptu je ale situace jiná (vysvětleno to bude v šesté kapitole), a tak tato označení používáme dle aktuálního kontextu. To, co je jednou funkce, je jindy metoda a naopak. Termín signatura funkce označuje počet a typy parametrů, někdy zahrnuje i typ návratové hodnoty.
Opáčko základů JavaScriptu Většinu JavaScriptových konstrukcí v ukázkách kódu popíšeme a dostatečně vysvětlíme. Pojďme si rychle projít základní vlastnosti jazyka, ať se s nimi později nemusíme zdržovat:
Proměnné jsou buď globální (tj. dostupné odkudkoliv), nebo lokální (definované v rámci funkce). Lokální jsou k dispozici jen uvnitř funkce, kde jsou definovány. var a = 3; function f() { var b = 5; return a+b; } f(); // 8 alert(b); // Chyba! Proměnná „b“ je k dispozici jen uvnitř funkce
Jednoduché datové typy (číslo, řetězec, bool, undefined a null) jsou do funkcí předávány hodnotou. To znamená, že když je použijeme jako parametr, tak je při volání jejich hodnota zkopírována a uvnitř funkce se pracuje s touto kopií. Složitější datové typy (ty ostatní – pole, objekty, funkce a další) jsou předávány odkazem; pokud je jako parametry uvnitř funkce změníme, projeví se to i na hodnotě mimo funkci. Toto chování nelze ovlivnit.
22
K2209-sazba.indd 22
2.6.2015 12:02:26
Pár slov o prohlížečích
function add(array, value) { array.push(value); value += 1; } var array = [1, 2, 3]; var value = 4; add(array, value); alert(array); // [1, 2, 3, 4] alert(value); // 4
Funkce je datový typ. Funkce tak můžeme ukládat do proměnných a předávat jako parametry. var f = function() { alert(„Hi“); } setTimeout(f, 1000); // vykonat f za 1000 msec
Definice funkcí do sebe můžeme zanořovat. Lokální proměnné z vnější funkce jsou k dispozici i ve funkci vnitřní (dochází k takzvané uzávěře). function outer(a) { function inner(b) { return a+b; } return inner; } var f = outer(3); f(5); // 8
Pro čtenáře tápající i v těchto krátkých ukázkách je na webu k dispozici nepřeberné množství textů a začátečnických JavaScriptových lekcí. Autor knihy může jako velmi kvalitní zdroj informací doporučit (anglický) web https://developer.mozilla.org/.
Pár slov o prohlížečích Dnes (počátek roku 2015) jsou uživatelům k dispozici webové prohlížeče na skutečně vysoké technologické úrovni. Většina výrobců prohlížečů svůj software aktualizuje velmi často (mluvíme pak o tzv. evergreen prohlížečích), respektované standardizační instituce (WhatWG, W3.org) dohlíží na vznik a specifikaci nových rozhraní. Nebudeme si proto s otázkou tzv. cross-browser compatibility lámat hlavu; namísto toho se spokojíme s konstatováním, že v aktuálních verzích moderních prohlížečů (Mozilla Firefox, Google Chrome, Apple Safari, Microsoft Internet Explorer) bude veškerý kód v knize fungovat bez problémů.
23
K2209-sazba.indd 23
2.6.2015 12:02:26
KAPITOLA 3 Než se dáme do práce
Ve výčtu absentuje prohlížeč Opera – to proto, že jeho aktuální verze jsou z větších částí postaveny na komponentách Google Chrome, a proto s tímto prohlížečem téměř identicky sdílí funkcionalitu. Připomeňme však, že historicky situace vždy tak jednoduchá nebyla. V dřevních dobách webových aplikací patřila otázka kompatibility napříč prohlížeči k těm nejdůležitějším. Kód napsaný a otestovaný v prohlížeči X se mohl klidně ukázat jako kompletně nefunkční v jiném prohlížeči Y; něco takového samosebou vývoj brzdilo a notně komplikovalo. Důvodů pro takové chování existovalo hned několik:
Malá míra centralizované standardizace webových rozhraní; HTML značek, CSS selektorů a vlastností, JavaScriptových API. Když tak chtěl výrobce prohlížeče nabrat konkurenční výhodu, navrhl si pro vlastní potřeby API nové, nekompatibilní. Nízká frekvence aktualizací verzí prohlížečů. Díky tomu mohlo trvat třeba i několik let, než se naprogramovaná funkcionalita dostala mezi uživatele. Bouřlivý rozmach dynamických webových stránek v kontrastu s malou množinou existujících rozhraní. Pro pokročilejší problémy a techniky tak neexistovaly jednotné postupy k jejich řešení, což vedlo ke vzniku mnoha řešení navzájem nekompatibilních.
Tyto problémy s časem buď vymizely, nebo se nenápadně transformovaly do otázky kompatibility v desítkách a desítkách rozmanitých mobilních zařízení. Pro naše potřeby se naštěstí orientujeme jen na plnohodnotné prohlížeče desktopových počítačů. A ještě pozor – v závěrečných kapitolách knihy se objevují dvě velmi moderní (místy až experimentální) rozhraní, WebGL a Web Audio API. U nich je podpora v prohlížečích slabší, a tak se může stát, že kód ve starší verzi prohlížeče (nebo v závislosti na hardwaru a jeho ovladačích) fungovat nebude.
Coding style Zdrojové kódy v knize drží společnou štábní kulturu, takzvaný coding style. Bylo by pěkné, kdyby existovala jednotná pravidla pro zápis kódu – usnadnilo by to jeho čitelnost a spolupráci napříč vývojovými týmy. Něco takového však není možné, protože syntaktická pravidla programovacích jazyků (JavaScript nevyjímaje) většinou dovolují notnou dávku flexibility. Ve výsledku se pak často stává, že si každý vývojář navykne na nějakou sadu vlastních pravidel zápisu. Ta se snaží konzistentně dodržovat v celém projektu, ale takovýchto stylů je pak skoro stejně tolik, jako je vývojářů. Situaci komplikuje skutečnost, že k dobře napsanému zdrojovému kódu vývojáře často váže takřka mateřská hrdost; málokdo je pak ochoten přistoupit na změnu stylu zápisu jen kvůli konzistenci (třeba s ostatními členy týmu). Čtenář knihy má v této otázce naštěstí volnou ruku, a pokud se rozhodne samostatně přistoupit k implementaci, je
24
K2209-sazba.indd 24
2.6.2015 12:02:26
Coding style
pánem svého kódu. Při čtení knihy a souvisejících souborů však nezbude než respektovat styl autorův. Pojďme se podívat, jaká pravidla zápisu zdrojového kódu platí v této knize.
Řádky kódu jsou zleva odsazeny jedním tabulátorem za každou úroveň zanoření. Ve většině programovacích jazyků lze jako odsazení použít tabulátory nebo mezery; JavaScript je jedním z mála, ve kterém ještě užívání sudého počtu mezer zcela nepřeválcovalo tabulátory. Ne ve všech jazycích je tato benevolence: někdy je používání tabulátorů nutnost (Makefile), někdy to naopak nelze (YAML). Otevírací složené závorky jsou na konci řádku před otevřeným blokem kódu či definicí objektu. To je dobrá praxe s ohledem na ASI (Automatic Semicolon Insertion; vlastnost jazyka JavaScript způsobující automatické vkládání středníků na konce řádků). Tento kód by totiž v JavaScriptu napáchal pěkný nepořádek: function f() { return { hello: „world“ } }
Díky ASI by došlo k vložení středníku za klíčové slovo return, a funkce by tak vrátila undefined.
Složené závorky jsou použity i pro ohraničení bloku kódu s jediným příkazem; tento se ale zpravidla vejde na jeden řádek i s oběma závorkami. Názvy proměnných začínají malými písmeny, víceslovné názvy se spojují pomocí camelCase (odstranit mezery, od druhého slova dál s prvním velkým písmenem). Názvy konstant jsou velkými písmeny, První Velké Písmeno se používá pro globální objekty a funkce, ze kterých lze vytvářet instance objektů. Pole a objekty (jedná se o lehce matoucí pojmenování, ve skutečnosti jde o struktury typu klíč-hodnota) jsou definovány pomocí znaků [] a {}. Zápis new Array(), resp. new Object(), je funkčně identický a zbytečně zdlouhavý. Pole jsou iterována tradičním způsobem pomocí cyklu for. V JavaScriptu lze používat též funkcionální iteraci (metody map, forEach, filter a další); tato technika je zajímavou alternativou, ale nepřináší nic koncepčně nového, a proto se budeme držet klasického přístupu. V kódu je jen velmi málo komentářů. Dobře komentovaný kód je vždy pěkná vizitka programátora, ale v případě této knihy je kód více než zevrubně popsán v knize samotné. Další komentáře v kódu by tak spíše překážely. Pokud se v kódu přece jen čas od času komentáře objeví, jsou psány česky a s diakritikou. To není příliš obvyklá praxe, ale našinci to usnadní čtení.
25
K2209-sazba.indd 25
2.6.2015 12:02:26
KAPITOLA 3 Než se dáme do práce
Názvy proměnných, funkcí, objektů a jejich vlastností jsou anglicky. Působí totiž velmi nezvykle, když dochází k míchání (anglických) klíčových slov typu function, replace či length a českých názvů proměnných.
Test na závěr kapitoly Znalost jazyka (a zároveň připravenost pro další čtení) je nyní možno ověřit na krátkém příkladu. Ideálně bychom měli přesně chápat každý řádek této funkce, určené k výpočtu n-té hodnoty Fibonacciho posloupnosti (hodnota je definována jako součet dvou předchozích) s mezipamětí, do které si pro urychlení ukládáme mezivýsledky: /* Výpočet Fibonacciho posloupnosti s ukládáním mezivýsledků */ var fib = function(x, cache) { /* známe z předchozích výpočtů? */ if (x < cache.length) { return cache[x]; } /* cache se předá odkazem */ var result = fib(x-1, cache) + fib(x-2, cache); /* přidat mezivýsledek */ cache[x] = result; return result; }
/* posloupnost inicializujme hodnotami 0 a 1 */ var start = [0, 1]; alert( /* desátá hodnota */ fib(10, start) );
Generování hodnot Fibonacciho posloupnosti samosebou není hlavním úkolem této kapitoly ani knihy. Čtenář by teď již ale měl být připraven na výpisy zdrojových kódů a popisy algoritmů, protože to vše začneme používat hned od příští kapitoly.
26
K2209-sazba.indd 26
2.6.2015 12:02:27
KAPITOLA
První výkop
4
V této kapitole: Odbočka (historická): knihovny, vanilka a další Jdeme na to Důvod k menší oslavě!
Je na čase dát se do práce a vytvořit první – nebo spíše nultou – verzi aplikace. Do začátku nedbáme na žádná pravidla a také nepotřebujeme kompletní funkčnost; začněme s něčím jednoduchým a v dalších kapitolách pak doplníme zbytek.
Odbočka (historická) Knihovny, vanilka a další Jazyk JavaScript je tu s námi slušnou řádku let a za tu dobu si vypěstoval solidní infrastrukturu různých knihoven, modulů a dalších nástrojů. Hned zkraje se nabízí otázka, jestli by nám nějaký existující kód nemohl usnadnit práci, a učinit tak náš vývoj jednodušším a rychlejším. Pojďme se proto podívat, co máme koncepčně k dispozici.
Tradičním prvkem pro členění kódu je knihovna – sbírka znovupoužitelných funkcí, objektů a podobně. Knihoven pro JavaScript existuje nespočet a zpravidla mívají jedno konkrétní zaměření (knihovna kryptografických funkcí; knihovna pro práci s fyzikálními simulacemi; knihovna pro kreslení grafů a podobně). Některé knihovny vznikly z potřeby zastřešit nekonzistentní rozhraní napříč různými webovými prohlížeči, některé proto, aby existující funkcionalitě nabídly odlišné a jednodušší pojmenování. Důležitým zástupcem této třídy knihoven je jQuery, velmi populární nástroj Johna Resiga (první verze vznikla v roce 2006).
Speciální kategorií knihoven jsou takzvané polyfilly (česká varianta slova neexistuje). Jedná se o moduly (zpravidla menší velikosti), jejichž úkolem je do prohlížeče doplnit nějakou standardizovanou funkcionalitu, která v něm z různých – povětšinou historických – důvodů chybí. Příkladem může být třeba metoda Date.now(), která do jazyka přibyla ve verzi ES5. V prohlížečích, jejichž interpret JavaScriptu tuto verzi nepodporuje, je možné zmíněnou funkci doplnit triviálním kódem: if (!Date.now) { Date.now = function() { return new Date().getTime(); } }
Když pak bude požadovaná funkcionalita doplněna do prohlížeče samotného, lze polyfill z projektu prostě odstranit a není nutné upravovat aplikaci. Polyfilly tedy nemají žádné vlastní roz-
27
K2209-sazba.indd 27
2.6.2015 12:02:27
KAPITOLA 4 První výkop
hraní a při psaní aplikačního kódu s nimi nepracujeme explicitně. Využíváme jen rozhraní standardizovaná, která jsou k dispozici buď vestavěná, nebo doplněná polyfillem. Něco takového je možné jen s ohledem na dynamickou povahu jazyka JavaScript.
Řádově složitější přístup představují frameworky (česky aplikační rámce, málo používaný překlad). Jedná se o téměř hotová řešení pro celé třídy problémů, která si vývojář vždy jen adekvátně upraví či nastaví podle svých konkrétních požadavků. Framework zpravidla sestává z většího množství provázaných souborů, poskytuje významně složitější rozhraní a nezřídkakdy je objemnější než samotný aplikační kód. Hranice mezi knihovnou a frameworkem není vždy jednoznačná. Obecně se ale dá říci, že knihovny využíváme dle vlastní potřeby s ohledem na vlastní kód, při použití frameworku jsme naopak nuceni následovat konvence a postupy, které stanovil jeho autor. Náš kód je pak spíše knihovnou využívanou frameworkem samotným.
Nesmíme opomenout preprocesory – nástroje, které dovolují psát kód v upravené verzi jazyka – nebo v jazyce úplně jiném. Ten je pak před spuštěním transformován (někdy se užívá zdobný termín transpilován) do JavaScriptu odpovídajícím programem. Vývojář si tak může vybrat, pokud mu (syntakticky) vyhovuje jiný zápis. Nejčastěji transpilovaným jazykem je CoffeeScript, známý je též TypeScript, doplňující do JavaScriptu informace o typech proměnných – takzvané typové anotace.
Poslední termín, který stojí za zmínku, je tzv. vanilla (česky vanilka). Používáme ho pro označení takového kódu, který nezávisí na knihovnách a modulech třetích stran, a je tedy psaný jen s využitím existující vestavěné funkcionality (a volitelně potřebných polyfillů).
Pro potřeby této knihy je veškerý kód psán ve vanilla JavaScriptu. Naše úloha je natolik specifická a jednoduchá, že nepotřebujeme žádné nadstavby. Zejména se ale snažíme problematice skutečně podrobně porozumět, a bylo by proto kontraproduktivní spoléhat se na kód ukrytý v cizích knihovnách.
Jdeme na to Máme k dispozici pestrou paletu webových technologií. Ta stojí na třech hlavních pilířích: HTML, CSS a JavaScript. Výstupem naší práce bude tedy HTML stránka; pro jednoduchost zatím vložíme kód přímo do ní (časem bude na místě jej oddělit do souborů stranou). Jazykům HTML a CSS se v knize již dále věnovat nebudeme, pojďme si proto nyní poměrně podrobně po částech projít výsledný soubor index.html:
<meta charset=“utf-8“ /> Atomy, kapitola 4
28
K2209-sazba.indd 28
2.6.2015 12:02:27
Jdeme na to
Deklarace doctype dává tušit, že se jedná o HTML5, aktuálně používanou verzi jazyka. Do hlavičky stránky patří (povinný) titulek a značka <meta> definuje kódování souboru. To není vždy nutné uvádět (správně nastavený webový server to udělá za nás), ale není to na škodu. Jakým způsobem v HTML nejsnáze reprezentovat hrací plochu, tj. čtvercovou mřížku? Nabízí se tabulka (), ale bylo by jistě zbytečně pracné ji celou definovat pomocí ručně zapsaných HTML značek. V souboru si tedy připravíme tabulku prázdnou a její buňky si necháme vygenerovat skriptem. S tím souvisí i otázka stylování tabulky a vykreslování jednotlivých atomů. Jdeme cestou nejmenšího odporu, a tak pro atomy použijeme písmeno o, které se podobá kolečku. Nestaráme se rovněž o estetické rozmístění atomů v buňce a necháme je pouze zarovnat na střed. Kód související se vzhledem aplikace pak vypadá takto: <style> td { border: 1px solid black; width: 60px; height: 60px; text-align: center; color: blue; }
U značky <style> můžeme vynechat atribut type, neboť jeho výchozí hodnota je text/css. Buňkám tabulky nastavíme orámování, rozměry, zarovnání na střed a barvu písma. Při psaní HTML stránek nebývá zvykem umísťovat značku <style> benevolentně kamkoliv v těle dokumentu (patří spíše do hlavičky nebo ještě lépe do odděleného souboru ve prospěch značky ), ale pro účel snadného pochopení kódu je praktické mít styl poblíž tabulky. Dále nás čeká skript, který se postará o výrobu celé tabulky. Atribut type lze opět, stejně jako u stylu, vynechat: <script> var table = document.querySelector(„table“); for (var i=0; i<6; i++) { var row = document.createElement(„tr“); for (var j=0; j<6; j++) { var cell = document.createElement(„td“); row.appendChild(cell); }
29
K2209-sazba.indd 29
2.6.2015 12:02:27
KAPITOLA 4 První výkop
table.appendChild(row); }
Pro přístup ke značce ve stránce použijeme metodu querySelector, která je velmi užitečným (a přitom často opomíjeným) způsobem dotazování HTML dokumentu. Jejím parametrem je selektor jazyka CSS, návratovou hodnotou je první HTML prvek, který selektoru vyhovuje. Pro výrobu dvourozměrné tabulky potřebujeme dva zanořené cykly. Iterační proměnné pojmenujeme dle tradičních zvyklostí písmeny i (řádky) a j (sloupce). Tabulka bude mít 6 × 6 buňek, každá z nich odpovídá HTML značce . Jakmile je každý řádek hotov, připneme ho do tabulky. Zbývá doplnit logiku interakce uživatele s aplikací. Při klepnutí myší na buňku do ní musíme přidat atom. Zatím se zajímáme jen o jednoho uživatele (také proto jsou všechny naše atomy modré), a protože neimplementujeme řetězovou reakci, nemusíme se ani starat o počet atomů v buňce: document.addEventListener(„click“, function(e) { var node = e.target; if (node.nodeName == „TD“) { update(node); } }); var update = function(cell) { cell.innerHTML += „o“; }
Vidíme zde tzv. programování řízené událostmi, kdy dopředu definujeme, jak má aplikace reagovat na různé druhy uživatelské interakce. Klíčová je pro to metoda addEventListener, která provádí párování typu události (zde klepnutí myší, “click“) s funkcí, která má být následně vykonána. V našem případě jsme použili takzvanou anonymní funkci, které není důvod dávat jméno, když ji máme v plánu jen předat coby posluchač události. Anonymní funkce tedy bude vykonána po klepnutí myší a ve specifikaci se můžeme dočíst, že jí bude předán jeden parametr, objekt události. Ten obsahuje jednak užitečné metody pro řízení zpracování události, druhak také vlastnosti popisující událost samotnou. Nás zajímá zejména vlastnost target, která odpovídá HTML prvku, na který bylo klepnuto. Jeho vlastnost nodeName je název značky (a z historických důvodů je uváděna vždy velkými písmeny); zpracujeme tedy jen ty události, při kterých bylo klepnuto na buňku tabulky. Na definici funkce update je dobře vidět, že funkce je v JavaScriptu normální datový typ (a lze ji tak uložit do proměnné a předávat dále). Pracujeme zde s vlastností innerHTML, která odpovídá zřetězenému obsahu HTML prvku. Naše volání tak dovnitř buňky vloží na konec jedno další písmeno o (operátor += je zkratka za = původní hodnota +).
30
K2209-sazba.indd 30
2.6.2015 12:02:27
Důvod k menší oslavě!
Čtenáře by nemělo zmást, že volání funkce update se nachází o několik řádků výše než její definice. To je v pořádku, neboť tuto funkci prohlížeč vykoná až v rámci posluchače události, tedy mnohem později (pokud vůbec), než proběhne inicializace var update = ....
Důvod k menší oslavě! Máme hotovo. Napsali jsme méně než padesát řádků a máme funkční aplikaci! To rozhodně není špatné. V závislosti na použitém písmu může výsledek vypadat nějak takto:
Obrázek 4.1 První funkční prototyp
Pojďme si na konec kapitoly shrnout hlavní nedodělky současné verze a zvážit, jak pracné by bylo jejich odstranění.
Mezi jednotlivými buňkami tabulky je bílé místo. Souvisí to se způsobem, kterým se v HTML tabulky vykreslují. Ovlivnit to můžeme přidáním CSS vlastnosti border-collapse na prvku tabulky.
31
K2209-sazba.indd 31
2.6.2015 12:02:27
KAPITOLA 4 První výkop
Neexistuje žádný test na kritické množství atomů v buňce. Opakovaným klikáním tak můžeme (cca sedmým atomem) rozbít rozložení tabulky. Paušální omezení na maximální hodnotu (čtyři) by šlo snadno přidat triviálním testem v metodě update. Stav hry nedržíme v žádné JavaScriptové proměnné; namísto toho počty atomů v buňkách ukládáme jako posloupnosti písmen do tabulky. Je zřejmé, že pro potřeby dalšího rozvoje bude nutné toto provizorní řešení značně vylepšit. Dovolujeme účast jen jednoho hráče. To souvisí mj. s tím, že u buňky hrací plochy není možné poznat, kterému z případných dalších hráčů patří. Tento nedostatek bude možné odstranit teprve poté, co naimplementujeme důmyslnější způsob ukládání stavu hry.
Jak vidno, prostoru pro zlepšení je více než dost. Zadání kapitoly je nicméně splněno; v té další se podíváme, jak se připravit na další růst kódu a funkcionality.
32
K2209-sazba.indd 32
2.6.2015 12:02:27
KAPITOLA
Modularizace
5
V této kapitole: Návrhové vzory a API SoC, SRP Odbočka (terminologická): Bikeshedding Hlavní komponenty Odbočka (historická): globals, CommonJS, AMD, ES6
Kód z předchozí kapitoly je funkční v tom smyslu, že pracuje dle zadání a na první pohled neobsahuje žádné chyby. Mohli bychom se tedy nyní soustředit na jeho vylepšování a přidávání další logiky tak, abychom se přiblížili hratelné verzi. Je však pravděpodobné, že by postupným rozšiřováním začal kód bobtnat a od jisté chvíle (či možná od jistého počtu řádků) by se v něm začal ztrácet každý čtenář. Čitelnost zdrojového kódu je přitom velmi důležitým měřítkem při hodnocení jeho kvality. To platí jak v případě několika spolupracujících vývojářů (kteří si mezi sebou navzájem musí samosebou rozumět), tak i když je kód produktem jednotlivce. Není neobvyklé, že se vývojář po několika měsících odpočinku podívá na svůj starší kód a u špatně čitelných částí nemá vůbec tušení, jak fungují. Proto nyní musíme odhlédnout od vývoje od další funkcionality a věnovat se chvíli rozdělení kódu na menší části.
Návrhové vzory a API Na první pohled není vůbec patrné, kde by se mělo do kódu říznout, tj. jestli ho rozdělit na funkce, na JavaScriptové objekty, na soubory či nějaké jiné části. Ať už se rozhodneme pro kterýkoliv způsob, je jasné, že to pro nás přinese práci navíc – tyto nově vzniklé komponenty mezi sebou totiž budou muset nějak komunikovat. Dostáváme se tak k jádru programátorského řemesla: k neustálému a opakovanému navrhování a implementaci miniaturních rozhraní, k návrhu parametrů funkcí a jejich návratových hodnot, k definici vhodných datových typů a dalšímu. Pokud při práci využíváme cizí rozhraní (anglicky API – Application Programming Interface), jsme odkázáni na takový návrh, který poskytl jeho autor. V dokumentaci nalezneme správná jména funkcí a jejich signatury, diagramy závislostí a dědičností tříd, 33
K2209-sazba.indd 33
2.6.2015 12:02:27
KAPITOLA 5 Modularizace
datové typy. Jakmile ale začneme navrhovat API vlastní (byť jen pro osobní potřebu), sami zodpovídáme za jeho formu, design, konzistenci a použitelnost. A to rozhodně není lehký úkol. Pomoci nám v takovou chvíli mohou například tzv. návrhové vzory (anglicky Design patterns) – sada postupů a doporučení pro navrhování chování jednotlivých komponent aplikace. Nejlepším zdrojem pro hlubší studium návrhových vzorů je kniha Design Patterns1, která i dvacet let po svém prvním vydání zůstává uznávanou biblí pro mnoho vývojářů. Je tomu tak i proto, že návrhové vzory mají ambice fungovat napříč programovacími jazyky a nebýt vázány na konkrétní technologii. Nejsou to tedy jednoznačné definice nebo přímo úryvky zdrojového kódu; jde spíše o obecná paradigmata toho, jak lze navrhovat členění aplikace do menších částí. Součástí konceptu návrhových vzorů je i sjednocené pojmenování, takže třeba při používání názvu vzoru Factory si můžeme být jisti, že nám bude rozumět jakýkoliv jiný vývojář.
SoC, SRP Nejobecnější doporučení, aplikovatelné na téměř jakýkoliv programátorský problém, se anglicky nazývá Separation of Concerns (SoC; česky Oddělení zodpovědností). Učí nás, že kód by měl být strukturován především do takových částí, které řeší oddělené problémy, a tím pádem svoje chování navzájem neduplikují. V případě naší hry tak dává smysl nějakou formou odlišovat vizuální stránku (zobrazování hracího pole, jednotlivých atomů, provádění animací a podobně), logickou stránku (proces umísťování atomů, kontrolu počtu, provádění reakcí), interaktivitu (práci s myší) a případné další funkce, které do aplikace v budoucnu přidáme. Ruku v ruce se SoC jde další obecný koncept, Single Responsibility Principle (SRP; česky Princip jedné odpovědnosti). SRP staví na myšlence, že jedna část programu (objekt, funkce, třída, …) by měla v ideálním případě provádět jen jednu činnost – a za tu být adekvátně zodpovědná. Pokud se tedy například ukáže, že nějaký zajímavý algoritmus (řekněme seřazení dat) je vyžadován na více místech kódu, v souladu se SRP bude vhodné pro tuto činnost vyrobit komponentu a využívat její služby tam, kde je to požadováno.
Odbočka (terminologická) Bikeshedding V tuto chvíli už máme alespoň lehce jasno v tom, do jakých částí chceme současný kód rozdělit. Zatím však ale nevíme, na jaké technologické úrovni by se tak mělo stát (bude-li to dělení na
1
http://en.wikipedia.org/wiki/Design_Patterns, ISBN 0-201-63361-2
34
K2209-sazba.indd 34
2.6.2015 12:02:27
Hlavní komponenty
funkce, objekty, soubory, …) a jak výsledné komponenty pojmenovat. To je ideální chvíle na připomenutí tzv. bikesheddingu – jevu, který často pozorujeme nejen v oblasti výpočetní techniky. Představme si výbor úředníků, kteří mají za úkol schválit plán výstavby jaderné elektrárny. To je velká, složitá a nebezpečná věc; proto také podklady pro stavbu reaktoru dodali jaderní inženýři a většina bodů souvisejících s fungováním a řízením štěpné reakce je schválena bez větší diskuze. Když pak ale dojde na zcela okrajovou a nepodstatnou součást (například na barvu či materiál přístřešku na kola, který bude vystaven vedle vchodu do elektrárny), začnou se dít věci. Každý člen výboru má na věc názor, navrhuje vlastní řešení a čile diskutuje s ostatními. Tomuto říkáme Zákon triviality: Míra důležitosti je nepřímo úměrná míře zájmu veřejnosti2. Takovou barvu přístřešku na kola (anglicky colour of the bike shed) pak potkáváme při rozhodování dílčích problémů, které nejsou pro vyřešení hlavního úkolu zásadní. Jaké jméno zvolit pro nový objekt? Vytvořit metodu statickou či nikoliv? Použít v názvu souboru tečky či pomlčky? Tento vývojářský bikeshedding je jasnou ztrátou času. Můžeme ho ale využít inverzně: pokud se k nějakému tématu opakovaně vracíme a stále nejsme s jeho formou spokojeni, může to být ukazatelem skutečnosti, že nejde o nic závažného. V případě naší aplikace nebudeme barvou přístřešku na kola plýtvat časem a sáhneme po prvním řešení, které je po ruce: jednotlivé komponenty budeme reprezentovat coby JavaScriptové objekty a pro každý vytvoříme vlastní soubor. Zvolíme pojmenování anglickými slovy s prvním velkým písmenem (abychom tak názvy komponent odlišili od názvů proměnných).
Hlavní komponenty Čeká nás rozdělení monolitického řešení z předchozí kapitoly do souborů odpovídajících hlavním komponentám aplikace. Z původního velkého HTML souboru moc nezbude; kromě základních značek veškeré skripty i styly umístíme bokem: <meta charset=“utf-8“ /> Atomy, kapitola 5 <script src=“draw.js“>
<script src=“board.js“>
2
http://en.wikipedia.org/wiki/Parkinson%27s_law_of_triviality
35
K2209-sazba.indd 35
2.6.2015 12:02:27
KAPITOLA 5 Modularizace
<script src=“player.js“> <script src=“game.js“> <script>Game.start()
Nově vzniklý stylopis (style.css) snad ani nestojí za zmínku, neboť nedoznal změn: td { border: 1px solid black; width: 60px; height: 60px; text-align: center; color: blue; }
O práci s hlavní datovou strukturou (totiž stavem hrací desky) se nově stará soubor board.js: var Board = []; var N = 6; for (var i=0; i
Globální proměnná Board je zcela obyčejné pole polí (JavaScript nemá žádná vícerozměrná pole) naplněné nulami (tj. všechny hrací buňky jsou prázdné). Rozměr hrací desky, proměnnou N, jsme zatím definovali jen pro potřeby souboru board.js, nicméně s ohledem na nulové zapouzdření je opět globální. Interakci uživatele s aplikací pokrývá soubor player.js: var Player = {}; Player.listen = function() { document.body.addEventListener(„click“, Player.click); } Player.click = function(e) { var position = Draw.getPosition(e.target); if (!position) { return; }
36
K2209-sazba.indd 36
2.6.2015 12:02:27
Toto je pouze náhled elektronické knihy. Zakoupení její plné verze je možné v elektronickém obchodě společnosti eReading.
|