čtvrtek 10. září 2009

Závislosti polí ve formuláři a Drools

Ve druhé části blogu o závislostech polí ve formuláři bych rád navázal na předchozí úvahy a ukázal, jak postup popsaný v první části implementovat s pomocí knihovny Drools. Tento příspěvek nechce být obecnou úvahou nad vhodností použití rule engines. 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á.

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.

Popis parcelyČeské KÚ?Má číslo?Typ?OvěřitelnéČísloČíslo LVPomocná identifikaceZdroj PZE
Parcela katastru nemovitostíanoanoKNanopřístupné, povinnépřístupné, povinnénepřístupné, nullnepřístupné, null
Parcela zjednodušené evidenceanoanoZEanopřístupné, povinnépřístupné, povinnénepřístupné, nullpřístupné, povinné
Budoucí parcelaanonenepřístupné, nullnenepřístupné, nullnepřístupné, nullpřístupné, povinnénepřístupné, null
Zahraniční parcelanenepřístupné, nullnepřístupné, nullnenepřístupné, nullnepřístupné, nullpřístupné, povinnénepřístupné, null

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ů.

Dále budu pracovat s třídou Plot, která reprezentuje parcelu, s třídou FieldState, která obecně definuje stavy polí a s třídou PlotFieldState, která definuje stavy polí odpovídající atributům parcely. Dále si připomeňme interface PlotDependencySolver s metodou solveDependencies(Plot, PlotFieldState), která reprezentuje algoritmus řešení závislostí. Přesnou definici těchto tříd můžete nalézt v první části blogu.

Ř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 "Land register" může vypadat třeba takto:
rule "Land register"
when
plot : Plot(locatedInCR == true, numberAssigned == true, plotType == "KN")
state : PlotFieldState()
then
state.setNumberAssigned(FieldState.ENABLED);
state.setPlotType(FieldState.ENABLED);

state.setVerifiable(FieldState.ENABLED);
state.setNumber(FieldState.ENABLED_MANDATORY);
state.setOwnershipNumber(FieldState.ENABLED_MANDATORY);
state.setAuxiliaryId(FieldState.DISABLED);
state.setSimplifiedEvidenceSource(FieldState.DISABLED);

plot.setAuxiliaryId(null);
plot.setSimplifiedEvidenceSource(null);
end
Aplikovatelnost pravidla je popsána klauzulí "when", která porovnává hodnoty atributů v instanci třídy Plot, tedy hodnoty, které byly vyplněné ve formuláři. Další parametr říká, že pravidlo musí mít k dispozici i instanci třídy PlotState, 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.

Podobně potom vypadá další pravidlo, tentokrát popisujicí zahraniční parcelu.
rule "Foreign plot"
when
plot : Plot(locatedInCR == false)
state : PlotFieldState()
then
state.setNumberAssigned(FieldState.DISABLED);
state.setPlotType(FieldState.DISABLED);
state.setVerifiable(FieldState.DISABLED);
state.setNumber(FieldState.DISABLED);
state.setOwnershipNumber(FieldState.DISABLED);
state.setAuxiliaryId(FieldState.ENABLED_MANDATORY);
state.setSimplifiedEvidenceSource(FieldState.DISABLED);

plot.setNumberAssigned(false);
plot.setPlotType(null);
plot.setNumber(null);
plot.setOwnershipNumber(null);
plot.setSimplifiedEvidenceSource(null);
end
Všechna pravidla můžeme udržovat ve zdrojovém souboru PlotRules.drl.
package PlotRules

import dependency.*;
import plot.*;

rule "Land register"
...
end

rule "Simplified evidence"
...
end

rule "Future plot"
...
end

rule "Foreign plot"
...
end
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.

Máme pravidla, ale co je třeba udělat, aby se správně aplikovala? Implementace metody PlotDependencySolver.solveDependencies, která bude používat Drools a pravidla, je jednoduchá.
 public void solveDependencies(Plot plot, PlotFieldState state) {
setUp();
StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
ksession.execute(Arrays.asList(plot, state));
}
Vytvoříme novou stateless session pro aplikaci pravidel a do její working memory vložíme instanci třídy Plot (data) a instanci PlotFieldState (stav formuláře). Je na Drools, jakým způsobem a v jakém pořadí pravidla aplikuje. Metoda setUp ukazuje, jak vytvořit instanci KnowledgeBase. Jinými slovy, jak inicializovat runtime knihovny Drools, abychom mohli aplikovat naše pravidla.
 protected void setUp() {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("PlotRules.drl"), ResourceType.DRL);
KnowledgeBuilderErrors errors = kbuilder.getErrors();
if (errors.size() > 0) {
for (KnowledgeBuilderError error: errors) {
System.err.println(error);
}
throw new IllegalArgumentException("Could not parse knowledge.");
}
kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
}
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 KnowledgeBase). 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 KnowledgeBase.

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í.
  • 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.
  • 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.
  • 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.
Co to stojí?
  • 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í.
  • 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.
  • 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.
  • 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 sada pluginů 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 blogu Drools 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.
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 testovací projekt (konfigurace v pom.xml).

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í.

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ě).

3 komentáře:

  1. SEAM framework pouziva DROOLs pravidla pri pridelovani permissions, tj. je mozne urobit security pravidla deklarativne, tak si to teraz trosku studujem.

    Co sa tyka JBoss Tools pluginu, pride mi to tak, ze jediny rozumny sposob ako debuggovat pravidla je vytvorit si log a ten potom preskumat, pretoze breakpoint je mozne dat iba na RHS pravidla.

    Ja som si to predstavoval skor tak, ze by sa mi pri debuggovani objavil RETE strom a tam by som presne videl nody ako sa mi plnia pri asserte (inserte) nejakeho faktu, taktiez by som chcel vidiet ako sa mi meni agenda...

    IDEA plugin som tiez hladal a nenasiel, zrejme to nebude take jednoduche urobit, tak sa do toho ani nepustaju. :)

    OdpovědětVymazat
  2. Díky za tip, framework Seam zatím neznám, ale zkusím se s ním seznámit.

    OdpovědětVymazat
  3. Supr, mrknul jsem na drools, a mrknu ne Seam, ale zkouset asi budu, az to budu mit kde zasit. Bude nejake dalsi pokracovani?

    OdpovědětVymazat