Návrhový vzor Fluid Logic
Návrhový vzor Fluid Logic byl publikován v díle Real World Java EE Patterns vydané v roce 2009 Adamem Bienem. Jedná se o návrhový vzor využívající se při vývoji software, který do sebe musí implementovat prvky dynamických jazyků. Vzor byl navržen a je prezentován pomocí programovacího jazyka Java, který je spustitelný v prostředí JVM, stejně jako i mnoho nově vznikající dynamických jazyků.
Důvody pro vznik návrhového vzoru
[editovat | editovat zdroj]Java je statický jazyk zdůrazňující silné typování, proto je vhodná pro implementování dobře definované a stabilní business logiky. Tento programovací jazyk však není stavěný na algoritmy, které se často mění, protože každá změna aplikace vyžaduje její kompilaci a nové nasazení do produkčního prostředí.
Silné typování a statická podoba jazyka nese tudíž i negativa. Pro různé účely je lepší využít možností dynamických jazyků než jazyka Java. Těmito jazyky mohou být například JavaScript, Ruby nebo Python. V jazyce Java se nám jen stěží podaří interpretovat business logiku aplikace za jejím běhu. Mohli bychom využít různé nástroje, jako je například ANTLR (http://www.antlr.org/), a vytvořit si vlastní doménově specifický jazyk (DSL), který by byl zpracováván a interpretován za běhu programu. S vytvořením vlastního tímto DSL je však spojeno obrovské úsilí ohledně designu, testování a dokumentace.
Výhody využití návrhového vzoru
[editovat | editovat zdroj]- Program je navržen tak, že musí spustit některou svojí část business logicky dynamicky.
- Algoritmy, které se často mění je potřeba izolovat a nahrávat dynamicky bez toho, aby ovlivnily zbytek aplikace.
- Část business logiky může být změněna, aniž by se musel program kompilovat a znovu nasazovat do produkčního prostředí.
- Není potřeba realizovat náročný vývoj vlastního interpretu.
- Možnost snadného integrování skriptových komponent s programy běžícími na Java EE prostředí.
Vytvoření návrhového vzoru
[editovat | editovat zdroj]Problém s vložením vlastním skriptů, které by náš program mohl dynamicky zpracovávat byl vyřešen v Java 6 Standard Edition. V Java specifikaci označené jako JSR-223 (Scripting for the Java Platform) je navrženo vhodné API, které nám nabízí interakci s více než 100 skriptovacími jazyky. Je dokonce možné vložit java objekt do objektu ScriptEngine a učinit ho tak dostupným pro zpracování dynamickým jazykem. Inicializace samotného ScriptEngine vezme jen pár řádek kódu a je ukázána níže na příkladu. Použití skriptovacích jazyků není závislé na žádném prostředí a může být využito jak případech aplikací, které musí běžet v různých Java EE kontejnerech, tak v případech, kde postačuje nasazení platformy Java SE. Protože návrhový vzor úzce souvisí s Java Scripting API, tak proto podrobněji vysvětluji a ukazuji na příkladech jeho využití, aby bylo jasně patrné, čeho všeho lze implementováním vzoru Fluid Logic dosáhnout.
Třída Calculator, definovaná níže v kódu, je naprogramována tak, aby zpracovala výraz předaný jako parametr metodě calculate.
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class Calculator {
private static final String ENGINE_NAME = "JavaScript";
private ScriptEngine scriptEngine = null;
private final static double FIVE = 5;
public Calculator(){
initScripting();
}
private void initScripting(){
ScriptEngineManager engineManager = new ScriptEngineManager();
this.scriptEngine = engineManager.getEngineByName(ENGINE_NAME);
if(this.scriptEngine == null){
throw new IllegalStateException("Cannot create... " + ENGINE_NAME);
}
}
public Object calculate(String formula){
Object retVal = null;
try {
Bindings binding = this.scriptEngine.createBindings();
binding.put("FIVE", FIVE);
this.scriptEngine.setBindings(binding, ScriptContext.GLOBAL_SCOPE);
retVal = this.scriptEngine.eval(formula);
} catch (Exception e) {
throw new IllegalStateException("Cannot create... " + e, e);
}
return retVal;
}
}
Nyní si ukážeme, jak třídu Calculator využít a spustit kód z JavaScript v Javě pomocí Java Scripting API. Můžete si všimnout, že při generování výrazu 2 * 2 * FIVE, jsme využili konstantu, kterou jsme si ve třídě Calculator nadefinovali a vložili do skriptového kontextu.
public class App {
public static void main( String[] args ){
Calculator cal = new Calculator();
System.out.println("Výsledek výpočtu je " +
cal.calculate("2 * 2 * FIVE"));
}
}
Jak jsme již napsali dříve, návrhový vzor Fluid Logic je vhodný zejména pro jeho možnost jednoduchého přemístění dynamického kódu nebo jeho změny. Kód může být nahrán z mnoha zdrojů. V další části si ukážeme, jak může být kód dynamicky nahrán ze souboru, jsou však možné i jiné varianty, například nahrání kódu uloženého v databázi nebo ze vzdáleného souboru umístěného na síti.
Způsob nahrání kódu ze souboru
[editovat | editovat zdroj]Nyní ukážu, jak je možné nahrát dynamicky kód ze souboru a nechat ho zpracovat pomocí Java Scripting API. Znovu se jedná o kód napsaný v JavaScriptu.
// Vytvoříme engine manager
ScriptEngineManager engineManager = new ScriptEngineManager();
// Vytvoříme konkrétní JavaScript engine
ScriptEngine scriptEngine = engineManager.getEngineByName("JavaScript");
// Pomocí JavaScript enginu zpracujeme soubor s kódem
scriptEngine.eval(new FileReader("soubor.js"));
Zavolání konkrétní funkce
[editovat | editovat zdroj]Další příklad nám ukazuje, jak je možné vytvořit metodu a poté jí opakovaně volat pomocí rozhraní Invocable.
String script = "function hello(name) { print('Hello, ' + name); }";
scriptEngine.eval(script);
// javax.script.Invocable je volitelné rozhraní.
// Před použitím musíme zkontrolovat, jestli náš engine rozhraní implementuje!
// JavaScript toto rozhraní implementuje.
Invocable inv = (Invocable) engine;
// Zavolání funkce hello s parametrem name
inv.invokeFunction("hello", "Java Scripting API");
Volání metod na objektech
[editovat | editovat zdroj]Pokud námi zvolený dynamický jazyk podporuje objektově orientované programování, tak je možné využití objektů i pomocí Java Scripting API.
// Pomocí JavaScriptu vytvoříme objekt s metodou hello
String script = "var obj = new Object();";
script += "obj.hello = function(name) { print('Hello, ' + name); }";
scriptEngine.eval(script);
Invocable inv = (Invocable) scriptEngine;
// Získáme objekt, na kterém chceme volat metodu
Object obj = scriptEngine.get("obj");
// Zavoláme metodu "hello" na objektu "obj" s daným parametrem
inv.invokeMethod(obj, "hello", "Java Scripting API" );
Implementace rozhraní
[editovat | editovat zdroj]Další příklad nám ukáže jak můžeme vzít objekt ze skriptovacího jazyka a použít ho jako implementaci javovského rozhraní.
String script = "var obj = new Object();";
script += "obj.run = function() { println('run method called'); }";
scriptEngine.eval(script);
Object obj = scriptEngine.get("obj");
Invocable inv = (Invocable) scriptEngine;
// Získaný objekt vložíme jako implementaci "Runnable" rozhraní
Runnable r = inv.getInterface(obj, Runnable.class);
// Vytvoříme nové vlákno s implementací ze skriptovacího jazyka
Thread th = new Thread(r);
th.start();
Využití java tříd ve skriptovacím jazyce
[editovat | editovat zdroj]Poslední příklad ukazuje na využití javovských tříd ve skritovacím jazyce. Na příkladu je zdrojový kód z JavaScriptu, který následně lze snadno nahrát jako souboru a spustit ho. Výsledkem bude vytvoření okna a vypsání „hello“ na standardní výstup.
importPackage(java.awt);
importClass(java.awt.Frame);
var frame = new java.awt.Frame("hello");
frame.setVisible(true);
print(frame.title);
Další podporované dynamické jazyky
[editovat | editovat zdroj]Příklad dalších podporovaných dynamických jazyků, které mají vytvořeny svůj ScriptEngine, tudíž mohou interpretovány a stát se tak součástí business logiky aplikace.
JRuby, Groovy, BeanShell, JACL, Jaskell, Java
Jawk, Jelly, JEXL, Javascript, Jython, OGNL
EEP, xPath, XSLT, Pnuts, Scheme, sl
Literatura
[editovat | editovat zdroj]- BIEN, Adam. Real world Java EE patterns: rethinking best practices. S. l.: Press.adam-bien.com, 2009, 279 s. ISBN 978-0-557-07832-5.
- GROGAN, Mike. Scripting for Java Platform. 2006. Dostupné z: http://download.oracle.com/otndocs/jcp/java_scripting-1.0-fr-eval-oth-JSpec/
- Java Scripting Programmer's Guide. Java SE Documentation [online]. [cit. 2012-04-17]. Dostupné z: http://docs.oracle.com/javase/6/docs/technotes/guides/scripting/programmer_guide/index.html