tag:blogger.com,1999:blog-83956404260392888362024-03-20T13:03:55.467+01:00Malé radosti enterprise developeraSložité pokusy zformulovat jednoduché myšlenky.Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.comBlogger19125tag:blogger.com,1999:blog-8395640426039288836.post-65724629929809921522019-05-23T15:10:00.001+02:002019-05-23T15:10:47.417+02:00Layers, packages a architektonicky evidentní styl kódování<div dir="ltr" style="text-align: left;" trbidi="on">
<a href="https://profinit.eu/blog/layers-packages-a-architektonicky-evidentni-styl-kodovani/">https://profinit.eu/blog/layers-packages-a-architektonicky-evidentni-styl-kodovani</a></div>
Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-68527473703960298582019-02-12T17:30:00.001+01:002019-02-12T17:30:20.068+01:00Jak implementovat cache a nezamrznout u toho<div dir="ltr" style="text-align: left;" trbidi="on">
<a href="https://profinit.eu/blog/jak-implementovat-cache-a-nezamrznout-u-toho">https://profinit.eu/blog/jak-implementovat-cache-a-nezamrznout-u-toho</a><br />
<br /></div>
Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-90314336261937368682015-11-01T22:02:00.001+01:002015-11-01T22:02:40.151+01:00Jak se vyplatí kešování?<div dir="ltr" style="text-align: left;" trbidi="on">
Vyplatí se kešování? Jak moc? Dá se účinnost kešování předem nějak kvantifikovat? Jak nastavit velikost a retenci keše? Na tyto a podobné otázky se pokusí odpovědět můj článek.<br />
<br />
Před časem jsme na jednom produkčním systému - službě vracející stavy a historii zpracování balíků - zvažovali možnost konfigurace kešování. Kešovat jsme chtěli stav a historii pro daný identifikátor. Jde ale předem zjistit, jestli bude kešování účinné? Vyplatí se vůbec? Jak nastavit parametry kešování, aby bylo co nejúčinnější? Kešování nemělo být spásou pro řešení aktuálních problémů, ale jednou ze strategií, jak se připravit stabilní systém na očekávanou zvýšenou zátěž o Vánocích.<br />
<br />
K dispozici jsme měli zkušenosti a logy za první dva týdny provozu.<br />
<br />
<h4 style="text-align: left;">
Statistika</h4>
<br />
Jednoduchá statistika requestů - počty a distinct počty zásilek pro první dva týdny provozu:<br />
<div>
<br />
<table border="1" cellpadding="0" cellspacing="0" style="background-color: white; border-collapse: collapse; border: none; margin-left: 0.75pt; table-layout: fixed; widows: 1; width: 570px;"><tbody>
<tr style="height: 14.4pt;"><td nowrap="nowrap" style="border: 1pt solid windowtext; height: 14.4pt; padding: 0px 3.5pt; width: 25.219298245614034%;" valign="bottom" width="80"><div style="margin: 0px;">
<span style="font-size: 15px;"><span style="font-family: Calibri;">datum</span></span></div>
</td><td nowrap="nowrap" style="border-color: windowtext; border-style: solid solid solid none; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 17.105263157894736%;" valign="bottom" width="80"><div style="margin: 0px;">
<span style="font-family: Calibri, sans-serif;"><span style="font-size: 14.6667px;">počet</span></span></div>
</td><td nowrap="nowrap" style="border-color: windowtext; border-style: solid solid solid none; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 27.412280701754387%;" valign="bottom" width="123"><div style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>distinct počet</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-color: windowtext; border-style: solid solid solid none; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 30.043859649122805%;" valign="bottom" width="126"><div style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">% distinct (0..1)</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014-04-01</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">291119</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>119738</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,411302594</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-02</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">283813</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>111442</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,392659956</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-03</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">279593</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>104104</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,372341225</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-04</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">260349</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>97965</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,376283373</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-05</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">161026</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>50408</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,313042614</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-06</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">153951</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>48013</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,311871959</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-07</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">282551</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>110197</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,390007468</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-08</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">220211</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>86256</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,391697054</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-09</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">307659</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>109649</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,35639783</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-10</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">313538</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>120067</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,382942418</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-11</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">297510</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>113246</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,380646029</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-12</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">157255</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>54044</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,343671107</span></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2014</span></span></span></span><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">-04-13</span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 48pt;" width="80"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">161753</span></span></span></span></div>
</td><td style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 74.2pt;" width="123"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>58605</b></span></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 75.8pt;" valign="bottom" width="126"><div align="right" style="margin: 0px;">
<span style="color: #212121;"><span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,362311673</span></span></span></span></div>
</td></tr>
</tbody></table>
<br />
<div style="background-color: white; color: #212121; font-family: wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif; font-size: 15px; margin: 0px; widows: 1;">
</div>
Procento unikátních čísel samo o sobě moc neříká, ale počet distinct zásilek za den dává přesnou představu, kolik by cache mohla obsahovat nejvíce záznamů. Horní odhad pro počet záznamů v keši je tak přibližně 120000.<br />
<br />
<h4 style="text-align: left;">
Simulace!</h4>
<br />
Access log obsahující url každého přístupu k zásilkám nám dovoluje chování keše simulovat. Simulace znamená procházení requestů jeden po druhém a počítání, kolik by cache ušetřila.<br />
<br />
Jednoduchá implementace simulátoru keše v Groovy:<br />
<br />
<script src="https://gist.github.com/tomaspinos/9a67454d71c60d853c19.js"></script>
<br />
Simulace pak prochází log a pro každou zásilku zavolá <span style="font-family: Courier New, Courier, monospace;">cache.intercept(parcelNumber, dateTimeRequest)</span>:<br />
<br />
<script src="https://gist.github.com/tomaspinos/ddb93fefb686c5066d4f.js"></script>
<br />
Příklad načítá záznamy access logu z databáze. Nad access logem jsme prováděli více analýz a tato transformace se nám vyplatila.<br />
<br />
Simulace pro různé parametry keše nám dovolí konfigurace porovnat. Následující tabulka zachycuje výsledky simulace nad produkčními daty za první dva týdny běhu. Simulace běžela pro 3151282 requestů.<br />
<div>
<div>
<span style="color: #212121;"><span style="font-family: wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif;"><span style="font-size: 15px;"><br /></span></span></span></div>
<table border="1" cellpadding="0" cellspacing="0" style="background-color: white; border-collapse: collapse; border: none; color: #212121; font-family: wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif; font-size: 15px; margin-left: 0.75pt; table-layout: fixed; width: 606px;"><tbody>
<tr style="height: 14.4pt;"><td nowrap="nowrap" style="border: 1pt solid windowtext; height: 14.4pt; padding: 0px 3.5pt; width: 34.63917525773196%;" valign="bottom" width="197"><div style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">Retence (sekundy)</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-style: solid; border-bottom-width: 1pt; border-left-color: windowtext; border-left-style: none; border-left-width: 1pt; border-right-color: windowtext; border-right-style: solid; border-right-width: 1pt; border-top-color: windowtext; border-top-style: solid; border-top-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 23.505154639175256%;" valign="bottom" width="83"><div style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">Hit count</span></span></span></div>
</td><td nowrap="nowrap" style="border-color: windowtext; border-style: solid solid solid none; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 19.587628865979383%;" valign="bottom" width="95"><div style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">Miss count</span></span></span></div>
</td><td nowrap="nowrap" style="border-color: windowtext; border-style: solid solid solid none; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 22.061855670103093%;" valign="bottom" width="108"><div style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">% hitů (0..1)</span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td nowrap="nowrap" style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 118.4pt;" valign="bottom" width="197"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">1800</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 50.25pt;" valign="bottom" width="83"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">570352</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 57.45pt;" valign="bottom" width="95"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2580930</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 65pt;" valign="bottom" width="108"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,180990467</span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td nowrap="nowrap" style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 118.4pt;" valign="bottom" width="197"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">(hodina) 3600</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 50.25pt;" valign="bottom" width="83"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">769467</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 57.45pt;" valign="bottom" width="95"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2381815</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 65pt;" valign="bottom" width="108"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,244175862</span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td nowrap="nowrap" style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 118.4pt;" valign="bottom" width="197"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>(něco přes hodinu) 4000</b></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 50.25pt;" valign="bottom" width="83"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>843278</b></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 57.45pt;" valign="bottom" width="95"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>2308004</b></span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 65pt;" valign="bottom" width="108"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;"><b>0,267598393</b></span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td nowrap="nowrap" style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 118.4pt;" valign="bottom" width="197"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">7500</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 50.25pt;" valign="bottom" width="83"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">1075121</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 57.45pt;" valign="bottom" width="95"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2076161</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 65pt;" valign="bottom" width="108"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,341169403</span></span></span></div>
</td></tr>
<tr style="height: 14.4pt;"><td nowrap="nowrap" style="border-color: windowtext; border-style: none solid solid; border-width: 1pt; height: 14.4pt; padding: 0px 3.5pt; width: 118.4pt;" valign="bottom" width="197"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">(1 den) 86400</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 50.25pt;" valign="bottom" width="83"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">2069802</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 57.45pt;" valign="bottom" width="95"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">1081480</span></span></span></div>
</td><td nowrap="nowrap" style="border-bottom-color: windowtext; border-bottom-width: 1pt; border-right-color: windowtext; border-right-width: 1pt; border-style: none solid solid none; height: 14.4pt; padding: 0px 3.5pt; width: 65pt;" valign="bottom" width="108"><div align="right" style="margin: 0px;">
<span style="font-family: Calibri,sans-serif; font-size: x-small;"><span style="font-size: 11pt;"><span style="color: black;">0,656812688</span></span></span></div>
</td></tr>
</tbody></table>
<div style="background-color: white; color: #212121; font-family: wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif; font-size: 15px; margin: 0px;">
<span style="font-family: Calibri, sans-serif;"><span style="color: #1f497d;"><br /></span></span></div>
</div>
<div>
<div>
Zjistili jsme, že ideální hodnota pro retenci keše je 4000 sekund. Pro tuto hodnotu by zahitovalo přibližně 27% requestů. Na kontextu aplikace záleží, jestli je retence "něco přes hodinu" přijatelná a jestli je to dobrý výsledek. V našem případě to dobré bylo a vyhodnotili jsme, že kešování má smysl. </div>
<div>
<br /></div>
<div>
Jednoduchá implementace není bez omezení. Nezohlednila například cluster a replikace keší.<br />
<br /></div>
<div>
<h4 style="text-align: left;">
Shrnutí</h4>
<br />
Motivací pro vznik článku byla snaha odhadnout účinnost kešování ještě před jeho použitím. Simulace kešování nad reálnými daty dovoluje ověřit účinnost kešování i tam, kde už nakonfigurované je. Konfigurace kešování nemusí být střelbou od boku, ale promyšleným krokem s přesně odhadnutým očekávaným výsledkem.<br />
<br /></div>
</div>
</div>
</div>
Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-37888860063855520412015-10-31T22:18:00.000+01:002015-11-01T21:13:17.563+01:00Tomcat vládne všem<div dir="ltr" style="text-align: left;" trbidi="on">
Na webu <a href="http://www.profinit.eu/index.php?id=4976">Profinitu </a>a v časopise <a href="http://www.systemonline.cz/clanky/tomcat-vladne-vsem-pri-vyberu-aplikacniho-serveru.htm">IT Systems</a> mi vyšlo starší zamyšlení nad Tomcatem.<br />
<br />
<a href="http://www.profinit.eu/index.php?id=4976">http://www.profinit.eu/index.php?id=4976</a><br />
<br />
<a href="http://www.systemonline.cz/clanky/tomcat-vladne-vsem-pri-vyberu-aplikacniho-serveru.htm">http://www.systemonline.cz/clanky/tomcat-vladne-vsem-pri-vyberu-aplikacniho-serveru.htm</a><br />
<br /></div>
Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-87945239182486045392015-02-28T14:30:00.002+01:002015-11-01T21:12:48.841+01:00Srovnání Java aplikačních serverů<div dir="ltr" style="text-align: left;" trbidi="on">
Na webu <a href="http://www.profinit.eu/o-spolecnosti/myslime-si/srovnani-java-aplikacnich-serveru.html">Profinitu </a>a v časopise <a href="http://www.systemonline.cz/sprava-it/srovnani-java-aplikacnich-serveru.htm">IT Systems</a> mi vyšel půl roku starý článek shrnující diskuzi nad výběrem Java aplikačního serveru.<br />
<br />
<a href="http://www.profinit.eu/o-spolecnosti/myslime-si/srovnani-java-aplikacnich-serveru.html">http://www.profinit.eu/o-spolecnosti/myslime-si/srovnani-java-aplikacnich-serveru.html</a><br />
<br />
<a href="http://www.systemonline.cz/sprava-it/srovnani-java-aplikacnich-serveru.htm">http://www.systemonline.cz/sprava-it/srovnani-java-aplikacnich-serveru.htm</a></div>
Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-51562715939042346732011-03-23T08:32:00.000+01:002011-03-23T08:32:22.896+01:00Proč psát javovské testy v Groovy IIDruhá část blogu o <a href="http://www.aspectworks.com/cs/blog/2011/02/proc-psat-javovske-testy-v-groovy-i/">psaní javovských testů</a> v Groovy přinese méně slov a více kódu.<br />
<br />
<a href="http://www.aspectworks.com/cs/blog/2011/03/proc-psat-javovske-testy-v-groovy-ii/">http://www.aspectworks.com/cs/blog/2011/03/proc-psat-javovske-testy-v-groovy-ii/</a>Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-44972515020151504432011-02-24T18:23:00.000+01:002011-02-24T18:23:30.614+01:00Proč psát javovské testy v Groovy I<span class="Apple-style-span" style="color: #002f38; font-family: 'Arial CE', Arial, Helvetica, sans-serif; font-size: 12px; line-height: 20px;">Chcete začít programovat v Groovy? Máte načtenou dokumentaci a tutoriály, ale stále čekáte, až se objeví příležitost, kde Groovy použít? Chtěli byste Groovy použít na aktuálním projektu, ale kvůli různým omezením to nejde? Začněte Groovy používat už teď pro psaní testů produkčního Java kódu.</span><br />
<span class="Apple-style-span" style="color: #002f38; font-family: 'Arial CE', Arial, Helvetica, sans-serif; font-size: 12px; line-height: 20px;"><a href="http://www.aspectworks.com/cs/blog/2011/02/proc-psat-javovske-testy-v-groovy-i/">http://www.aspectworks.com/cs/blog/2011/02/proc-psat-javovske-testy-v-groovy-i/</a></span>Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-51300160966189063522010-11-13T21:21:00.000+01:002010-11-13T21:21:39.222+01:00Jak pořád nepsat labelyFormulářová pole, validační hlášky, hlavičky seznamů, historie změn entity, … je mnoho míst, kde je potřeba pracovat s popisy polí, která odpovídají vlastnostem nějaké třídy. Článek na <a href="http://www.aspectworks.com/cs/blog/2010/11/jak-porad-nepsat-labely/">blogu AspectWorks</a> představí tip, jak si rutinní činnost zjednodušit.Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-146469282396047862010-09-23T21:02:00.000+02:002010-09-23T21:02:22.311+02:00Spring - MyBatis integrace a něco navícČlánek popisující možnosti integrace Spring a MyBatis frameworků a představující užitečné rozšíření – factory na automatickou registraci Mapper rozhranní implementovaných XML definicí, jsem napsal pro <a href="http://www.aspectworks.com/cs/blog/2010/09/spring-mybatis-integrace-a-neco-navic">blog AspectWorks</a>.Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-560926130378503512010-09-09T20:53:00.001+02:002010-09-09T20:55:04.391+02:00Automatický update knihoven Ehcache a QuartzZdá se, že automatický update se stává populárním i mezi Java knihovnami. Co si myslíte vy o automatické kontrole aktuálnosti knihovny Ehcache a Quartz? Článek na toto téma jsem napsal pro <a href="http://www.aspectworks.com/cs/blog/2010/09/automaticky-update-knihoven-ehcache-a-quartz/">blog AspectWorks</a>.Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-25597020385133284642010-03-31T21:43:00.000+02:002010-03-31T21:43:58.098+02:00Změny v jBPM projektu a jeho budoucnostPřed pár dny zveřejnil JBoss <a href="http://www.jboss.org/feeds/post/open_letter_to_the_jbpm_community">otevřený dopis jBPM komunitě</a>, ve kterém ohlašuje odchod zakladatele jBPM <a href="http://processdevelopments.blogspot.com/">Toma Bayense</a> a dalšího core developera <a href="http://www.jorambarrez.be/blog/">Jorama Barreze</a> z jBPM projektu. K dopisu bylo otevřeno <a href="https://community.jboss.org/thread/149965">diskuzní vlákno</a>, kde je ale dost mrtvo. Tom Bayens pak na svém <a href="http://processdevelopments.blogspot.com/2010/03/alive-and-kicking.html">blogu </a>naznačuje, že se pouští do implementace nové BPM platformy pod Apache licencí. Z komentářů je pak zřejmé, že Tom Bayens jistě neví, jestli bude práce na jBPM 4 pokračovat (přesněji jestli se bude dál rozvíjet aktuální codebase jBPM 4). JBoss slibuje zveřejnit v řádu týdnů novou roadmapu pro jBMP.<br />
<br />
Co si o tom myslíte? Myslíte, že dojde k nějaké formě sloučení jBPM a <a href="http://www.jboss.org/drools/drools-flow.html">Drools Flow</a>? To jsou dva projekty z portfolia JBossu, které si začínají velmi konkurovat.Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com4tag:blogger.com,1999:blog-8395640426039288836.post-42609717707845276222010-03-03T21:00:00.001+01:002010-03-03T21:09:51.635+01:00Anketa: Jakým způsobem řešíte verzování datového modelu?V anketě o používaných způsobech verzovaní datového modelu se sešlo 23 hlasů. Díky za ně. Jasným vítězem jsou "skripty v svn", na druhém místě potom vlastní nástroj pro verzování datového modelu.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAJjS6n2kZjGY7w1oypwTP683w46yPFdYUT6UVx3KI2-GzL0Gjfjtbdj9sAif50tDf-8uqBC-dMsfkyi-d4yw97gT0mYEKVry85TY_5c53lYtknCXV-2EgSAVxfaz-aIBCBrovYs4SQDg_/s1600-h/Anketa+Jak%C3%BDm+zp%C5%AFsobem+%C5%99e%C5%A1%C3%ADte+verzov%C3%A1n%C3%AD+datov%C3%A9ho+modelu.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAJjS6n2kZjGY7w1oypwTP683w46yPFdYUT6UVx3KI2-GzL0Gjfjtbdj9sAif50tDf-8uqBC-dMsfkyi-d4yw97gT0mYEKVry85TY_5c53lYtknCXV-2EgSAVxfaz-aIBCBrovYs4SQDg_/s320/Anketa+Jak%C3%BDm+zp%C5%AFsobem+%C5%99e%C5%A1%C3%ADte+verzov%C3%A1n%C3%AD+datov%C3%A9ho+modelu.PNG" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"></div>Zájemcům o řešení, které může snížit režii spojenou s verzováním a správou modelu, doporučuji svůj přehledový článek o nástroji <a href="http://tom2ee-cs.blogspot.com/2010/01/verzovani-datoveho-modelu-liquibase.html">LiquiBase</a>.Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-31733534043983251632010-02-20T22:47:00.000+01:002010-02-20T22:47:32.440+01:00Unit testy nad in-memory databázíV poslední době jsem několikrát narazil na otázku testování dao tříd a volby databáze, nad kterou testy běží. Překvapilo mě, jak kontroverzní otázka to je a jak vyhraněné názory na ni existují. Testujete dao třídy? Píšete unit testy pro cílovou databázi nebo používáte nějakou in-memory variantu jako HSQL? Mají testy s jinou než cílovou databází smysl? Na tyto a související otázky se pokusí odpovědět tento článek.<br />
<br />
<b>Testování dao tříd</b><br />
<br />
<div>Předně bych chtěl říct, že rozhodně jsem zastáncem psaní testů dao tříd. Ať už na projektu používáte ORM, přímý přístup přes JDBC nebo objektovou databázi, dao třídy obsahují množství kódu, jehož funkčnost je třeba ověřit. Správnost mapování, skládání dotazů, zpracování result setů, řešení krajních stavů nebo třeba odolnost vůči sql injection mohou být faktory, na které se typický test zaměří. Možnost použití jiné než cílové databázi (tou myslím databázi - Oracle, MySql, ... - která bude použitá v produkčním prostředí) zásadně závisí na tom, jestli se v kódu dao tříd vyskytuje konkrétní sql dialekt. Pokud se dotazujeme přímo přes JDBC a používáme pokročilé vlastnosti databáze, těžko můžeme dao třídy nechat běžet nad něčím jiným. Jaké jsou tedy důvody, které mohou někoho vést k psaní unit testů nad in-memory databází? </div><br />
<div><b>Proč vůbec in-memory databázi používat?</b></div><br />
Někdy mohou být zásadní <i>náklady na vytvoření a správu testovacího prostředí</i>. U běžně používaných databází (MySql, Oracle, PostgreSql, ...) většinou nepředstavuje další udržovaná instance velký problém. Existují ale výjimky. Setkal jsem se například s prostředím, kdy byl v produkci použitý Informix a kde vytvoření "další" instance nebylo snadné. Požadavek na novou instanci neznamenal konečnou, ale byl dost nákladný na to, aby ospravedlnil hledání jiných možností.<br />
<br />
<div><div>Dalším důvodem jsou <i>(ne)závislosti</i>, které unit testy potřebují ke svému běhu. V některých prostředích je výhodnější, když unit testy potřebují ke svému běhu jenom svůj a testovaný kód, nikoliv závislosti na dalších systémech včetně databáze. S tím úzce souvisí i opakovatelnost testů, resp. opakovatelnost očekávaného výsledku. Testy nad cílovou databází mohou být citlivější na změny a chyby v testovacích datech, pokud už data před testem obsahují.</div><br />
<div>Často diskutovaným důvodem je také <i>rychlost spouštění</i> testů. Zastánci testů nad in-memory databází někdy vyzdvihují jejich rychlost, kdy testy nad nimi běží údajně výrazně rychleji než nad cílovou databází. Já tento argument neuznávám. V případě in-memory databáze musíme vedle běhu samotného testu do výsledku započítat určitě vytvoření datového modelu, případně inicializaci dat a to jsou akce, které mohou něco stát.<br />
<br />
</div><div><b>Unit nebo integrační?</b></div><br />
Jsou testy dao tříd nad databází unit testy nebo spíš testy integrační? Odpověď na tuto otázku nemusí ležet v kódu testů, ale v náhledu na ně. Pokud na test nad in-memory databází nahlížíme jako na unit test, můžeme in-memory databázi chápat jako stub, který prostě nahrazuje jednu závislost a dovoluje testu běžet v "izolaci". To je chápání, které je mi blízké. Pokud ale takový přístup nazveme integračním testem, nabízí se otázka <i>jakou má takový test vypovídající hodnotu</i>? O funkčnosti dao třídy toho může test hovořit hodně, ale o integraci s produkční databází velmi málo. Proto testy dao tříd nad in-memory databází za integrační nepovažuji.</div><br />
<b>Závěr</b><br />
<br />
<div>Jsem zastáncem testů nad in-memory databází tam, kde to má smysl. Dobrými důvody jsou pro mě náklady na správu testovacího prostředí a nezávislost unit testů na dalších systémech. </div><br />
Testujete na svých projektech dao třídy? Jakou databázi k testům používáte? Cítíte rozpor mezi použitím cílové databáze něbo nějaké in-memory varianty? Podělte se o své názory a zkušenosti v diskuzi pod článkem. Pokud přemýšlíte o tom, jak vytvořit a spravovat datový model pro testy, můžete se podívat na můj článek o nástroji <a href="http://tom2ee-cs.blogspot.com/2010/01/verzovani-datoveho-modelu-liquibase.html" id="ct31" title="LiquiBase">LiquiBase</a>. Pokud řešíte inicializaci dat pro testy, můžete některé odpovědi nalézt v článku o <a href="http://tom2ee-cs.blogspot.com/2009/11/inicializace-databazovych-dat.html" id="v837" title="inicializaci databázových dat">inicializaci databázových dat</a>.Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com4tag:blogger.com,1999:blog-8395640426039288836.post-17025273549902117522010-01-31T22:37:00.001+01:002010-01-31T22:39:57.379+01:00Verzování datového modelu a LiquiBase<div>Přidávání tabulek, sloupců, integritních omezení, přejmenovávání a štěpení tabulek, konfigurace vývojového, testovacího a produkčního prostředí... Je mnoho požadavků, které mají vliv na podobu datového modelu. V tomto blogu zkusím popsat běžné činnosti týkající se správy <i>relačního</i> datového modelu a stručně představit nástroj <a href="http://www.liquibase.org/" id="rzek" title="LiquiBase">LiquiBase</a>. Článek je volným pokračováním předchozího příspěvku <a href="http://tom2ee-cs.blogspot.com/2009/11/inicializace-databazovych-dat.html" id="htlz" title="Inicializace databázových dat prostředky Javy">Inicializace databázových dat prostředky Javy</a>.</div><div><br />
</div><div>Různá prostředí kladou na správu datového modelu různé nároky - přizpůsobitelnost, nezávislost na konkrétní databázi, použitelnost pro testy nebo verzování. Krátce jsem je popsal v <a href="http://tom2ee-cs.blogspot.com/2009/11/inicializace-databazovych-dat.html" id="y2l:" title="předchozím příspěvku">předchozím příspěvku</a>. Jaké jsou běžné činnosti, které se správou datového modelu souvisí? Samozřejmě potřebujeme evidovat jakékoliv změny formou nějakého changelogu. V jistou chvíli je nutné změny z changelogu aplikovat na nějaký existující model - provést jeho aktualizaci. Někdy může být nutné naopak změny odstranit - provést jejich rollback. Při kvapném rozvoji datového modelu přijde vhod možnost porovnat dva modely. Při zavádění verzování je užitečná možnost reverzního vytvoření changelogu z existující databáze. A v neposlední řadě je dobré mít změny dobře zdokumentované.</div><div><br />
</div><div>Právě na tyto činnosti se zaměřili tvůrci nástroje <a href="http://www.liquibase.org/" id="tzw_" style="color: #551a8b;" title="LiquiBase">LiquiBase</a>. Než se k němu ale dostanu, popíšu napřed alternativní možnosti. Změny datového modelu se tradičně dají organizovat pomocí sql skriptů verzovaných nějakým scm nástrojem (jako je třeba Subversion). Pro každou změnu datového modelu se připraví samostatný sql skript, který se prostě vloží do scm systému. Jeho revize potom dovolí vybrat správnou množinu skriptů, které někdo ručně spustí. Některé firmy nedají dopustit na nástroje, které si vyvinuly svépomocí a které dovolí správu modelu alespoň částečně automatizovat. I správa datového modelu je doménou, na které se dá vydělávat a kterou řeší komerční nástroje.</div><div><br />
</div><div><b><span class="Apple-style-span" style="font-size: x-large;">LiquiBase</span></b></div><div><br />
</div><div>Na nových projektech se snažím používat <a href="http://www.liquibase.org/" id="akwc" title="LiquiBase">LiquiBase</a>. Je to nástroj, který mě zaujal svým úzkým zaměřením (neřeší nic, co by se správou datového modelu nesouviselo), snadnou přístupností (začlenění do vývojového procesu je jednoduché a přímočaré) a perfektní dokumentací. Nebudu suplovat <a href="http://www.liquibase.org/manual/home" id="lvty" title="dokumentaci">dokumentaci</a> LiquiBase a <a href="http://www.liquibase.org/quickstart" id="tjao" title="tutoriály">tutoriály</a> přístupné z jeho webu, chci nástroj jenom stručně představit.</div><div><br />
</div><div><b>Changelog</b></div><div><br />
</div><div>Klíčový konceptem je changelog - xml soubor obsahující změny, zvané changesety. Změnou je třeba založení tabulky, přidání sloupce nebo vytvoření indexu. Každá změna je identifikovaná dvojicí (id,author). Changelog představuje historii změn datového modelu, ale neříká nic o tom, jak vypadají konkrétní instance modelu. Proto LiquiBase potřebuje, aby každý spravovaný mode obsahoval tabulku DatabaseChangeLog(id,author,filename,dateexecuted,...), která eviduje changesety aplikované na model.</div><div><br />
</div><div>Příklad changelogu:</div><div><br />
<div><div><databaseChangeLog</div><div> xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.6"</div><div> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"</div><div> xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.6</div><div> http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.6.xsd"></div><div></div><div> <changeSet id="1" author="bob"></div><div> <createTable tableName="department"></div><div> <column name="id" type="int"></div><div> <constraints primaryKey="true" nullable="false"/></div><div> </column></div><div> <column name="name" type="varchar(50)"></div><div> <constraints nullable="false"/></div><div> </column></div><div> <column name="active" type="boolean" defaultValue="1"/></div><div> </createTable></div><div> </changeSet></div><div></div><div></databaseChangeLog></div><div><br />
</div>Changelog neobsahuje identifikaci databáze nebo chcete-li dialektu. Ten se zadává až při spouštění update a rollback příkazů. Pokud nemáme příliš specifické požadavky, můžeme tedy jeden changelog použít např. pro testovací MySQL nebo produkční Oracle. Vedle předdefinovaných příkazů mohou changesety obsahovat i sql skripty nebo odkazy na ně. </div><div><div><br />
</div><div>Podporované jsou tyto databáze:</div><div><ul><li>MySQL, PostgreSQL, Oracle, MS-SQL, Sybase Enterprise, Sybase Anywhere, DB2, Apache Derby, HSQL, H2, InterSystems Caché, Firebird, MaxDB / SAPDB, SQLite.</li>
</ul></div><div>A tyto předdefinované operace (refactorings):</div><div><ul><li>Structural Refactorings - Add Column, Rename Column, Modify Column, Drop Column, Alter Sequence, Create Table, Rename Table, Drop Table, Create View, Rename View, Drop View, Merge Columns, Create Stored Procedure</li>
<li>Data Quality Refactorings - Add Lookup Table, Add Not-Null Constraint, Remove Not-Null Constraint, Add Unique Constraint, Drop Unique Constraint, Create Sequence, Drop Sequence, Add Auto-Increment, Add Default Value, Drop Default Value</li>
<li>Referential Integrity Refactorings - Add Foreign Key Constraint, Drop Foreign Key Constraint, Add Primary Key Constraint, Drop Primary Key Constraint</li>
<li>Architectural Refactorings - Create Index, Drop Index</li>
<li>a další</li>
</ul></div><div>LiquiBase je možné používat i na nepodporované databázi. Pokud pro ni nefungují některé z předdefinovaných operací, může changeset jednoduše obsahovat specifický sql kód.</div><div><br />
</div><div>Changelog můžeme udržovat ručně, vytvořit reverzně z existujícího modelu nebo použít podporu v IDE. Dobře funguje plugin pro IntelliJ IDEA a deklarovaná je i podpora pro Eclipse. Plugin pro Eclipse se mi ale nepodařilo zprovoznit.</div><div><br />
</div></div><div><b>Update a rollback</b></div><div><br />
</div><div>Základní příkazy pro aplikaci, resp. mazání změn, jsou update a rollback. Spouští se z příkazové řádky, z mavenu, z antu nebo třeba z IDE. Příkaz update aplikuje všechny nové changesety na existující model. To, jestli je daný changeset nový nebo již aplikovaný, pozná podle záznamů v tabulce DatabaseChangeLog. Příkaz rollback bere jako vstupní parametr identifikaci changesetu a všechny novější změny z modelu odstraní (pokud je to možné).</div><div><br />
</div><div>Dalšími užitečnými funkcemi jsou například</div><div><ul><li>Diff - porovnání dvou databází a vytvoření changelogu.</li>
<li>DBDoc - vytvoří html manuál s popisem všech změn datového modelu.</li>
<li>nebo SQL výstup, který changlog převede na SQL skript.</li>
</ul></div><div><b>Alternativy</b></div><div><br />
</div><div><a href="http://migrate4j.sourceforge.net/" id="wrg3" title="migrate4j">migrate4j</a> nebo <a href="http://code.google.com/p/dbmigrate/" id="iu_6" title="dbmigrate">dbmigrate</a> jsou nástroje s podobným zaměřením jako LiquiBase. Vývojáři Ruby asi budou znát <a href="http://guides.rubyonrails.org/migrations.html" id="s.10" title="Ruby Migrations">Ruby Migrations</a>. </div><br />
<b>Shrnutí</b><br />
<br />
Článek chtěl popsat požadavky na evidenci změn datového modelu a ukázat, že s LiquiBase lze s malým úsilím změny organizovat lépe než jako "skripty v svn". Jak změny datového modelu organizujete vy? Máte bližší zkušenosti s nástrojem LiquiBase nebo jiným podobným? Podělte se o své názory v diskuzi pod článkem a zapojte se do hlasování.<br />
<br />
</div>Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com3tag:blogger.com,1999:blog-8395640426039288836.post-65232174991049370612009-11-04T21:33:00.001+01:002009-11-04T21:38:49.520+01:00Inicializace databázových dat prostředky Javy<div>Končí inicializace databáze vytvořením tabulek a integritních omezení? Jak ji naplnit nezbytnými daty? Téměř každá aplikace potřebuje k práci nějaké ty číselníky, role, přístupová práva, uživatele a další data. V tomto blogu zkusím popsat, co může inicializace dat v <i>relační</i> databázi znamenat a jak pro ni výhodně použít prostředky Javy.<br />
</div><div><br />
</div><div>Článek je psán s ohledem na běžný projekt, který pracuje nad relačními daty (ne nad objektovou databází, ne nad geografickými daty a pod.), kde objem inicializačních dat není extrémně velký (nebudu popisovat inicializaci dat v obrovském datovém centru), kde v databázi není implementovaná žádná logika a kde si persistenci řeší aplikace (ORM, sql, ...).<br />
</div><div><br />
</div><div>Budu předpokládat, že už máme založenou databázi a v ní vytvořený datový model. Vytvoření datového modelu může být sama o sobě zajímavá otázka, hlavně s ohledem na jeho vývoj, aktualizace a udržování změn. Dnes mě ale bude zajímat pouze inicializace dat. Co od ní můžeme požadovat?<br />
</div><div><ul><li><b>Přizpůsobitelnost</b> - Můžeme si představit aplikaci, která je instalovaná u několika zákazníků, typicky mnoha. O datech pak můžeme mluvit jako o obecných a zákaznicky specifických. Jak inicializovat zákaznicky specifická data? Je dobré mít inicializaci dat rozdělenou na inicializaci těch obecných a s ní konzistentní způsob na inicializaci zákaznických rozšíření.</li>
<li><b>Nezávislost na konkrétní databázi</b> - Jsou aplikace, typicky produktová řešení, které je možné provozovat na různých databázích (ve smyslu Oracle, MySql, ...). Nemusí to být vždy možné, ale určitě by bylo výhodné mít společný způsob inicializace dat použitelný pro každou z nich.</li>
<li><b>Použitelnost pro testy</b> - Unit testy vyžadující databázi také potřebují mít inicializovaná data. Typicky vyžadují podmnožinu z dat produkčních (číselníky...) a nějaká data specifická pro testy. Navíc, různé testy mohou potřebovat různá testovací data. Různé testy si tak můžeme představit jako různé zákazníky a testovací data rozdělit na obecná a specifická pro konkrétní test. Bylo by hezké mít jeden způsob, jak přistoupit k inicializaci produkčních i testovacích dat.</li>
<li><b>Testovatelnost</b> - Také v datech mohou být chyby. Bylo by dobré mít způsob, jak správnost inicializace dat otestovat.</li>
<li><b>Verzování a správa změn</b> - Tak, jak se vyvíjí aplikace, mohou se vyvíjet i data, se kterými pracuje. Přirozeným požadavkem se zdá být možnost verzování změn inicializačních dat.</li>
<li><b>Přehlednost a udržovatelnost</b> - Stejně jako zdrojové kódy nebo jiné artefakty, i data a jejich inicializace chceme mít dobře organizovaná a inicializační proceduru přiměřeně jednoduchou a dlouhodobě udržovatelnou. </li>
<li>A další... Samozřejmě existuje spousta dalších kritérií, která můžeme po inicializaci požadovat. Snažil jsem se shrnout ta, která jsou zajímavá pro další text.</li>
</ul><br />
</div><div>Jak se inicializace dat řeší? Shrnu ty způsoby, které jsem si na projektech vyzkoušel a které mi přijdou jako nejčastěji používané.<br />
</div><div><br />
</div><b>Dump celé databáze</b><br />
<div><br />
</div><div>Dost často se databáze průběžným vývojem přivede do stavu, který odpovídá požadavkům na produkční prostředí, a pak se pořídí její dump. Ten se v cílovém prostředí celý naimportuje. Problémem může snaha o podporu více databází - jak ve smyslu Oracle, MySql, tak podle zákazníků. Různé varianty vyžadují existenci různých dumpů, tedy i databází, ze kterých dump vytvoříme. Naopak výhodou je rychlost importu dat. Otázkou je, nakolik je pro nás rychlost inicializace důležitá. Dumpy se často kombinují s sql skripty, které databázi po importu dumpu aktualizují. Přehlednost, udržovatelnost, použitelnost pro testy, verzování, ... také vidím jako obtížné.<br />
</div><div><br />
<b>Sql skripty</b><br />
</div><div><br />
</div><div>Data můžeme do databáze přirozeně vložit sql skripty. Typicky ale nestačí "dlouhý seznam insertů", skripty mohou vypadat o dost složitěji. U složitěji provázaných dat (na úrovni doménového modelu bych řekl "grafů objektů") mohou skripty obsahovat hodně práce s proměnnými, cykly, někdy i funkce nebo celé balíčky funkcí. Zajímá mě inicializace dat na projektu, kde si perzistence řeší aplikační vrstva a taková spousta sql kódu jenom pro inicializaci dat je tedy nechtěná. Přehlednost a udržovatelnost sql skriptů také stojí hodně úsilí.<br />
</div><div><br />
<b>Speciální nástroj</b> na správu a migraci dat<br />
</div><div><br />
</div><div>Existují nástroje, které inicializaci a obecně správu a migraci dat komplexně řeší. Příkladem může být třeba <a href="http://www.embarcadero.com/products/change-manager" id="zujx" title="Embarcadero Change Manager">Embarcadero Change Manager</a>. Vedle ceny může být u takových nástrojů limitující i způsob, jak práci s nimi zaintegrovat s našimi dalšími nástroji (IDE, maven, ...) a vývojovým procesem. Příklad nebo hodnocení komerčních nástrojů je ale mimo rozsah tohoto článku.<br />
</div><div><br />
<b>Java + ORM</b><br />
</div><div></div><div><br />
</div><div>Na několik posledních projektech jsme inicializaci dat prováděli z prostředí Javy. Máme-li doménový model, který data plně popisuje, můžeme libovolná data instancemi tříd z doménového modelu reprezentovat (od toho doménový model máme, že...). Inicializace dat se tak převedla na inicializaci doménového modelu. A to už je otázka pro Javu, kde to umím řešit přehledně a dlouhodobě udržovatelně a to i pro velmi složité "grafy objektů". Dobrý nápad je zestručnit výřečný Java kód pro inicializace použitím nějakého dynamického jazyka, výhodné může být třeba Groovy.<br />
</div><div><br />
</div><div>Pro řešení persistence na projektech používáme nejčastěji Hibernate, máme tedy pro doménový model vytvořené mapování. Zapsat instance tříd z doménového modelu do databáze je potom triviální úkol. Řeším inicializaci a chci tedy řešit hlavně "data", algoritmus zápisu mi kód nebude znepřehledňovat.<br />
</div><div><br />
</div><div>Podle některých učebnic by asi inicializace dat neměla záviset na kódu, který data posléze bude využívat, ale v závislosti na doménovém modelu a mapování nic špatného nevidím. Pokud na projektu není použité ORM a persistenci řeší DAO třídy třeba s využitím JDBC nebo jakkoliv jinak, ani závislost na DAO vrstvě nemusí být problém.<br />
</div><div><br />
</div><div>Použití ORM je výhodné i v případech, kdy se vytváří nějaké produktové řešení a je nutnost podporovat více databází. Hibernate se svými dialekty mi proto velmi vyjde vstříc.<br />
</div><div><br />
</div><div>Pro některé typy dat může být výhodné reprezentovat jejich inicializační hodnoty mimo Java kód. Řešíme tak třeba číselníky. Máme je reprezentované jednoduchým XML formátem a v Javě napsaný jeho parser a konvertor do doménového modelu. XML je výhodné i v tom, že obsah XML souborů může generovat nebo kompilovat z různých zdrojů automaticky.<br />
</div><div><br />
</div><div><b>Shrnutí</b><br />
</div><div><br />
</div><div>Popsané způsoby a jejich vztah k požadavkům na inicializaci dat si můžeme shrnout v následující tabulce.<br />
</div><div><br />
</div><div><div><table border="1" bordercolor="#000000" cellpadding="3" cellspacing="0" class="" id="wfnz"><tbody>
<tr><td width="14.285714285714286%"><br />
</td><td style="text-align: center;" width="14.285714285714286%">Přehlednost a udržovatelnost<br />
</td><td style="text-align: center;" width="14.285714285714286%">Přizp.<br />
</td><td style="text-align: center;" width="14.285714285714286%">Nezávislost na konkrétní db<br />
</td><td style="text-align: center;" width="14.285714285714286%">Použitelnost pro testy<br />
</td><td style="text-align: center;" width="14.285714285714286%">Testovatelnost<br />
</td><td style="text-align: center;" width="14.285714285714286%">Verzování a správa změn<br />
</td></tr>
<tr><td width="14.285714285714286%">Dump<br />
</td><td style="text-align: center;" width="14.285714285714286%">subjektivní, <br />
imho ne<br />
</td><td style="text-align: center;" width="14.285714285714286%">obtížně<br />
</td><td style="text-align: center;" width="14.285714285714286%">různé dumpy pro různé db<br />
</td><td style="text-align: center;" width="14.285714285714286%">obtížně<br />
</td><td style="text-align: center;" width="14.285714285714286%">ano<br />
</td><td style="text-align: center;" width="14.285714285714286%">obtížně<br />
</td></tr>
<tr><td width="14.285714285714286%">Sql<br />
</td><td style="text-align: center;" width="14.285714285714286%">subjektivní, <br />
imho ne<br />
</td><td style="text-align: center;" width="14.285714285714286%">obtížně<br />
</td><td style="text-align: center;" width="14.285714285714286%">různé skripty pro různé db<br />
</td><td style="text-align: center;" width="14.285714285714286%">obtížně<br />
</td><td style="text-align: center;" width="14.285714285714286%">ano<br />
</td><td style="text-align: center;" width="14.285714285714286%">ano<br />
</td></tr>
<tr><td width="14.285714285714286%">Java + ORM mapování<br />
</td><td style="text-align: center;" width="14.285714285714286%">subjektivní, <br />
imho ano<br />
</td><td style="text-align: center;" width="14.285714285714286%">ano<br />
</td><td style="text-align: center;" width="14.285714285714286%">ano<br />
</td><td style="text-align: center;" width="14.285714285714286%">ano<br />
</td><td style="text-align: center;" width="14.285714285714286%">ano<br />
</td><td style="text-align: center;" width="14.285714285714286%">ano<br />
</td></tr>
</tbody></table></div><br />
</div><div>Tabulka samozřejmě přináší značně subjektivní zhodnocení, ale ukazuje, proč dávám přednost variantě s Javou a mapováním.<br />
</div><div><br />
</div><div>Článek neměl ambici encyklopedicky popsat všechny možné způsoby inicializace. Chtěl jsem shrnout ty přístupy, se kterými jsem se setkal a vysvětlit, proč mi přijde výhodné provádět inicializaci prostředky Javy. Jako každé řešení to má své výhody i nevýhody. Myslíte, že je inicializace dat prostředky Javy dobrý nápad? Používáte podobný postup taky? Podělte se o své názory a nápady v diskuzi pod článkem.<br />
</div><div><div><br />
</div></div><div>V příštím blogu se podívám na to, jak přistoupit k založení datového modelu a jak zvládnout jeho vývoj a aktualizace včetně aktualizací dat. Představím také zajímavý nástroj LiquiBase.<br />
</div><div><br />
</div>Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com5tag:blogger.com,1999:blog-8395640426039288836.post-5826009856866764412009-10-26T19:38:00.002+01:002009-10-26T19:45:06.205+01:00Hledání země nezeměKódy zemí, měn, bank, názvy měst a vesnic, poštovní směrovací čísla… Většina aplikací pracuje s nějakými číselníky. Kde ale vzít jejich hodnoty a nekrást? Pro firemní blog jsem napsal článek o několika zdrojích dat, které se mi osvědčily na nedávném projektu.<div><br /><a href="http://www.aspectworks.com/cs/blog/2009/10/hledani-zeme-nezeme/">http://www.aspectworks.com/cs/blog/2009/10/hledani-zeme-nezeme</a><br /></div>Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com0tag:blogger.com,1999:blog-8395640426039288836.post-48792059859917108582009-09-30T21:56:00.016+02:002009-10-02T16:51:56.481+02:00Práce s číselníkovými hodnotami<div style="text-align: justify;">V tomto příspěvku s lehce nudným názvem bych se rád pokusil o svěží zamyšlení nad prací s číselníkovými hodnotami. Konkrétně mě bude zajímat porovnávání číselníkových hodnot a rozhodování podle číselníkových hodnot v obchodní logice. Mám na mysli otázky typu „Jak zpracovat adresu typu 'sídlo firmy'?“ nebo například „Jak vypočítat provizi podle schématu 'fixní procentní podíl'?“ Je to další profláknuté a docela jednoduché téma a na většině projektů asi není nutnost „zamýšlet se“ nad ním. Zároveň ale platí, že na většině projektů, u kterých jsem byl, by se porovnávání číselníkových dalo řešilo o dost přehledněji a čitelněji, což mě nakonec po troše váhání vedlo k napsání tohoto příspěvku.</div><br /><span style="font-weight: bold;">Co je číselník</span><br /><br />Číselníkovou položkou myslím třídu,<br /><ul><li>která je perzistentní</li><li>a má minimálně atributy reprezentující kód, se kterým může pracovat obchodní logika,</li><li>a text, případně odkaz na jazykový text (tzn. českou, anglickou, … verzi), který se zobrazuje uživateli. </li></ul>Mějme takovou číselníkovou položku reprezentovanou třídou AbstractCatalogEntity.<pre class="Java" name="code">public abstract class AbstractCatalogEntity {<br />private Long id;<br />private String code;<br />private String label;<br />...<br />}<br /></pre>Tento text neřeší:<br /><ul><li>Jestli je o hodnotách, které chci porovnávat, lepší mluvit jako o „kódech“ nebo třeba „klíčích“.</li><li>Jestli má být odkaz na číselník realizován přes „id“ nebo „code“.</li><li>Jestli mají mít číselníkové položky platnost a jestli ji má porovnávání hodnot zohledňovat.</li><li>Další související otázky.</li></ul>Opravdu bude zajímat pouze vyhodnocování stringového kódu, tedy atributu code.<br /><br /><span style="font-weight: bold;">Příklad</span><br /><br />Budu používat jednoduchý příklad s číselníkem států, číselníkem typů adres, adresou a vazbou na adresu. <pre class="Java" name="code">public class Country extends AbstractCatalogEntity { }<br />public class AddressType extends AbstractCatalogEntity { }<br /><br />public class Address {<br />private Long id;<br />private Country country;<br />private String city;<br />private String street;<br />private String postcode;<br />...<br />}<br />public class AddressLink {<br />private Long id;<br />private AddressType addressType;<br />private Address address;<br />private String note;<br />...<br />}<br /></pre>Adresa má vlastnost country ukazující do číselníku zemí. Číselník zemí je příklad toho, kdy v obchodní logice potřebujeme vyhodnocovat pouze jednu nebo několik položek číselníku a ostatní nás víceméně nezajímají. Příklady budou zjišťovat, jestli je daná country rovna České republice (tedy jestli je daná adresa v Česku).<br />Vazba na adresu má vlastnost addressType určující typ adresy – tzn. jestli je daná adresa adresou sídla firmy, adresou fakturační nebo třeba adresou pobočky. Odpovídající číselník typů adres je příkladem toho, kdy se budou v obchodní logice vyhodnocovat všechny jeho položky. Podle typu adresy se může zpracování adresy lišit.<br /><br /><span style="font-weight: bold;">Stringové literály bez konstant</span><br /><br />Přímočarým řešením je porovnávat číselníkové kódy na očekávanou hodnotu všude tam, kde je to třeba.<pre style="font-family:'courier new';">Assert.assertEquals(“CZ”, address.getCountry().getCode());<br /></pre>Java kód plný stringových literálů ale není můj ideál, proto od tohoto způsobu radši rychle pryč. Každé řešení je ale dobré nebo špatné vždy v určitém kontextu, proto jsem pro úplnost zmínil i tuto variantu.<br /><br /><span style="font-weight: bold;">Stringové konstanty v doménové třídě</span><br /><br />Dalším krůčkem k lepšímu řešení může být zavedení konstant v doménových třídách reprezentujících číselníky:<pre class="Java" name="code">public class Country extends AbstractCatalogEntity {<br />public static final String CZECH_REPUBLIC = "CZ";<br />}<br /></pre>Samotný kód tedy máme definovaný na jednom místě. Jeho případné změny jsou bezbolestné, snadno můžeme vyhledávat všechna jeho použití.<pre style="font-family:'courier new';">Assert.assertEquals(Country.CZ, address.getCountry().getCode());<br /></pre>Stále ale porovnáváme řetězce a to nemusí být vždy vhodné.<br /><br /><span style="font-weight: bold;">Má smysl porovnávat kódy?</span><br /><br />Má tedy porovnávání kódu vůbec smysl? No, číselníkové kódy máme reprezentované jako řetězce, takže někde se ty řetězce porovnávat musí. Otázka byla míněna jinak – má smysl porovnávat kódy v obchodní logice? Domnívám se, že ne a proto se dostáváme k dalšímu řešení.<br /><br /><span style="font-weight: bold;">Doménová třída s isX metodami</span><br /><br />V obchodní logice je porovnávání číselníkových kódů typicky velmi „malým“ problémem a jeho realizace by teda mohla být podobně úsporná a čitelná. Typicky tam nejsou důležité otázky typu „jestli je daný kód rovný jinému kódu“, ale spíš jestli daný kód nebo instance doménové třídy splňuje vlastnost reprezentovanou daným číselníkovým kódem. Můžeme si proto představit následující drobné rozšíření doménových tříd.<pre class="Java" name="code">public class AddressLink {<br />public static final String RESIDENCE = "RESIDENCE";<br />public static final String INVOICE = "INVOICE";<br />public static final String BRANCH = "BRANCH";<br />….<br />public boolean isResidence() {<br />return addressType != null && RESIDENCE.equals(addressType.getCode());<br />}<br />public boolean isInvoice() {<br />return addressType != null && INVOICE.equals(addressType.getCode());<br />}<br />}<br /></pre>IsX metody rozhodnou, jestli daná doménová třída splňuje vlastnost reprezentovanou číselníkovým kódem. Na pozadí samozřejmě musí dojít k porovnání číselníkových kódů, obchodní logiku tím ale nemusíme zatěžovat.<pre style="font-family:'courier new';">Assert.assertTrue(addressLink.isResidence());<br /></pre>Alternativně můžeme tyto konstanty a metody umístit do třídy AddressType. Záleží víceméně na osobním vkusu.<br /><br /><span style="font-weight: bold;">Oddělení definice číselníku od jeho hodnot</span><br /><br />Jsou případy, kdy je nutné oddělit definici číselníků od jejich hodnot. Někdy není žádoucí aby např. třída AddressType znala svoje možné hodnoty, to znamená obsahovala konstanty s možnými hodnotami číselníkových kódů. Příkladem mohou být projekty, kdy jsou číselníkové a doménové třídy součástí nějakého frameworku nebo komponenty a mohou mít na různých zákaznických projektech různé hodnoty. Tomuto požadavku se bude věnovat zbytek článku.<br /><br /><span style="font-weight: bold;">Stringové konstanty v k tomu určeném rozhraní</span><br /><br />Pokud nechceme, aby doménové třídy znaly číselníkové kódy, můžeme kódy umístit třeba do k tomu určenému rozhraní.<pre class="Java" name="code">public interface IAddressTypeCodes {<br />String RESIDENCE = "RESIDENCE";<br />String INVOICE = "INVOICE";<br />String BRANCH = "BRANCH";<br />}<br /></pre>Po předchozí argumentaci je jasné, že nejsem nakloněn porovnávání kódů přímo v obchodní logice. Toto řešení ale uvádím jako nejčastěji používané (z toho, co jsem měl možnost vidět) a jako řešení, které jsem dříve automaticky používal taky. 'I' je v názvu rozhraní jenom proto, abych ho odlišil od dalších příkladů.<br /><br /><span style="font-weight: bold;">Rozhraní?</span><br /><br />Někdo může namítnout, že rozhraní reprezentuje kontrakt mezi jeho implementací a klientským kódem a že proto není vhodné používat jej jako kontejner na stringové konstanty. A bude mít pravdu. Jsou číselníkové kódu součástí takového kontraktu? Ano i ne. Tento faktor chápu jako dost subjektivní, ale kloním se k tomu, že do rozhraní číselníkové kódy nepatří. Otázka typu „A proč to máš v rozhraní?“ vlastně vedla ke vzniku tohoto příspěvku.<br /><br /><span style="font-weight: bold;">Enum s match metodou</span><br /><br />Od Javy 1.5 je přirozeným prostředkem k evidování konstant typ enum. Pouhé vyjmenování číselníkových kódů ale mnoho neřeší. Je vhodné vyžadovat, aby obchodní logika volala metody typu Country.getCode nebo AddressType.getCode? Jinými slovy, je vhodné, aby obchodní logika „znala“ vnitřní strukturu číselníků? Rozhodně to není nutné. Enum může vedle číselníkových hodnot obsahovat i prostředky k jejich porovnání. Takovým prostředkem může být metoda match:<pre class="Java" name="code">public enum ECountryCodes {<br />CZ;<br /><br />public boolean match(Country country) {<br />return country != null && name().equals(country);<br />}<br />}<br /></pre>Místo podmínek typu<pre style="font-family:'courier new';">Assert.assertTrue(ECountryCodes.CZ.equals(address.getCountry().getCode()));<br /></pre>tak můžeme dostat výrazně přehlednější kód<pre style="font-family:'courier new';">Assert.assertTrue(ECountryCodes.CZ.match(address.getCountry()));<br /></pre><span style="font-weight: bold;"><div><span class="Apple-style-span" style="font-weight: normal;">Opět, 'E' je v názvu enumu jenom proto, abych ho odlišil od ostatních příkladů.</span></div><div><span class="Apple-style-span" style="font-weight: normal;"><br /></span></div>Enum s isX metodami</span><br /><br />Abychom se úplně vyhnuli porovnávání a mohli vyhodnocovat jenom to, jestli daná instance doménové třídy splňuje vlastnost reprezentovanou číselníkovým kódem, můžeme opět použít isX metody.<pre class="Java" name="code">public enum EAddressTypeCodes {<br />RESIDENCE, INVOICE, BRANCH;<br /><br />public static boolean isResidence(AddressType addressType) {<br />return RESIDENCE.match(addressType);<br />}<br />public static boolean isInvoice(AddressType addressType) {<br />return INVOICE.match(addressType);<br />}<br />public static boolean isBranch(AddressType addressType) {<br />return BRANCH.match(addressType);<br />}<br />public boolean match(AddressType addressType) {<br />return addressType != null && name().equals(addressType.getCode());<br />}<br />}<br /></pre>Vyhodnocení číselníkových hodnot potom konečně vypadá celkem elegantně.<pre style="font-family:'courier new';">Assert.assertTrue(EAddressTypeCodes.isResidence(addressLink.getAddressType()));<br /></pre>Navíc, isX metody samozřejmě nemusí mít jako vstupní parametry jenom samotné číselníkové třídy, pro pohodlné použití a úspornější volání mohou obsahovat například i metody isCzechRepublic(Address) nebo isResidence(AddressLink).<br /><br /><span style="font-weight: bold;">Závěr</span><br /><br />Tolik jednoduché zamyšlení nad porovnáváním číselníkových hodnot. Nekladl jsem si za cíl přinést úplný výčet všech myslitelných řešení, šlo mi o to popsat posloupnost úvah, které vedou k mnou preferovanému řešení – enumu s isX metodami. <div>Jak porovnávání číselníkových hodnot řešíte vy? Podělte se o své nápady a zkušenosti v diskuzi pod článkem.</div><div><br /></div>Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com11tag:blogger.com,1999:blog-8395640426039288836.post-69070356749823729062009-09-10T21:36:00.002+02:002009-09-10T22:09:54.272+02:00Závislosti polí ve formuláři a Drools<div>Ve druhé části blogu o <a href="http://tom2ee-cs.blogspot.com/2009/07/zavislosti-poli-ve-formulari.html">závislostech polí ve formuláři</a> bych rád navázal na předchozí úvahy a ukázal, jak postup popsaný v první části implementovat s pomocí knihovny <a href="http://jboss.org/drools/">Drools</a>. Tento příspěvek nechce být obecnou úvahou nad vhodností použití <a href="http://en.wikipedia.org/wiki/Business_rules_engine">rule engine</a>s. Na příkladě závislostí polí ve formuláři chci ukázat řešení s pomocí technologie, která je v mém okolí stále chápána jako okrajová a netradiční, ale která je podle mě pro řešení tohoto problému velmi výhodná. </div><div><br /></div><div>Použiju stejný příklad jako minule a to parcelu evidovanou na katastru nemovitostí. Atributy parcely a jejich vztahy pro čtyři typy parcel jsou zachyceny následující tabulkou.<br /><br /><table><tbody></tbody><tbody><tr><td bgcolor="lightgray">Popis parcely</td><td bgcolor="lightgray">České KÚ?</td><td bgcolor="lightgray">Má číslo?</td><td bgcolor="lightgray">Typ?</td><td bgcolor="lightgray">Ověřitelné</td><td bgcolor="lightgray">Číslo</td><td bgcolor="lightgray">Číslo LV</td><td bgcolor="lightgray">Pomocná identifikace</td><td bgcolor="lightgray">Zdroj PZE</td></tr><tr><td bgcolor="lightblue">Parcela katastru nemovitostí</td><td bgcolor="antiquewhite">ano</td><td bgcolor="antiquewhite">ano</td><td bgcolor="antiquewhite">KN</td><td bgcolor="palegreen">ano</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">nepřístupné, null</td></tr><tr><td bgcolor="lightblue">Parcela zjednodušené evidence</td><td bgcolor="antiquewhite">ano</td><td bgcolor="antiquewhite">ano</td><td bgcolor="antiquewhite">ZE</td><td bgcolor="palegreen">ano</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="palegreen">přístupné, povinné</td></tr><tr><td bgcolor="lightblue">Budoucí parcela</td><td bgcolor="antiquewhite">ano</td><td bgcolor="antiquewhite">ne</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">ne</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="pink">nepřístupné, null</td></tr><tr><td bgcolor="lightblue">Zahraniční parcela</td><td bgcolor="antiquewhite">ne</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">ne</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="pink">nepřístupné, null</td></tr></tbody></table><br />První tři atributy můžeme chápat jako „diskriminant“ parcely – jejich hodnoty podmiňují přístupnost, povinnost a přednastavené hodnoty jejich dalších atributů.<br /><br />Dále budu pracovat s třídou <span class="Apple-style-span" style="font-family:'courier new';">Plot</span>, která reprezentuje parcelu, s třídou <span class="Apple-style-span" style="font-family:'courier new';">FieldState</span>, která obecně definuje stavy polí a s třídou <span class="Apple-style-span" style="font-family:'courier new';">PlotFieldState</span>, která definuje stavy polí odpovídající atributům parcely. Dále si připomeňme interface <span class="Apple-style-span" style="font-family:'courier new';">PlotDependencySolver</span> s metodou <span class="Apple-style-span" style="font-family:'courier new';">solveDependencies(Plot, PlotFieldState)</span>, která reprezentuje algoritmus řešení závislostí. Přesnou definici těchto tříd můžete nalézt v <a href="http://tom2ee-cs.blogspot.com/2009/07/zavislosti-poli-ve-formulari.html">první části blogu</a>.</div><div><br /></div><div>Řekněme, že chceme napsat pravidlo (ve smyslu Drools), které popíše stav, kdy je parcela evidovaná v katastru nemovitostí (tedy stav popsaný prvním řádkem tabulky). Pravidlo "<span class="Apple-style-span" style="font-family:'courier new';">Land register</span>" může vypadat třeba takto:<pre class="Java" name="code">rule "Land register"<br />when<br />plot : Plot(locatedInCR == true, numberAssigned == true, plotType == "KN")<br />state : PlotFieldState()<br />then<br />state.setNumberAssigned(FieldState.ENABLED);<br />state.setPlotType(FieldState.ENABLED);<br /><br />state.setVerifiable(FieldState.ENABLED);<br />state.setNumber(FieldState.ENABLED_MANDATORY);<br />state.setOwnershipNumber(FieldState.ENABLED_MANDATORY);<br />state.setAuxiliaryId(FieldState.DISABLED);<br />state.setSimplifiedEvidenceSource(FieldState.DISABLED);<br /><br />plot.setAuxiliaryId(null);<br />plot.setSimplifiedEvidenceSource(null);<br />end<br /></pre>Aplikovatelnost pravidla je popsána klauzulí "<span class="Apple-style-span" style="font-family:'courier new';">when</span>", která porovnává hodnoty atributů v instanci třídy <span class="Apple-style-span" style="font-family:'courier new';">Plot</span>, tedy hodnoty, které byly vyplněné ve formuláři. Další parametr říká, že pravidlo musí mít k dispozici i instanci třídy <span class="Apple-style-span" style="font-family:'courier new';">PlotState</span>, aby stav formuláře mohlo aktualizovat. V těle pravidla potom dojde k aktualizaci stavu a nastavení některých hodnot atributů parcely, tak jak to u parcel z katastru nemovitostí chceme.<br /><br /></div><div>Podobně potom vypadá další pravidlo, tentokrát popisujicí zahraniční parcelu.<pre class="Java" name="code">rule "Foreign plot"<br />when<br />plot : Plot(locatedInCR == false)<br />state : PlotFieldState()<br />then<br />state.setNumberAssigned(FieldState.DISABLED);<br />state.setPlotType(FieldState.DISABLED);<br />state.setVerifiable(FieldState.DISABLED);<br />state.setNumber(FieldState.DISABLED);<br />state.setOwnershipNumber(FieldState.DISABLED);<br />state.setAuxiliaryId(FieldState.ENABLED_MANDATORY);<br />state.setSimplifiedEvidenceSource(FieldState.DISABLED);<br /><br />plot.setNumberAssigned(false);<br />plot.setPlotType(null);<br />plot.setNumber(null);<br />plot.setOwnershipNumber(null);<br />plot.setSimplifiedEvidenceSource(null);<br />end<br /></pre></div><div>Všechna pravidla můžeme udržovat ve zdrojovém souboru PlotRules.drl.</div><pre class="Java" name="code">package PlotRules<br /><br />import dependency.*;<br />import plot.*;<br /><br />rule "Land register"<br />...<br />end<br /><br />rule "Simplified evidence"<br />...<br />end<br /><br />rule "Future plot"<br />...<br />end<br /><br />rule "Foreign plot"<br />...<br />end<br /></pre><div>Příklady ukázaly, že pravidla v Drools většinou píšeme v DRL (Drools Rule Language) a přistupujeme přitom k Java třídám.</div><div><br /></div><div>Máme pravidla, ale co je třeba udělat, aby se správně aplikovala? Implementace metody <span class="Apple-style-span" style="font-family:'courier new';">PlotDependencySolver.solveDependencies,</span> která bude používat Drools a pravidla, je jednoduchá. </div><pre class="Java" name="code"> public void solveDependencies(Plot plot, PlotFieldState state) {<br /> setUp();<br /> StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();<br /> ksession.execute(Arrays.asList(plot, state));<br />}<br /></pre>Vytvoříme novou stateless session pro aplikaci pravidel a do její working memory vložíme instanci třídy <span class="Apple-style-span" style="font-family:'courier new';">Plot</span> (data) a instanci <span class="Apple-style-span" style="font-family:'courier new';">PlotFieldState</span> (stav formuláře). Je na Drools, jakým způsobem a v jakém pořadí pravidla aplikuje. Metoda <span class="Apple-style-span" style="font-family:'courier new';">setUp</span> ukazuje, jak vytvořit instanci <span class="Apple-style-span" style="font-family:'courier new';">KnowledgeBase</span>. Jinými slovy, jak inicializovat runtime knihovny Drools, abychom mohli aplikovat naše pravidla.<pre class="Java" name="code"> protected void setUp() {<br /> KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();<br /> kbuilder.add(ResourceFactory.newClassPathResource("PlotRules.drl"), ResourceType.DRL);<br /> KnowledgeBuilderErrors errors = kbuilder.getErrors();<br /> if (errors.size() > 0) {<br /> for (KnowledgeBuilderError error: errors) {<br /> System.err.println(error);<br /> }<br /> throw new IllegalArgumentException("Could not parse knowledge.");<br /> }<br /> kbase = KnowledgeBaseFactory.newKnowledgeBase();<br /> kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());<br />}<br /></pre><div>Tato ukázka kódu není pro náš příklad příliš zajímavá, ale pro úplnost ukazuje, jak načíst soubor s pravidly a vytvořit runtime Drools (v tomto případě instanci <span class="Apple-style-span" style="font-family:'courier new';">KnowledgeBase</span>). Ve většině aplikací budeme typicky spoléhat na nějakou factory, které jako parametr předložíme resource odpovídající souboru PlotRules.drl a necháme náš DI framework, aby provedl injection očekávané instance <span class="Apple-style-span" style="font-family:'courier new';">KnowledgeBase</span>.</div><div><br /></div>V čem je tedy implementace s pomocí Drools a pravidel výhodnější a jaké jsou nevýhody? Nejčastější otázky programátorů seznamujících se s rule engines jsou "proč je to lepší než ify?" a "nebude to mít výkonnostní problémy?" Zkusme si rozbrat výhody a nevýhody na příkladě formulářů a závislostí.<div><ul><li>Deklarativní pravidla - Nepopisuji algoritmus vyhodnocení závislostí, definuji množinu pravidel, které reagují na stavy formuláře. Možnost popsat řešení problému deklarativně vidím jako velkou výhodu tam, kde se pracuje s větším množstvím samostatných podmínek a kde je někdy obtížné sestavit algoritmus, který by všechny podmínky zohlednil.</li><li>Přehlednost a udržovatelnost - Tohle je dost subjektivní faktor, ale já považuji deklarativní pravidla za stručnější, přehlednější a dlouhodobě lépe udržovatelná než Java implementaci popsanou v předchozím příspěvku. Znamená to, že doporučuji, aby se obchodní logika na různých systémech vždy implementovala pomocí pravidel? To samozřejmě netvrdím, záleží na konkrétních požadavcích.</li><li>Samostatný životní cyklus pravidel - Životní cyklus pravidel nemusí být stejný jako životní cyklus Java zdrojových kódů a verzí aplikace. Pokud se například pravidla mění výrazně častěji než zbytek aplikace, je možné je udržovat a distribuovat nezávisle na zbytku systému. Není to ale nutné, u některých systémů by takový přístup mohl být považovaný i za nevýhodu.</li></ul><div>Co to stojí? </div><div><ul><li>Integrace dalšího nástroje s naší aplikace - Integrace Drools s DI frameworky jako Spring nebo Google Guice je jednoduchá, představuje jenom malou investici času a úsilí.</li><li>Výkonnost - Rule engines a deklarativní pravidla se samozřejmě nehodí na všechny problémy. Nevhodné použití může znamenat výkonnostní problémy. V případě závislostí polí ve formuláři bych je ale nečekal. Drools nabízí několik možností, jak výkonnost testovat a optimalizovat. Pravidla mohu například indexovat (možno chápat podobně jako indexy v relačních databázích) a vyhodnocení tím výrazně zrychlit. </li><li>DRL - Drools Rule Language představuje nový programovací jazyk pro část projektu a vyžaduje tedy určitý čas na pochopení a zvládnutí. Není to ale žádné drama, struktura pravidel je často velmi jednoduchá a těla pravidel, to už je stará známá Java. </li><li>DRL a udržovatelnost kódu - Jako samozřejmost dnes bereme, že naše IDE umí hledat použití tříd, atributů, metod, ... nad všemi zdrojovými soubory projektu. V rámci JBoss Tools je k dispozici <a href="http://downloads.jboss.com/drools/docs/5.0.1.26597.FINAL/drools-expert/html/ch07.html">sada pluginů</a> pro Eclipse, které nabízí editor se zvýrazňováním syntaxe a code completion, vizualizaci pravidel, debugger pravidel, ale některé vlastnosti tato podpora stále nenabízí. Chybí mi např. vyhledávání referencí, které by uvažovalo i pravidla a také refactoring, který by zahrnoval pravidla. Na <a href="http://blog.athico.com/">blogu Drools</a> se lze dočíst, že se na těchto dvou vlastnostech IDE už pracuje, ale aktuální stav je, že tato podpora stále chybí. Pro IntelliJ Idea jsem žádnou podporu Drools nenašel.</li></ul></div><div>Tradiční implementace v Javě je diskutovaná v první části blogu. Zájemci o vyzkoušení a porovnání příkladů mohou uvítat možnost stáhnout si <a href="http://sites.google.com/site/tomaspinos/cms/tom2ee-cs/2903665475121572511/drools-test.zip">testovací projekt</a> (konfigurace v pom.xml).</div><div><br /></div><div>Příspěvek si kladl za cíl představit stále málo používané, ale v mnohých aspektech výhodné řešení problému závislostí polí ve formuláři pomocí frameworku Drools. Navazuje na první část, která popisuje obecný problém závislostí polí ve formuláři a diskutuje různé přístupy k jeho řešení. </div><div><br /></div><div>Jaký názor na rule engines máte vy? Použili jste už Drools nebo jiný framework na komerčním projektu? Podělte se o své zkušenosti v diskuzi pod článkem. Na téma konceptu rule engines a dalších vlastností knihovny Drools bych rád v budoucnu napsal samostatnou sérii článků (příklady, integrace se Springem, testy výkonnosti a podobně).</div><div><br /></div></div>Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com3tag:blogger.com,1999:blog-8395640426039288836.post-17444709345212549672009-07-10T06:15:00.000+02:002009-07-10T21:49:33.637+02:00Závislosti polí ve formuláři<div>Ve svém vůbec prvním blogu bych se rád podíval na řešení závislostí polí ve formuláři. Nechci popisovat konkrétní technologii pro psaní webových aplikací nebo například tlustých klientů. Zajímá mě obecný problém, kdy máme aplikaci pracující s množstvým formulářů, které jsou „složité“ a jejich složitost je tvořena mimo jiné i závislostmi mezi poli, která obsahují. Závislostí polí ve formuláři myslím některou z následujících možností:<br /><ul><li>Viditelnost nebo přístupnost pole závisí na hodnotě jiného pole nebo polí.</li><li>Seznam přípustných hodnot pro nějaké pole závisí na hodnotě jiného pole nebo polí.</li><li>Změna hodnoty pole (nebo polí) způsobí změnu stavu (tzn. přístupnosti nebo hodnoty) jiného pole nebo polí.</li><li>Libovolnou jinou závislost.</li></ul>V tomto blogu se nezaměřím na konkrétní technologií vytváření GUI aplikací, chci spíše popsat obecný postup, který lze implementovat při tvorbě webových aplikací, tlustých klientů nebo třeba i ajaxových webů. Příklady v blogu jsou naprogramované v Javě, ale diskutovaný problém i jeho řešení se na Javu omezovat nemusí.<br /><br />Většina frameworků a aplikací má dnes spolehlivě vyřešené problémy jako binding formulářových dat na doménový model aplikace, validace jednotlivých polí, lokalizace a další problémy, které musí typické GUI řešit. Ještě jsem se ale nesetkal s aplikací, která by závislostí polí ve formuláři řešila způsobem, který by byl přehledný, do budoucna udržovatelný a hlavně, který by byl testovatelný (ve smyslu unit testů). Domnívám se, že závislosti polí jsou často v návrhu aplikací opomíjeny, a chci proto představit jednoduchý návod, jak závislosti řešit. Jistě nejde o žádnou převratnou inovaci, ale podobný postup jsem zatím nikde zdokumentovaný a hlavně použitý nenašel. Smysl tohoto blogu tedy vidím v tom, že některým čtenářům pomůže uvědomit si problém a jeho nepříjemným důsledkům se vyhnout.<br /></div><br /><div><b>Příklad</b><br /><br />Budu používat příklad převzatý z bankovní aplikace, která řeší evidenci nemovitostí pro účely pořízení a schvalování žádosti o hypoteční úvěr. Aplikace eviduje tři typy nemovitostí – parcely, budovy a bytové jednotky. Pro svůj příklad se zaměřím na parcely. Atributy parcely a jejich závislosti jsou popsány následující tabulkou:<br /><br /><table><tbody></tbody><tbody><tr><td bgcolor="lightgray">Popis parcely</td><td bgcolor="lightgray">České KÚ?</td><td bgcolor="lightgray">Má číslo?</td><td bgcolor="lightgray">Typ?</td><td bgcolor="lightgray">Ověřitelné</td><td bgcolor="lightgray">Číslo</td><td bgcolor="lightgray">Číslo LV</td><td bgcolor="lightgray">Pomocná identifikace</td><td bgcolor="lightgray">Zdroj PZE</td></tr><tr><td bgcolor="lightblue">Parcela katastru nemovitostí</td><td bgcolor="antiquewhite">ano</td><td bgcolor="antiquewhite">ano</td><td bgcolor="antiquewhite">KN</td><td bgcolor="palegreen">ano</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">nepřístupné, null</td></tr><tr><td bgcolor="lightblue">Parcela zjednodušené evidence</td><td bgcolor="antiquewhite">ano</td><td bgcolor="antiquewhite">ano</td><td bgcolor="antiquewhite">ZE</td><td bgcolor="palegreen">ano</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="palegreen">přístupné, povinné</td></tr><tr><td bgcolor="lightblue">Budoucí parcela</td><td bgcolor="antiquewhite">ano</td><td bgcolor="antiquewhite">ne</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">ne</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="pink">nepřístupné, null</td></tr><tr><td bgcolor="lightblue">Zahraniční parcela</td><td bgcolor="antiquewhite">ne</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">ne</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="pink">nepřístupné, null</td><td bgcolor="palegreen">přístupné, povinné</td><td bgcolor="pink">nepřístupné, null</td></tr></tbody></table><br />Sloupce tabulky popisují atributy a vlastnosti parcely, řádky popisují čtyři možné typy parcel. První tři atributy můžeme chápat jako „diskriminant“ parcely – jejich hodnoty podmiňují přístupnost, povinnost a přednastavené hodnoty jejich dalších atributů. Závislosti polí nemusí být vždy specifikovány tabulkou, asi nejčastěji jsou podobné závislosti popsány sadou podmínek („if – then“). Bohužel není výjimkou, že podobná specifikace úplně chybí a předpokládá se nějaká implicitní znalost. V reálné aplikaci jsou definice parcely a závislostí jejich atributů mírně složitější. Snažil jsem se zadání zjednodušit, ale přitom ho ponechat dostatečně ilustrativní.<br /><br />Jak by takové zadání řešila typická aplikace? Nejčastěji se setkávám s tím, že se podobné závislosti neřeší systematicky, že jsou řešeny množstvím podmínek na různých místech nebo vrstvách aplikace. Podmínky mohou být často rozhozeny mezi view a controllery (ve smyslu MVC návrhu). Například událost změny katastrálního území parcely (KÚ) může vyvolat akci na serveru (ve smyslu volání metody controlleru Springu MVC nebo volání akce Struts 2), která změní KÚ a pokud nová hodnota znamená:<br /><ul><li>České KÚ - vynuluje ještě hodnoty pomocné identifikace a zdoje PZE;</li><li>Zahraniční KÚ - nastaví typ parcely na null, stejně tak její číslo a číslo LV a vynuluje také hodnotu zdroje PZE. </li></ul>Později se ještě nová hodnota KÚ bude muset zohlednit ve view, kde se podle ní rozhodne o přístupnosti / nepřístupnosti dalších polí formuláře. Pokud si teď představíme běžnou situaci, kdy má programátor rozhodnout, jestli je specifikace správně implementovaná, není to jednoduchý úkol. Jinak než uživatelským testováním není jednoduché zjistit:<br /><ul><li>Kde všude se závislosti polí vyhodnocují?</li><li>Dochází někde k duplikacím kódu pro vyhodnocování závislostí?</li><li>Jaký dopad to má na doménový model aplikace?</li><li>Kde všude se vyhodnocuje přístupnost polí formuláře?</li><li>Jak si s tím později poradí validace?</li><li>… a podobné otázky. </li></ul>Pokud je formulářů a závislostí hodně, začne být velkým problémem, že závislosti nejsou zachycené explicitně, tzn. že neexistuje něco jako stav přístupnosti polí formuláře a jednotný mechanismus pro jeho vyhodnocování. Viděl jsem už několik projektů, u kterých mě mrzelo, že kód jinak rozumně a vhodně navržené aplikace byl znehodnocen . Netvrdím samozřejmě, že jsou to právě závislosti formulářových polí, které vždy degradují kvalitu zdrojového kódu, domnívám se ale, že jde o jeden z typických problémů.<br /></div><br /><div><b>Návrh řešení</b><br /><br />Představme si, že parcelu máme reprezentovanou třídou <span class="Apple-style-span" style="font-family:'courier new';">Plot</span>.<pre class="Java" name="code">public class Plot {<br /> private boolean locatedInCR;<br /> private boolean numberAssigned;<br /> private String plotType;<br /> private String number;<br /> private String ownershipNumber;<br /> private String auxiliaryId;<br /> private String simplifiedEvidenceSource;<br />...<br />}<br /></pre>Obecně pro každé pole formuláře můžeme uvažovat čtyři stavy:<br /><ul><li>Přístupné, nepovinné</li><li>Přístupné, povinné</li><li>Nepřístupné</li><li>Skryté</li></ul>Mějme tyto stavy reprezentované třídou <span class="Apple-style-span" style="font-family:'courier new';">FieldState</span>.<pre class="java" name="code">public class FieldState {<br /> public static final FieldState ENABLED_OPTIONAL = new FieldState("ENABLED_OPTIONAL", true, true, false);<br /> public static final FieldState ENABLED_MANDATORY = new FieldState("ENABLED_MANDATORY", true, true, true);<br /> public static final FieldState ENABLED = ENABLED_OPTIONAL;<br /> public static final FieldState DISABLED = new FieldState("DISABLED", true, false, false);<br /> public static final FieldState HIDDEN = new FieldState("HIDDEN", false, false, false);<br /><br /> private String code;<br /> private boolean visible;<br /> private boolean enabled;<br /> private boolean mandatory;<br /><br /> public boolean isVisible() { return visible; }<br /> public boolean isEnabled() { return enabled; }<br /> public boolean isMandatory() { return mandatory; }<br /><br /> @Override<br /> public String toString() { return code; }<br /><br /> private FieldState(String code, boolean visible,<br /> boolean enabled, boolean mandatory)<br /> {<br /> this.code = code;<br /> this.visible = visible;<br /> this.enabled = enabled;<br /> this.mandatory = mandatory;<br /> }<br />}<br /></pre>Jako „zkratka“ je ve třídě ještě reprezentovaná hodnota „přístupné“ (<span class="Apple-style-span" style="font-family:'courier new';">ENABLED</span>), která vlastně přesně znamená „přístupné, nepovinné“.<br /><br />Pokud budeme uvažovat formulář pro editaci dané parcely, můžeme stav jeho polí reprezentovat třídou <span class="Apple-style-span" style="font-family:'courier new';">PlotFieldState</span>. Třída popisuje přístupnost jednotlivých polí.<br /><pre class="java" name="code">public class PlotFieldState {<br /> private FieldState numberAssigned = FieldState.ENABLED;<br /> private FieldState plotType = FieldState.ENABLED;<br /> private FieldState verifiable = FieldState.ENABLED;<br /> private FieldState number = FieldState.ENABLED;<br /> private FieldState ownershipNumber = FieldState.ENABLED;<br /> private FieldState auxiliaryId = FieldState.ENABLED;<br /> private FieldState simplifiedEvidenceSource = FieldState.ENABLED;<br /> ...<br />}<br /></pre>Předpokládejme, že existuje mechanismus, který pro každou kombinaci hodnot parcely (tzn. libovolnou instanci třídy <span class="Apple-style-span" style="font-family:'courier new';">Plot</span>), umí vyhodnotit stav formuláře parcela (tzn. přístupnost a povinnost jeho polí). Mějme tento výpočet reprezentovaný rozhranním <span class="Apple-style-span" style="font-family:'courier new';">PlotDependencySolver</span>.<br /><pre class="java" name="code">public interface PlotDependencySolver {<br /> void solveDependencies(Plot plot, PlotFieldState state);<br />}<br /></pre>Interface <span class="Apple-style-span" style="font-family:'courier new';">PlotDependencySolver</span> obdrží jako parametry aktuální hodnoty parcely a stav odpovídajících polí formuláře. Provede vyhodnocení a výsledek - tzn. změněné hodnoty a změněný stav - reflektuje do těchto parametrů.<br /><br />Explicitní reprezentaci stavu polí formuláře (reprezentovanou třídou <span class="Apple-style-span" style="font-family:'courier new';">PlotFieldState</span>) můžeme použít pro následující činnosti:<ul><li>Zobrazování (konstrukci view) formuláře. </li><ul><li>Stačí pouze procházet pole formuláře, kde pro každé z nich mám k dispozici stav, který říká, jestli dané pole vykreslit, resp. jestli bude přístupné. Důležité je, že viditelnost a přístupnost polí nemusím řešit složitými podmínkami (např. v JSP, u mnoha elementů typicky v atributu <span class="Apple-style-span" style="font-family:'courier new';">disabled</span>), ale jednoduchým dotazem na stav (<span class="Apple-style-span" style="font-family:'courier new';">PlotFieldState</span>).</li></ul><li>Validace hodnot z formuláře.</li><ul><li>Opět stačí procházet hodnoty polí z formuláře, pro každé z nich ihned vím, jestli je povinné / nepovinné, tedy jestli ho mám validovat. </li><li>Pokud by bylo požadavkem validovat i kombinace hodnot polí (např. kombinace hodnot combo boxů – „auto Škoda, model Octavia“), mohu třídu <span class="Apple-style-span" style="font-family:'courier new';">PlotFieldState</span> rozšířit i o reprezentaci přípustných hodnot pro pole formuláře. Snahou je přípravit si takovou stavovou informaci, která mi dovolí iterovat přes pole formuláře a validovat jedno po druhém. </li></ul><li>Testování závislosti polí.</li><ul><li>Mám-li stav formuláře explicitně reprezentovaný třídou a mechanismus pro zjišťování stavu, mohu tento mechanismus testovat (tzn. testovat závislost polí formuláře).</li></ul></ul><div>Hlavní myšlenka je tedy v tom mít explicitní reprezentaci polí formuláře a mechanismus, jak ho vyhodnotit. Výrazně nám to zjednoduší jinak potenciálně složité akce jako je zobrazování formuláře, validace hodnot a testování závislosti polí. </div><div><br /></div>Následující ukázka kódu demonstruje možný postup při validaci parcely.<pre class="java" name="code">public class PlotValidationHelper {<br /> public void validate(Plot plot, PlotFieldState state) {<br /> if (state.getNumber().isMandatory() && !isNumber(plot.getNumber())) {<br /> // report error<br /> }<br /> if (state.getOwnershipNumber().isMandatory() && !isNumber(plot.getOwnershipNumber())) {<br /> // report error<br /> }<br /> if (state.getAuxiliaryId().isMandatory() && plot.getAuxiliaryId() == null) {<br /> // report error<br /> }<br /> if (state.getSimplifiedEvidenceSource().isMandatory() && plot.getSimplifiedEvidenceSource() == null) {<br /> // report error<br /> }<br /> }<br /> ...<br />}<br /></pre>Další ukázka kódu představuje jednoduchý JUnit test case pro testivání závislostí polí.<pre class="java" name="code">public class Test1 extends TestCase {<br /> public void testLandRegister() {<br /> Plot plot = new Plot();<br /> plot.setLocatedInCR(true);<br /> plot.setNumberAssigned(true);<br /> plot.setPlotType("KN");<br /><br /> PlotFieldState state = new PlotFieldState();<br /><br /> getSolver().solveDependencies(plot, state);<br /><br /> assertEquals(FieldState.ENABLED, state.getVerifiable());<br /> assertEquals(FieldState.ENABLED_MANDATORY, state.getNumber());<br /> assertEquals(FieldState.ENABLED_MANDATORY, state.getOwnershipNumber());<br /> assertEquals(FieldState.DISABLED, state.getAuxiliaryId());<br /> assertNull(plot.getAuxiliaryId());<br /> assertEquals(FieldState.DISABLED, state.getSimplifiedEvidenceSource());<br /> assertNull(plot.getSimplifiedEvidenceSource());<br /> }<br /> …<br />}<br /></pre><br /></div><div><b>Implementace</b><br /><br />Zbývá už jen zamyslet se nad implementací mechanismu pro vyhodnocování nového stavu formuláře, tedy implementaci rozhranní <span class="Apple-style-span" style="font-family:'courier new';">PlotDependencySolver</span>. Pro implementaci můžeme zvážit dvě možnosti - přímou implementaci prostředky Javy a implementaci pomocí rule engine.</div><div><br />Nebude žádným překvapením, že prostředky Javy můžeme metodu solveDependencies implementovat prostě jako sadu if – then statementů.<pre class="java" name="code">public class JavaPlotDependencySolver implements PlotDependencySolver {<br /> @Override<br /> public void solveDependencies(Plot plot, PlotFieldState state) {<br /> if (plot.isLocatedInCR() && plot.isNumberAssigned() && "KN".equals(plot.getPlotType())) {<br /> // Land register<br /> state.setNumberAssigned(FieldState.ENABLED);<br /> state.setPlotType(FieldState.ENABLED);<br /> state.setVerifiable(FieldState.ENABLED);<br /> state.setNumber(FieldState.ENABLED_MANDATORY);<br /> state.setOwnershipNumber(FieldState.ENABLED_MANDATORY);<br /> state.setAuxiliaryId(FieldState.DISABLED);<br /> state.setSimplifiedEvidenceSource(FieldState.DISABLED);<br /><br /> plot.setAuxiliaryId(null);<br /> plot.setSimplifiedEvidenceSource(null);<br /> } else if (!plot.isLocatedInCR()) {<br /> // Foreign plot<br /> state.setNumberAssigned(FieldState.DISABLED);<br /> state.setPlotType(FieldState.DISABLED);<br /> state.setVerifiable(FieldState.DISABLED);<br /> state.setNumber(FieldState.DISABLED);<br /> state.setOwnershipNumber(FieldState.DISABLED);<br /> state.setAuxiliaryId(FieldState.ENABLED_MANDATORY);<br /> state.setSimplifiedEvidenceSource(FieldState.DISABLED);<br /><br /> plot.setNumberAssigned(false);<br /> plot.setPlotType(null);<br /> plot.setNumber(null);<br /> plot.setOwnershipNumber(null);<br /> plot.setSimplifiedEvidenceSource(null);<br /> } else { ... }<br /> }<br />}</pre>Takový přímočará implementace má ale i některá omezení:<ul><li>Musíme sestavit deterministický postup (algoritmus), kdy všechny podmínky a závislosti vyhodnotíme a reflektujeme do nového stavu. </li><li>Pokud si vyhodnocování závislostí představíme v nějakém "vyhodnocovacím stromě" (uvádím v uvozovkách a doufám, že intuitivní chápání pojmu tady postačí), může a bude se pravděpodobně stávat, že některé podstromy výpočtu se budou opakovat, protože musí reflektovat, že stav formuláře se během vyhodnocování mění (každá jednotlivá podmínka může do stavu něčím přispět). Mířím k tomu, že sestavit takový algoritmus nemusí být vždy jednoduché.</li></ul>Přesto platí, že dostat vyhodnocování závislostí na jedno místo, reprezentované rozhranním <span class="Apple-style-span" style="font-family:'courier new';">PlotDependencySolver</span>, vidím jako úspěch s jasným pozitivním dopadem na přehlednost a udržovatelnost kódu aplikace. U složitějších závislostí se dá přiklonit k rozšiřitelnějšímu řešení – mohu např. vytvořit „pluggable“ mechanismus, kde jednotlivé implementace <span class="Apple-style-span" style="font-family:'courier new';">PlotDependencySolver</span>-u budou zohledňovat jednotlivé typy parcel (viz. zadání příkladu výše).<br /><br /></div><div>Z uvedených důvodů je elegantní a preferovanou možností zapojení rule engine (např. Drools), které nám dovolí většinu zmíněných výhrad jednoduše překonat. Protože vidím tuto možnost jako velmi vhodnou a zajímavou, budu jí věnovat další samostatný blog.</div><div><br /><b>Shrnutí</b><br /><br /></div><div>V tomto blogu snažím klást důraz hlavně na myšlenku explicitní reprezentace stavu formuláře, ne na její implementaci. Důležité je dosáhnout toho, že závislosti polí budou vyhodnocovány na jednom místě a stav formuláře bude důsledně používaný. Pojďme si nakonec shrnout výhody a nevýhody řešení s explicitní reprezentací stavu polí formuláře. Postup přináší následující výhody:<ul><li>Závislosti polí ve formuláři jsou definovány na jednom místě. </li><ul><li>Abych závislosti zmapoval, pochopil nebo upravil, nemusím je hledat na různých místech / vrstvách aplikace.</li><li>Odpadávají duplicity vyhodnocování závislostí. Stejný kód pro vyhodnocení stavu formuláře mi poslouží pro vykreslení polí ve view jako i pro validaci jejich hodnot.</li></ul><li>Přehledný, snadno čitelný kód pro vykreslování view a pro validace hodnot z formuláře.</li><ul><li>Procházím pole jedno po druhém, vyhodnocuji jenom, jakým způsobem (ne)zobrazit a jak validovat.</li></ul><li>Testovatelnost závislostí. </li><ul><li>Testovatelnost závislostí vidím jako největší výhodu tohoto řešení. </li></ul></ul>Řešení má samozřejmě i nevýhody:<br /><ul><li>Režie navíc. </li><ul><li>Samostatná reprezentace stavu polí formuláře jako i práce s explicitně definovanými stavy může být pro jednodušší aplikace zbytečná. Vedle doménových tříd reprezentujích strukturu informací tu mám ještě další, speciální třídy pro zachycení stavu polí ve formuláři.</li></ul><li>Kombinace přístupu explicitního stavu polí s jiným řešením může smazat výhody explicitních stavů a vytvořit nepřehledný, těžko udržovatelný kód. </li><ul><li>To je ale obecná poznámka aplikovatelná snad na jakékoliv řešení. Pokud někdo nedodrží domluvené návrhové vzory, přehlednost a udržovatelnost kódu tím samozřejmě trpí vždy.</li></ul></ul>Pokud jste blog dočetli až sem, děkuji za pozornost a trpělivost. Jak závislosti polí řešíte vy? Považuje explicitní reprezentaci stavu formuláře za dobrý nápad? Pokud vás problematika zaujala, doporučuji svůj další blog, který bude popisovat implementaci závislostí s pomocí Drools.<br /></div>Tomáš Piňoshttp://www.blogger.com/profile/07543015673837913396noreply@blogger.com13