-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaplikacia.tex
189 lines (144 loc) · 14.3 KB
/
aplikacia.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
\chapter{Motivácia a spôsob prekladania kódu}%TODO preco a ako to chceme prelozit
\label{kap:motivacia} % id kapitoly pre prikaz ref
V práci sa sústredíme na funkčnosť a udržateľnosť kódu. Zaujíma nás najmä tá časť aplikácie, ktorá zabezpečuje funkcionalitu pre používateľa. Prostredie, v ktorom aplikácia beží (prehliadač, server, prihlásenie, \ldots) v práci neriešime, na tieto časti kódu sme využili polotovar (\emph{boilerplate})
\emph{este} \cite{Este} pre kód v \JS{}.
V aplikácii v jazyku Dart sme túto funkcionalitu zabezpečili vlastnými pomocnými knižnicami a~nástrojom \emph{pub}.
%Téma na túto prácu vznikla z praxe.
\section{Motivácia}
\paragraph{Zmena programovacieho jazyka}
Existujúca aplikácia vyvíjaná v jazyku Dart má svoje zabehnuté prostredie, v ktorom beží. Dart je výborný jazyk pre začínajúci tím (má voliteľnú kontrolu typov, pomerne presnú štruktúru), už však nie je taký živý a knižnice k nemu vznikajú pomaly a v obmedzenom množstve.
Taktiež podpora vývojových prostredí je menšia a slabšia. V čase, keď jazyk Dart začínal, existovalo preň vývojové prostredie. Dnes existuje viacero programov s podporou Dart-u.
JavaScript je skriptovací jazyk, ktorý beži v prehliadači priamo. Nevýhodou Dart-u je nutnosť kompilovať tento jazyk do JavaScript-u, ktorý vie byť vykonaný prehliadačom. \JS{} je pomerne nová verzia JavaScript-u, preto nie je ešte plne podporovaný všetkými prehliadačmi. Je nutné tento jazyk transpilovať do staršej verzie (napríklad ES5 má dobrú podporu). Má však potenciál bežať v budúcnosti rýchlejšie.
Práve z toho dôvodu bola snaha presunúť už existujúci kód do jazyka JavaScript, ktorý je v súčastnosti veľmi živý jazyk podporovaný mnohými komunitami.
Aj keď majú tieto jazyky isté odlišnosti, sú navzájom veľmi podobné a bolo jednoduché prispôsobiť sa jazyku JavaScript po skúsenostiach s Dart-om.
Rozdiel bol viac v~použitých knižniciach ako v~samotných jazykoch.
\paragraph{Jazykové mutácie}
Veľký prínos sme zaznamenali v spravovaní jazykových mutácií aplikácie. Knižnica \emph{react-intl} vie vyexportovať nepreložené a nové frázy, aj keď nie sú uložené v jednom súbore, čo sprehľadňuje správu týchto fráz.
\paragraph{Zmena návrhového vzoru}
Flux je pomerne jednoduchý návrhový vzor. Ako sme spomenuli vyššie, Redux-ová aplikácia je aj Flux-ová. Pridáva však pravidlá a obmedzenia, vďaka ktorým sa stáva aplikácia viac prehľadná a lepšie krokovateľná. Pri Redux-e je jasné, kedy a aké dáta sa zmenili. Redux vďaka čistým funkciám a zoznamu vykonaných akcií poskytuje možnosť simulovať celý beh v ľubovolnom čase, zopakovať niektoré kroky s rovnakým stavom aplikácie alebo sa vrátiť v čase dozadu.
V pôvodnej aplikácii sme robili dotazy na server z jednotlivých storov. V novej aplikácii sme túto funkcionalitu nemohli nechať na reducer, pretože by to porušilo princípy Redux aplikácie, preto tieto dotazy robí middleware. Pri písaní novej aplikácie tento vzor upriamil našu pozornosť na to, ktorá akcia je príčinou takéhoto dotazu na~server, keďže bolo potrebné počúvať práve na ňu.%TODO skratit a rozdelit
\paragraph{Entity}
V aplikácii sme spravili jednu zásadnú zmenu so štruktúrou vnútorného stavu aplikácie. V pôvodnej aplikácii boli entity (objednávky, protokoly) priamo v stave pod daným kľúčom.
V Redux-ovej aplikácii sme ich presunuli do samostatnej vetvy, kde sú jednotlivé entity uložené pod typom a identifikačným reťazcom. Potom v stave namiesto zoznamu celých entít existuje len zoznam ID (identifikátorov).
Túto zmenu sme spravili z toho dôvodu, aby bolo jednoduchšie upravovať jednotlivé entity. Teda aj keď sa na danú entitu odkazujeme na viacerých miestach, jej dáta upravujeme len na jednom. Túto funkcionalitu nám pomáha udržiavať knižnica \emph{normalizr}.
Tiež pri veľkom množstve entít to uľahčilo vyhľadávanie danej entity podľa ID (netreba iterovať cez pole entít, stačí sa pozrieť do mapy pod správnym kľúčom).
\paragraph{Hot reloading}
Program \emph{pub}, ktorý zabezpečuje beh programu v kóde Dart v prehliadači, nepodporuje hot reloading. Pri každej zmene treba obnoviť stránku.
Pre JavaScript hot reloading v našom kóde zabezpečuje knižnica \emph{webpack} \cite{webpack}.
Vyvíjanie aplikácie s možnosťou hot reloading ponúka rýchlejšie výsledky a lepší zážitok z programovania :).
\section{Ako preložiť všetky časti kódu}
V nasledujúcej časti opíšeme návrh, ako by sa mohli preložiť jednotlivé časti kódu v~jazyku Dart s použitím návrhového vzoru Flux do kódu v jazyku \JS{} s použitím návrhového vzoru Redux.
\subsection{Komponenty}
Úlohou komponentov v aplikácii je vytvoriť prostredie pre používateľa a poskytnúť mu rozhranie pre interakciu so systémom. Každý komponent predstavuje nejaký objekt v~danom jazyku.
Stále platí, že komponenty nesmú meniť stav. Jediný spôsob, ako by mali mať možnosť toto uskutočniť, je vytvorenie akcie.
V jazyku Dart sú komponenty samostatnými triedami (každý komponent je triedou dediacou od triedy Component z knižnice \emph{tiles}) [ukážka \ref{lst:dartComponent}, riadok 14].
V jazyku \JS{} používame na vytvorenie komponentov knižnicu \emph{React}. Zvolili sme si syntax, kde definujeme komponent ako funkciu s voliteľnými pomenovanými parametrami, ktorá vráti jeden objekt (nie pole objektov) [ukážka \ref{lst:jsComponent}, riadok 33].
Ak definujeme tomuto objektu okrem parametrov aj nejaký obsah, je dostupný cez~parameter \emph{children}.
V tabuľke \ref{table:components} popisujeme funkcionalitu komponentov v jazyku Dart so vzorom Flux. Uvádzame riešenia, ako zabezpečiť jednotlivé úlohy v jazyku \JS{} so vzorom Redux.
V ukážke \ref{lst:dartComponent} je komponent v~jazyku Dart a v ukážke \ref{lst:jsComponent} je ten istý komponent v~jazyku \JS{}, na ktorý sme aplikovali pravidlá z tabuľky.
\begin{comment}
\paragraph{Komponenty v JavaScript-e s Redux-om}
V JavaScriptovej aplikácii máme dva typy komponentov: kontajnerové a prezenčné.
Prezenčné sú jednoduchšie, priamočiarejšie. Všetky parametre, ktoré potrebujú, by mali dostať od volajúceho komponentu (vrátane funkcií na vytváranie akcií). Nemajú prístup ku celému stavu aplikácie. Mali by to byť hlavne znovu použiteľné komponenty (napríklad riadok tabuľky).
%TODO ukážka prezenčného komponentu
Kontajnerové komponenty sú o niečo zložitejšie. Majú prístup ku celému stavu aplikácie, z ktorého si vyberajú len potrebné časti. Na to nám pomáha funkcia \emph{connect} z knižnice \emph{react-redux}. Kontajnerový komponent vyzerá podobne ako prezenčný, tiež všetko vykresľuje len zo svojich parametrov.
Funkcia \emph{connect} tvorí medzivrstvu medzi týmto komponentom a jeho exportovaným variantom. Do tohto komponentu doplní potrebné dáta zo stavu. Tieto komponenty sú však menej univerzálne, keďže sú viazané na konkrétne dáta zo stavu.
\end{comment}
%TODO nezalamovat za predlozkami
\begin{table}
\caption{Úlohy komponentov}
\label{table:components}
\begin{tabular}{| p{0.3\linewidth} | p{0.3\linewidth} | p{0.3\linewidth} |}
\hline
Vlastnosť & implementácia v~Dart s~Flux-om & implementácia v~\JS{} s~Redux-om \\
\hline
\hline
vykreslenie komponentu, prekreslenie pri zmene dát &
knižnica \emph{tiles} &
knižnica \emph{react} \\
\hline
jazykové mutácie &
vlastná knižnica &
knižnica \emph{react-intl} \\
\hline
zistenie aktuálnej routy &
manipulator.router.url, knižnica \emph{route\_hierarchical} &
knižnica \emph{react-router} poskytuje pri vykreslení dáta o aktuálnej route \\
\hline
vytvorenie novej akcie &
dispatch a dispatchAsync v komponente alebo store (zabezpečuje kniž. \emph{flux}) &
kontajnerový komponent s funkciou \emph{connect} \ref{func:connect} \\
\hline
zmena routy &
dispatch(\{type: ROUTE, \ldots\}) &
komponent Link z knižnice \emph{react-router} \\
\hline
\end{tabular}
\end{table}
\input komponentyListing.tex
\subsubsection{Špeciálne konštrukcie}
Ide o prvky prostredia Dart, ktoré sa v prostredí \JS{} nenachádzajú, alebo ich plánujeme riešiť špeciálne iným spôsobom.
\subparagraph{Gettery}
V Dart-ovom kóde sme častokrát využívali gettery \ref{par:getters} (getter v čase zavolania vyhodnotí výraz, ktorý reprezentuje). Môžeme ku getterom pristúpiť troma spôsobmi:
\begin{itemize}
\item definujeme tieto výrazy ako funkcie mimo komponentu,
\item v komponente, v ktorom sme chceli použiť getter, si zadefinujeme pomocnú premennú, do ktorej uložíme výsledok vyhodnoteného výrazu,
\item na miesto volania getteru vložíme priamo výraz z gettera.
\end{itemize}
Getter mal za úlohu sprehľadniť kód, keďže jeho volanie bolo jednoduché [ukážka \ref{lst:dartComponent}, riadok 42].
V ukážke \ref{lst:jsComponent} [riadok 81] z aplikácie je použitý prvý prístup. Tento zachováva pôvodnú štruktúru kódu. Tento kód sa dá ešte refaktorovať použitím tretieho spôsobu nahradenia getterov. V ukážke sme však zvolili prvý, aby bola zachovaná názornosť prekladania kódu.
\subparagraph{Mixiny}
V Dart-ovom kóde sme časti kódu, ktoré sa opakovali, dali do mixinov \ref{par:mixins}. V JavaScript-e s Redux-om však chceme preferovať vykreslenie čistými funkciami, čo podporuje aj ideológia knižnice \emph{react}. Preto sme z mixinov spravili malé pomocné knižnice s čistými funkciami, ktoré len jednoducho importujeme. Často tieto mixiny využívali práve dáta danej triedy (komponentu) pomocou getterov. Toto ale pri knižniciach nie je možné, keďže komponent nezdieľa svoje dáta s knižnicami, ktoré používa.
\subparagraph{Perzistentné štruktúry}
Štruktúry/dáta v kóde môžeme udržať perzistentné dvoma spôsobmi.
Jedným je dôkladné kontrolovanie kódu, ktorý píšeme. Pri akomkoľvek úmysle zmeniť tieto dáta vytvoríme novú inštanciu.
Druhým je použitie vhodnej knižnice, ktorá túto správu a kontrolu bude robiť za nás.
Perzistentné štruktúry sme v jazyku Dart používali s pomocnou knižnicou \emph{vacuum\_persistent}. V jazyku \JS{} sme sa rozhodli používať prvú možnosť. Rozhodli sme sa tak na základe jednoduchosti používania rozširovacieho operátora \ref{par:spreadOp}, ktorý je v jazyku JavaScript dostupný od verzie ES6.
\subsection{Story}
Story v pôvodnej aplikácii spracúvajú všetky akcie. Každý store má zoznam akcií, na~ktoré počúva. Porovnanie funkcií časti store uvádzame v tabuľke \ref{table:store}.
V ukážke kódu \ref{lst:fetchJS} vidíme, ako je implementovaná komunikácia so serverom v jazyku \JS{} so vzorom Redux. Pre porovnanie uvádzame v ukážke \ref{lst:fetchDart} rovnakú funkcionalitu v jazyku Dart so vzorom Flux. Môžeme vidieť, že vo Flux-ovej aplikácii sa vytvárajú nové akcie postupne, zatiaľ čo v Redux-ovej ukážke máme na túto funkcionalitu middleware.% ktorý odchytí akciu, vykoná ju a zároveň s ním (middleware-om) sa zmení interný stav v reduceri.
Middleware aj reducer odchytia rovnakú akciu a reagujú na~ňu nezávisle.
\begin{table}
\caption{Úlohy store}
\label{table:store}
\begin{tabular}{| p{4cm} | p{5cm} | p{5cm} |}
\hline % na vrchu tabuľky je čiara
Vlastnosť & implementácia store v~Dart s~Flux-om & implementácia v~\JS{} s~Redux-om \\
\hline
\hline
uchováva a mení vnútorný stav aplikácie &
insert, insertInOrCreate &
reducer \\
\hline
komunikácia so serverom &
dispatchAsync &
middleware \\
\hline
zmena routy &
dispatchRoute, dispatch(GOROUTE) &
komponent Link z knižnice \emph{react-router} \\
\hline
vytvorenie novej akcie &
dispatch &
vyhnúť sa tomu, použiť reducery vo vhodnom poradí \\%v \emph{configureReducer.js}
\hline
\end{tabular}
\end{table}
\input lstDotazNaServer.tex
\subsection{Akcie}
Každá akcia má typ a voliteľné prídavné dáta. %niekoľko centrálnych skladísk akcií
Toto platí v oboch návrhoch aplikácie. Typ akcie je vždy textový reťazec. Dáta sú (podľa štandardu \cite{fluxStAction}) jeden objekt \emph{payload}.
Vďaka prirodzenej kompozícii a dekompozícii objektov v \JS{} [\ref{lst:deklarovanie}] sa s objektom \emph{payload} dobre pracuje.
(Nielen) V Redux-e je dobrým zvykom definovať všetky akcie na jednom mieste z dvoch dôvodov. Prvým je prehľad o celej funkcionalite súčasne a druhým je, že v prípade preklepu nevytvoríme nezmyselnú akciu.
Definujeme tieto akcie ako funkcie, ktoré pri namapovaní na dispečer vytvoria akciu s daným typom. Takúto akciu potom namapujeme na dispečer v kontanjerovom komponente pomocou funkcie \emph{connect} \ref{func:connect} cez jej druhý parameter.
Na akcie počúvajú story aj middlewares.
\input openSourceKniznice.tex
\section{Súborová štruktúra aplikácie}
\paragraph{Flux}
V aplikácii, ktorá bola v návrhu Flux, sme mali súbory usporiadané podľa typu súboru, teda zvlášť Story a zvlášť Komponenty. Pre akcie sme nemali žiaden priečinok, boli definované len konštanty, z ktorých sa typy akcií skladali.
Tento prístup bol výhodný, ak sme hľadali súbor podľa funkcionality.
\paragraph{Redux}
V aplikácii s návrhovým vzorom Redux sme zvolili usporiadanie súborov podľa toho, akej časti stavu sa venujú. Teda ak robíme úpravu jednej entity, meníme len jeden priečinok z celej aplikácie.
Výhodou tohto prístupu je, že máme všetky súbory týkajúce sa jednej problematiky na jednom mieste.
Akcie definujeme v jednom súbore. Máme tak naraz prehľad o~všetkých scenároch, aké sa môžu na stránke udiať.
V aplikácii v Redux-e pripravujeme kód univerzálne, aby aplikácia v budúcnosti mohla bežať okrem prehliadača aj na mobilných zariadeniach. Preto máme rozdelené súbory do priečinkov \emph{common} a \emph{browser}. V priečinku \emph{common} sa snažíme udržať čo najviac súborov, ktoré obsahujú logiku (hlavne store, akcie a veľkú časť komponentov). V \emph{browser} sú súbory, ktoré obsahujú funkcionalitu čisto pre prehliadač.