Dada uma linguagem definir uma representação para a sua gramática juntamente com um interpretador que usa a representação para interpretar sentenças dessa linguagem.
Em certos domínios, é comum existirem processos com regras bem definidas que podem ser descritos através de workflows. Os workflows podem ser definidos através de uma gramática que inclui quatro elementos principais: um comando executável, uma sequência para executar dois ou mais comandos seguidos, uma alternativa para executar um ou outro comando com base em uma condição e uma repetição que executará um mesmo comando até que uma condição seja satisfeita. Devido a essa forma estruturada e predefinida, alguns workflows permitem a automação de seus comandos. Outra característica é a simplicidade de visualização dos workflows por humanos através de fluxogramas, além da facilidade de eles serem entendidos por outros softwares se convertidos para um formato como XML. Dessa forma, o workflow torna-se compartilhável e pode ser reaproveitado, por exemplo, caso uma filial da empresa possua um processo semelhante cujo comportamento poderia também ser automatizado. Uma interface Action define um elemento de um workflow e a operação interpret, que recebe como parâmetro um objeto que armazenará o estado do workflow, e generateXML que irá gerar uma String em formato XML do workflow. A classe CommandAction implementa Action e representa um comando executável, enquanto SequenceAction representa uma sequência de comandos e ConditionalAction, que é uma classe abstrata, representa comandos que dependem de uma condição. As três implementam a interface Action e, com exceção de ConditionalAction, implementam também as operações interpret e generateXML. WhileRepetitionAction, UntilRepetitionAction e AlternationAction herdam de ConditionalAction, onde os dois primeiros definem laços de repetição e o último uma alternação entre duas ações. Todas implementam as operações interpret e generateXML da interface Action. A classe Client é responsável por instanciar um workflow, além de chamar a operação interpret para executá-lo. As interfaces Command, referenciada por CommandAction, e BooleanExpression, referenciada por ConditionalAction, permitem implementar os comandos e as condições que podem ser definidas no workflow.
package interpreter;
// Classe abstrata que define uma ação que possui uma condição
public abstract class ConditionalAction implements Action {
// Ação que será realizada
private final Action action;
// Condição que definirá se a ação será executada ou não
private final BooleanExpression condition;
public ConditionalAction(Action action, BooleanExpression condition) {
this.action = action;
this.condition = condition;
}
public Action getAction() {
return action;
}
public BooleanExpression getCondition() {
return condition;
}
}
package interpreter;
// Interface que define uma ação de um workflow
public interface Action {
// operação de interpretação das ações do workflow
public void interpret(Object context);
// operação para geração do XML do workflow
public String generateXML();
}
package interpreter;
// Ação que define uma sequência de duas ações no workflow
public class SequenceAction implements Action {
// Primeira ação que será executada
private final Action firstAction;
// Segunda ação que será executada
private final Action secondAction;
public SequenceAction(Action firstAction, Action secondAction) {
this.firstAction = firstAction;
this.secondAction = secondAction;
}
// Implementação da operação interpret que executa as duas ações em sequência
public void interpret(Object context) {
this.firstAction.interpret(context);
this.secondAction.interpret(context);
}
// Implementação da operação generateXML que gerará o XML das duas ações
public String generateXML() {
String returnXML = "";
returnXML = returnXML.concat(
this.firstAction.generateXML() +
this.secondAction.generateXML());
return returnXML.concat(" ");
}
}
package interpreter;
// Definição de um comando realizado no alvo do workflow
public interface Command {
public void execute(Object context);
public Object getValue();
}
package interpreter;
// Definição da interface para uma condição booleana
public interface BooleanExpression {
public boolean evaluate(Object context);
public Object getParameter();
}
package interpreter;
// Implementação da ação que define um laço de repetição faça-enquanto
public class UntilRepetitionAction extends ConditionalAction {
public UntilRepetitionAction(Action action, BooleanExpression condition) {
super(action, condition);
}
// Implementação da operação interpret que interpreta a ação enquanto a condição for falsa
public void interpret(Object context) {
do {
this.getAction().interpret(context);
} while (!this.getCondition().evaluate(context));
}
// Implementação da operação que gera o XML da condição e da ação
public String generateXML() {
String returnXML = "";
String conditionName = this.getCondition().getClass().getSimpleName();
returnXML = returnXML.concat(
"<" + conditionName + ">" +
this.getCondition().getParameter().toString() +
"" + conditionName + ">" +
this.getAction().generateXML());
return returnXML.concat(" ");
}
}
package interpreter;
// Ação de alternativa do workflow
public class AlternationAction extends ConditionalAction {
// Ação realizada caso a condição seja falsa (se for null, nenhuma ação é executada)
private Action falseAction=null;
public AlternationAction(Action action, BooleanExpression condition, Action falseAction) {
super(action, condition);
this.falseAction = falseAction;
}
public AlternationAction(Action action, BooleanExpression condition) {
super(action, condition);
}
// Implementação da operação interpret
// Avalia a condição e executa a operação para true ou para false
public void interpret(Object context) {
if(this.getCondition().evaluate(context))
this.getAction().interpret(context);
else{
if(falseAction != null) this.falseAction.interpret(context);
}
}
// Implementação da geração do XML
// Gera o XML para a condição, para a ação true e para a ação false
public String generateXML() {
String returnXML = "";
String conditionName = this.getCondition().getClass().getSimpleName();
returnXML = returnXML.concat(
"<" + conditionName + ">" +
this.getCondition().getParameter().toString() +
"" + conditionName + ">" +
this.getAction().generateXML());
if(this.falseAction != null) {
returnXML = returnXML.concat(
"" + this.falseAction.generateXML() + " ");
}
return returnXML.concat(" ");
}
}
package interpreter;
// Implementação de uma ação terminal que realiza um comando
public class CommandAction implements Action {
// Comando que será executado
private Command command;
public CommandAction(Command command) {
this.command = command;
}
// Implementação da operação interpret que executará o comando
public void interpret(Object context) {
this.command.execute(context);
}
// Implementação da operação generateXML que irá gerar o XML do comando
public String generateXML() {
String returnXML = "";
String commandName = command.getClass().getSimpleName();
returnXML = returnXML.concat(
"<" + commandName + ">" +
this.command.getValue().toString() +
"" + commandName + ">");
return returnXML.concat(" ");
}
}
package interpreter;
// Implementação da ação que define um laço de repetição enquanto-faça
public class WhileRepetitionAction extends ConditionalAction {
public WhileRepetitionAction(Action action, BooleanExpression condition) {
super(action, condition);
}
// Implementação da operação interpret que interpreta a ação enquanto a condição for verdadeira
public void interpret(Object context) {
while (this.getCondition().evaluate(context)){
this.getAction().interpret(context);
}
}
// Implementação da operação que gera o XML da condição e da ação
public String generateXML() {
String returnXML = "";
String conditionName = this.getCondition().getClass().getSimpleName();
returnXML = returnXML.concat(
"<" + conditionName + ">" +
this.getCondition().getParameter().toString() +
"" + conditionName + ">" +
this.getAction().generateXML());
return returnXML.concat(" ");
}
}
package interpreter;
import interpreter.commands.AddTemperatureCommand;
import interpreter.commands.AddWaterLevelCommand;
import interpreter.commands.SetPumperOnCommand;
import interpreter.contexts.WaterPumperAgent;
import interpreter.expressions.MaxTemperatureExpression;
import interpreter.expressions.MaxWaterLevelExpression;
import interpreter.expressions.PumperOnExpression;
public class Client {
public static void main(String[] args) {
// Retorna uma ação inicial de um workflow
Action workflow = defineWorkflow();
// Instancia o context (objeto alvo do workflow)
WaterPumperAgent agent = new WaterPumperAgent(false, 0, 20);
// Exibe as informações do context antes da interpretação
System.out.println("Agent status:\nPumper on: " + agent.isPumperOn() +
"\nWater Level: " + agent.getWaterLevel() +
"\nTemperature: " + agent.getTemperature() + "\n");
// Execução da operação interpret do workflow
workflow.interpret(agent);
// Exibe as informações do context após a interpretação
System.out.println("Agent status:\nPumper on: " + agent.isPumperOn() +
"\nWater Level: " + agent.getWaterLevel() +
"\nTemperature: " + agent.getTemperature() + "\n");
// Exibe o workflow em formato XML
System.out.println(workflow.generateXML());
}
public static Action defineWorkflow() {
// Inicio do workflow
// Se a bomba estiver desligada, ligar bomba. Se não, fim do workflow.
// Enquanto o nivel da agua estiver abaixo de 10, aumentar o nivel da agua em 1 e a temperatura em 5
// Caso a temperatura seja superior a 50, desligar a bomba
// Fim do workflow
//commands
SetPumperOnCommand pumperOnCommand = new SetPumperOnCommand(true);
SetPumperOnCommand pumperOffCommand = new SetPumperOnCommand(false);
AddWaterLevelCommand addWaterLevelCommand = new AddWaterLevelCommand(1);
AddTemperatureCommand addTemperatureCommand = new AddTemperatureCommand(5);
//expressions
PumperOnExpression pumperNotOnExpression = new PumperOnExpression(false);
MaxWaterLevelExpression maxWaterLevelExpression = new MaxWaterLevelExpression(10);
MaxTemperatureExpression maxTemperatureExpression = new MaxTemperatureExpression(50);
// Definição do Workflow
// Elementos terminais
CommandAction turnOnPumper = new CommandAction(pumperOnCommand);
CommandAction turnOffPumper = new CommandAction(pumperOffCommand);
CommandAction addToTemperature = new CommandAction(addTemperatureCommand);
CommandAction addWaterLevel = new CommandAction(addWaterLevelCommand);
// Elementos não terminais
SequenceAction addWaterLevelThenAddTemperature = new SequenceAction(addWaterLevel, addToTemperature);
AlternationAction verifyTemperature = new AlternationAction(turnOffPumper, maxTemperatureExpression);
WhileRepetitionAction doUntilMaxWaterLevel = new WhileRepetitionAction(addWaterLevelThenAddTemperature, maxWaterLevelExpression);
SequenceAction startPumperProcess = new SequenceAction(doUntilMaxWaterLevel, verifyTemperature);
SequenceAction startWorkflow = new SequenceAction(turnOnPumper, startPumperProcess);
return new AlternationAction(startWorkflow, pumperNotOnExpression);
}
}
package interpreter.commands;
import interpreter.Command;
import interpreter.contexts.WaterPumperAgent;
public class AddTemperatureCommand implements Command {
private float temperature;
public AddTemperatureCommand(float temperature) {
this.temperature = temperature;
}
public void execute(Object context) {
WaterPumperAgent agent = (WaterPumperAgent) context;
agent.setTemperature(agent.getTemperature() + this.temperature);
}
public Object getValue() {
return this.temperature;
}
}
package interpreter.commands;
import interpreter.Command;
import interpreter.contexts.WaterPumperAgent;
public class AddWaterLevelCommand implements Command {
private int level;
public AddWaterLevelCommand(int level) {
this.level = level;
}
public void execute(Object context) {
WaterPumperAgent agent = (WaterPumperAgent) context;
agent.setWaterLevel(agent.getWaterLevel() + this.level);
}
public Object getValue() {
return this.level;
}
}
package interpreter.commands;
import interpreter.Command;
import interpreter.contexts.WaterPumperAgent;
public class SetPumperOnCommand implements Command {
private boolean pumperOn;
public SetPumperOnCommand(boolean pumperOn) {
this.pumperOn = pumperOn;
}
public void execute(Object context) {
WaterPumperAgent agent = (WaterPumperAgent)context;
agent.setPumperOn(this.pumperOn);
}
public Object getValue() {
return this.pumperOn;
}
}
package interpreter.expressions;
import interpreter.BooleanExpression;
import interpreter.contexts.WaterPumperAgent;
public class PumperOnExpression implements BooleanExpression {
private final boolean pumperIsOn;
public PumperOnExpression(boolean pumperIsOn) {
this.pumperIsOn = pumperIsOn;
}
public boolean evaluate(Object context) {
WaterPumperAgent agent = (WaterPumperAgent)context;
return agent.isPumperOn() == pumperIsOn;
}
public Object getParameter() {
return this.pumperIsOn;
}
}
package interpreter.expressions;
import interpreter.BooleanExpression;
import interpreter.contexts.WaterPumperAgent;
public class MaxWaterLevelExpression implements BooleanExpression {
private final int level;
public MaxWaterLevelExpression(int level) {
this.level = level;
}
public boolean evaluate(Object context) {
WaterPumperAgent agent = (WaterPumperAgent)context;
return agent.getWaterLevel() < level;
}
public Object getParameter() {
return this.level;
}
}
package interpreter.expressions;
import interpreter.BooleanExpression;
import interpreter.contexts.WaterPumperAgent;
public class MaxTemperatureExpression implements BooleanExpression {
private final float temperature;
public MaxTemperatureExpression(float temperature) {
this.temperature = temperature;
}
public boolean evaluate(Object context) {
WaterPumperAgent agent = (WaterPumperAgent) context;
return agent.getTemperature() >= temperature;
}
public Object getParameter() {
return this.temperature;
}
}
package interpreter.contexts;
public class WaterPumperAgent {
private boolean pumperOn;
private int waterLevel;
private float temperature;
public WaterPumperAgent(boolean pumperOn, int waterLevel, float temperature) {
this.pumperOn = pumperOn;
this.waterLevel = waterLevel;
this.temperature = temperature;
}
public boolean isPumperOn() {
return pumperOn;
}
public void setPumperOn(boolean pumperOn) {
this.pumperOn = pumperOn;
}
public int getWaterLevel() {
return waterLevel;
}
public void setWaterLevel(int waterLevel) {
this.waterLevel = waterLevel;
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
}