Interpreter

Intenção

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.

Vantagens

Desvantagens

Exemplos

Exemplo #1

Exemplo #2

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.

Diagrama de Classe

Exemplo #2 - Diagrama

Participantes

  • AbstractExpression (Action): Declara a operação abstrata interpret que será implementada por todos os interpretadores
  • TerminalExpression (CommandAction): Implementa a operação interpret para os elementos terminais da gramática
  • NonterminalExpression (SequenceAction, WhileRepetitionAction, UntilRepetitionAction, AlternationAction): Implementa a operação interpret para elementos não terminais da gramática
  • Context (Object): Contém a informação global para o interpretador
  • Client (Client): Constrói a árvore sintática que representa a sentença na linguagem definida e invoca a operação interpret.

Código

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() +
                        "" +
                        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() +
                "" +
                        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() +
                "");

        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() +
                        "" +
                        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;
    }
}
Clique aqui para fazer o download do código completo de implementação deste Design Pattern.

Padrões Relacionados

Este Padrão pode ser usado para resolver os seguintes problemas: