[Patrones de diseño - Notas de estudio] 23 Patrones de diseño - Intérprete en modo intérprete (explicación del principio + introducción al escenario de la aplicación + introducción al caso + implementación del código Java)

Introducción del caso

Implemente cuatro operaciones aritméticas a través del modo intérprete, como a+b-cvalores calculados, requisitos específicos

  • Primero ingrese la forma de la expresión, por ejemplo a+b+c-d+e, se requiere que las letras de la expresión no se puedan repetir.
  • a,b,c,d,eValores ingresados ​​por separado
  • Finalmente encuentra el resultado.

Insertar descripción de la imagen aquí

solución tradicional

  • Escriba un método para recibir la forma de una expresión y luego analícelo según el valor ingresado por el usuario para obtener el resultado.

【analizar】

Si se agregan nuevos operadores, como * o /, etc., no favorecerá la expansión. Además, permitir el análisis de un método hará que la estructura del programa sea confusa y poco clara.

【Mejorar】

Considere usar el modo intérprete, es decir表达式->解释器(可以有多种解释器)->结果

introducir

introducción básica

  • En el modo intérprete, el problema a resolver por el programa se expresará en un "minilenguaje" muy simple, es decir, un miniprograma escrito en el "minilenguaje" expresa el problema específico. El miniprograma no puede funcionar solo, también necesitamos escribir un programa responsable del "intérprete" en lenguaje Java. El traductor comprende el minilenguaje, lo interpreta y finalmente ejecuta el miniprograma. Este programa de traducción también se llama intérprete. De esta manera, cuando el problema a resolver cambia, no es necesario modificar el programa en lenguaje Java, solo es necesario modificar el mini programa en lenguaje para solucionarlo.
  • En el principio de compilación, una expresión aritmética se forma en unidades léxicas mediante un analizador léxico, y luego estas unidades léxicas se construyen mediante un analizador de sintaxis para construir un árbol de análisis de sintaxis y, finalmente, se forma un árbol de análisis de sintaxis abstracta. Aquí tanto el analizador léxico como el analizador de sintaxis pueden considerarse intérpretes.

Escenarios de aplicación

  • Las oraciones en un lenguaje que necesitan ser interpretadas y ejecutadas se pueden representar como un árbol de sintaxis abstracta. Algunos problemas recurrentes se pueden expresar en un lenguaje simple, como los siguientes escenarios: compiladores, cálculo de expresiones operativas, expresiones regulares, instrucciones de robots… …

Caracteres

Insertar descripción de la imagen aquí

  • AbstractExpression(抽象表达式): Expresión abstracta, que declara una operación de interpretación abstracta (que define la interfaz común de los nodos del árbol de sintaxis). Este método es compartido por todos los nodos en el árbol de sintaxis abstracta. El método puede denominarse parse/interpreter, que se traduce como análisis/traducción.
  • TerminalExpression(终结符表达式): Es una expresión terminal e implementa operaciones de interpretación relacionadas con símbolos terminales en la gramática.
  • NonTermialExpression(非终结符表达式): Es una expresión no terminal e implementa operaciones de interpretación para símbolos no terminales en la gramática.
  • Context(上下文): Es una función ambiental que contiene información global fuera del intérprete y proporciona la información necesaria para que el intérprete realice el análisis gramatical.
  • Client(请求者): Llame a TerminalExpression y NonTermialExpression para derivar el árbol de sintaxis

Implementación de casos

Caso numero uno

Diagrama de clase

Insertar descripción de la imagen aquí

lograr

【Expresión】

package com.atguigu.interpreter;

import java.util.HashMap;

/**
 * 抽象类表达式,通过HashMap键值对, 可以获取到变量的值
 *
 * @author Administrator
 *
 */
public abstract class Expression {
    
    

   /**
    * 如表达式是:a + b - c ,key就是公式(表达式)的参数a、b、c, value就是就是具体值
    * 实例:HashMap {a=10, b=20}
    * @param var
    * @return
    */
   public abstract int interpreter(HashMap<String, Integer> var);
}

【Analizador de variables】

package com.atguigu.interpreter;

import java.util.HashMap;


/**
 * 变量的解释器
 * @author Administrator
 *
 */
public class VarExpression extends Expression {
    
    

   /**
    * key=a,key=b,key=c
    */
   private String key;

   public VarExpression(String key) {
    
    
      this.key = key;
   }

   /**
    * var 就是{a=10, b=20}
    * interpreter的功能就是根据变量名称来返回对应值
    * @param var
    * @return
    */
   @Override
   public int interpreter(HashMap<String, Integer> var) {
    
    
      return var.get(this.key);
   }
}

[Intérprete de símbolos aritméticos abstractos]

package com.atguigu.interpreter;

import java.util.HashMap;

/**
 * 抽象运算符号解析器
 * 每个运算符号,都只和自己左右两个数字有关系,
 * 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是Expression类的实现类
 *
 * @author Administrator
 *
 */
public class SymbolExpression extends Expression {
    
    

   protected Expression left;
   protected Expression right;

   public SymbolExpression(Expression left, Expression right) {
    
    
      this.left = left;
      this.right = right;
   }

   /**
    * 因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现
    * @param var
    * @return
    */
   @Override
   public int interpreter(HashMap<String, Integer> var) {
    
    
      // 默认实现
      return 0;
   }
}

[Intérprete de símbolos de operación específica: intérprete de suma]

package com.atguigu.interpreter;

import java.util.HashMap;

/**
 * 加法解释器
 * @author Administrator
 *
 */
public class AddExpression extends SymbolExpression  {
    
    

   public AddExpression(Expression left, Expression right) {
    
    
      super(left, right);
   }

   /**
    * 处理相加
    * var 仍然是 {a=10,b=20}..
    * @param var
    * @return
    */
   public int interpreter(HashMap<String, Integer> var) {
    
    
      // super.left.interpreter(var):返回 left 表达式对应的值 a = 10
      // super.right.interpreter(var): 返回 right 表达式对应值 b = 20
      // 将运算左表达式的值和右表达式相加
      return super.left.interpreter(var) + super.right.interpreter(var);
   }
}

[Intérprete de símbolos de operación específica: intérprete de resta]

package com.atguigu.interpreter;

import java.util.HashMap;

/**
 * 减法解释器
 */
public class SubExpression extends SymbolExpression {
    
    

    public SubExpression(Expression left, Expression right) {
    
    
        super(left, right);
    }

    /**
     * 求出left 和 right 表达式相减后的结果
     *
     * @param var
     * @return
     */
    public int interpreter(HashMap<String, Integer> var) {
    
    
        return super.left.interpreter(var) - super.right.interpreter(var);
    }
}

【calculadora】

package com.atguigu.interpreter;

import java.util.HashMap;
import java.util.Stack;

public class Calculator {
    
    

   /**
    * 定义表达式
    */
   private Expression expression;

   /**
    * 构造函数传参,解析字符串生成表达式
    * @param expStr
    */
   public Calculator(String expStr) {
    
    
      // 如 expStr = a+b

      // 安排运算先后顺序
      Stack<Expression> stack = new Stack<>();
      // 表达式拆分成字符数组,变成[a, +, b]
      char[] charArray = expStr.toCharArray();

      Expression left = null;
      Expression right = null;
      //遍历我们的字符数组, 即遍历  [a, +, b]
      //针对不同的情况,做处理
      for (int i = 0; i < charArray.length; i++) {
    
    
         switch (charArray[i]) {
    
    
            case '+':
               // 从stack取出左表达式 "a"
               left = stack.pop();
               // 取出右表达式 "b"
               right = new VarExpression(String.valueOf(charArray[++i]));
               // 然后根据得到left和right构建AddExpresson加入stack
               stack.push(new AddExpression(left, right));
               break;
            case '-':
               left = stack.pop();
               right = new VarExpression(String.valueOf(charArray[++i]));
               stack.push(new SubExpression(left, right));
               break;
            default:
               //如果是一个 Var 就创建要给 VarExpression 对象,并push到stack
               stack.push(new VarExpression(String.valueOf(charArray[i])));
               break;
         }
      }
      //当遍历完整个charArray数组后,stack就得到最终的Expression
      this.expression = stack.pop();
   }

   public int run(HashMap<String, Integer> var) {
    
    
      //最后将表达式 a+b 和 var={a=10,b=20}
      //然后传递给expression的interpreter进行解释执行
      return this.expression.interpreter(var);
   }
}

【Cliente】

package com.atguigu.interpreter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;

public class ClientTest {
    
    

    public static void main(String[] args) throws IOException {
    
    
        // a+b
        String expStr = getExpStr();
        // var {a=10, b=20}
        HashMap<String, Integer> var = getValue(expStr);
        Calculator calculator = new Calculator(expStr);
        System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
    }

    /**
     * 获得表达式
     *
     * @return
     * @throws IOException
     */
    public static String getExpStr() throws IOException {
    
    
        System.out.print("请输入表达式:");
        return (new BufferedReader(new InputStreamReader(System.in))).readLine();
    }

    /**
     * 获得值映射
     *
     * @param expStr
     * @return
     * @throws IOException
     */
    public static HashMap<String, Integer> getValue(String expStr) throws IOException {
    
    
        HashMap<String, Integer> map = new HashMap<>();

        for (char ch : expStr.toCharArray()) {
    
    
            if (ch != '+' && ch != '-') {
    
    
                if (!map.containsKey(String.valueOf(ch))) {
    
    
                    System.out.print("请输入" + String.valueOf(ch) + "的值:");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    map.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }

        return map;
    }
}

【correr】

Connected to the target VM, address: '127.0.0.1:4322', transport: 'socket'
请输入表达式:a+b
请输入a的值:10
请输入b的值:20
运算结果:a+b=30
Disconnected from the target VM, address: '127.0.0.1:4322', transport: 'socket'

Process finished with exit code 0

【Proceso de implementación】

  • Primer bucle: poner el analizador de variables a en la pila
  • Segundo ciclo: tome la expresión izquierda "a" de la pila, luego obtenga y genere una nueva expresión "b" de la matriz, y finalmente construya la expresión suma "a+b" y guárdela en la pila

Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí

Caso 2

ilustrar

Hay un automóvil y necesita escribir un pequeño programa simple para controlar el movimiento del automóvil. Por ejemplo program go right go right go right go right end, después de que el automóvil reciba instrucciones, seguirá la siguiente trayectoria

Insertar descripción de la imagen aquí

Diagrama de clase

Insertar descripción de la imagen aquí

lograr

[Expresión abstracta: Nodo]

package com.atguigu.interpreter.Sample;

/**
 * 语法树中各个部分(节点)中最顶层的类
 */
public abstract class Node {
    
    
    /**
     * 进行语法解析处理
     *
     * @param context 语法解析上下文的类
     * @throws ParseException
     */
    public abstract void parse(Context context) throws ParseException;
}

[Excepción de análisis personalizado]

package com.atguigu.interpreter.Sample;

public class ParseException extends Exception {
    
    
    public ParseException(String msg) {
    
    
        super(msg);
    }
}

[Expresión de terminador: PrimitiveCommandNode]

Expresión terminal: no continuará ninguna expansión adicional y se llamará al método de análisis.

package com.atguigu.interpreter.Sample;

// <primitive command> ::= go | right | left
public class PrimitiveCommandNode extends Node {
    
    
    /**
     * 记录 指令的名字 如 go left right
     */
    private String name;

    /**
     * PrimitiveCommandNode 的 parse 方法没有调用其他类的parse方法
     * @param context 语法解析上下文的类
     * @throws ParseException
     */
    public void parse(Context context) throws ParseException {
    
    
        // 记录指令的名字
        name = context.currentToken();
        context.skipToken(name);
        if (!name.equals("go") && !name.equals("right") && !name.equals("left")) {
    
    
            throw new ParseException(name + " is undefined");
        }
    }

    public String toString() {
    
    
        return name;
    }
}

[Expresión no terminal: ProgramNode]

package com.atguigu.interpreter.Sample;

// <program> ::= program <command list>
public class ProgramNode extends Node {
    
    
    private Node commandListNode;
    public void parse(Context context) throws ParseException {
    
    
        // 迷你语法最开始会出现单词program,这行代码可以跳过 program 这个标记
        // 比如一开始context的值是program end,那么currentToken的值就是program,执行context.skipToken("program")后,currentToken的值变成end
        context.skipToken("program");
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }
    public String toString() {
    
    
        // 等效于  return "[program " + commandListNode.toString() + "]";
        return "[program " + commandListNode + "]";
    }
}

[Expresión no terminal: CommandNode]

package com.atguigu.interpreter.Sample;

// <command> ::= <repeat command> | <primitive command>
public class CommandNode extends Node {
    
    
    private Node node;
    public void parse(Context context) throws ParseException {
    
    
        if (context.currentToken().equals("repeat")) {
    
    
            // 使用repeat解析器
            node = new RepeatCommandNode();
            node.parse(context);
        } else {
    
    
            // 使用指令解释器,解析go left right等指令
            node = new PrimitiveCommandNode();
            node.parse(context);
        }
    }
    public String toString() {
    
    
        return node.toString();
    }
}

[Expresión no terminal: CommandListNode]

package com.atguigu.interpreter.Sample;

import java.util.ArrayList;

// <command list> ::= <command>* end
public class CommandListNode extends Node {
    
    
    /**
     * 保存多个命令
     */
    private ArrayList list = new ArrayList();
    public void parse(Context context) throws ParseException {
    
    
        while (true) {
    
    
            if (context.currentToken() == null) {
    
    
                // 如果context.currentToken() == null,表示后面没有任何标记了(即已经解析到迷你程序的末尾),说明缺少了end,抛出异常
                throw new ParseException("Missing 'end'");
            } else if (context.currentToken().equals("end")) {
    
    
                // 如果当前的标记是end,表示已经解析至末尾,end不需要执行,直接跳过即可
                context.skipToken("end");
                // 到了end,解析已经完成了,退出循环即可
                break;
            } else {
    
    
                // 当前标记不是end,则是其他需要解析的标记
                Node commandNode = new CommandNode();
                // 解析标记
                commandNode.parse(context);
                list.add(commandNode);
            }
        }
    }
    public String toString() {
    
    
        return list.toString();
    }
}

[Expresión no terminal: RepeatCommandNode]

package com.atguigu.interpreter.Sample;

// <repeat command> ::= repeat <number> <command list>
public class RepeatCommandNode extends Node {
    
    
    /**
     * 循环调用的次数
     */
    private int number;
    private Node commandListNode;
    public void parse(Context context) throws ParseException {
    
    
        context.skipToken("repeat");
        number = context.currentNumber();
        context.nextToken();
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }
    public String toString() {
    
    
        return "[repeat " + number + " " + commandListNode + "]";
    }
}

【Contexto】

package com.atguigu.interpreter.Sample;

import java.util.StringTokenizer;

/**
 * 该类提供语法解析需要的方法
 */
public class Context {
    
    
    /**
     * 使用java.util.stringTokenizer类来简化程序,它会将接收到的字符串分割为标记。
     * 在分割字符串时使用的分隔符是空格“”、制表符“\t”、换行符“\n”回车符“\r”、换页符“\f”
     */
    private StringTokenizer tokenizer;
    private String currentToken;

    public Context(String text) {
    
    
        tokenizer = new StringTokenizer(text);
        nextToken();
    }

    /**
     * 获取下一个标记
     *
     * @return
     */
    public String nextToken() {
    
    
        // 当判断还有下一个标记时,就获取下一个标记
        if (tokenizer.hasMoreTokens()) {
    
    
            currentToken = tokenizer.nextToken();
        } else {
    
    
            currentToken = null;
        }
        return currentToken;
    }

    /**
     * 返回当前的标记
     * @return
     */
    public String currentToken() {
    
    
        return currentToken;
    }

    /**
     * 跳过标记
     *
     * @param token
     * @throws ParseException
     */
    public void skipToken(String token) throws ParseException {
    
    
        if (!token.equals(currentToken)) {
    
    
            throw new ParseException("Warning: " + token + " is expected, but " + currentToken + " is found.");
        }
        nextToken();
    }

    /**
     * 读取数字
     *
     * @return
     * @throws ParseException
     */
    public int currentNumber() throws ParseException {
    
    
        int number = 0;
        try {
    
    
            number = Integer.parseInt(currentToken);
        } catch (NumberFormatException e) {
    
    
            throw new ParseException("Warning: " + e);
        }
        return number;
    }
}

【Cliente: Principal】

package com.atguigu.interpreter.Sample;

import java.io.BufferedReader;
import java.io.FileReader;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            BufferedReader reader = new BufferedReader(new FileReader("src/com/atguigu/interpreter/Sample/program.txt"));
            String text;
            while ((text = reader.readLine()) != null) {
    
    
                System.out.println("迷你程序 = \"" + text + "\"");
                Node node = new ProgramNode();
                node.parse(new Context(text));
                System.out.println("语法解析结果 = " + node);
                System.out.println();
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

【programa.txt】

program end
program go end
program go right go right go right go right end
program repeat 4 go right end end
program repeat 4 repeat 3 go right go left end right end end

【correr】

迷你程序 = "program end"
语法解析结果 = [program []]

迷你程序 = "program go end"
语法解析结果 = [program [go]]

迷你程序 = "program go right go right go right go right end"
语法解析结果 = [program [go, right, go, right, go, right, go, right]]

迷你程序 = "program repeat 4 go right end end"
语法解析结果 = [program [[repeat 4 [go, right]]]]

迷你程序 = "program repeat 4 repeat 3 go right go left end right end end"
语法解析结果 = [program [[repeat 4 [[repeat 3 [go, right, go, left]], right]]]]


Process finished with exit code 0

Insertar descripción de la imagen aquí

expandir

  • La función del programa anterior es solo analizar el mini programa y en realidad no ejecuta las instrucciones. Lo siguiente continuará mejorando el programa para que el automóvil realmente pueda ejecutarse de acuerdo con las instrucciones.
  • El siguiente código es de hecho un poco complicado. El código no solo usa el modo de intérprete, sino que también usa el modo de apariencia para facilitar el uso del intérprete. Además, también usa el modo de método de fábrica para proporcionar métodos para generar la ejecución correspondiente de acuerdo con al nombre de la instrucción createExecutor(String name). Tómese su tiempo para apreciarlo.

【Excepción de análisis】

package com.atguigu.interpreter.A1.language;

public class ParseException extends Exception {
    
    
    public ParseException(String msg) {
    
    
        super(msg);
    }
}

【Ejecutar excepción】

package com.atguigu.interpreter.A1.language;

public class ExecuteException extends Exception {
    
    
    public ExecuteException(String msg) {
    
    
        super(msg);
    }
}

【Nodo】

package com.atguigu.interpreter.A1.language;

/**
 * 实现Executor执行器接口
 */
public abstract class Node implements Executor {
    
    
    public abstract void parse(Context context) throws ParseException;
}

【Nodo de programa】

package com.atguigu.interpreter.A1.language;

public class ProgramNode extends Node {
    
    
    private Node commandListNode;

    public void parse(Context context) throws ParseException {
    
    
        context.skipToken("program");
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }

    public void execute() throws ExecuteException {
    
    
        // 连续执行多个指令 的 execute方法
        commandListNode.execute();
    }

    public String toString() {
    
    
        return "[program " + commandListNode + "]";
    }
}

【Nodo de comando】

package com.atguigu.interpreter.A1.language;

public class CommandNode extends Node {
    
    
    private Node node;

    public void parse(Context context) throws ParseException {
    
    
        if (context.currentToken().equals("repeat")) {
    
    
            node = new RepeatCommandNode();
            node.parse(context);
        } else {
    
    
            node = new PrimitiveCommandNode();
            node.parse(context);
        }
    }

    /**
     * 直接调用 RepeatCommandNode 和 PrimitiveCommandNode 的执行器
     * @throws ExecuteException
     */
    public void execute() throws ExecuteException {
    
    
        node.execute();
    }

    public String toString() {
    
    
        return node.toString();
    }
}

【Nodo de lista de comandos】

package com.atguigu.interpreter.A1.language;

import java.util.ArrayList;
import java.util.Iterator;


public class CommandListNode extends Node {
    
    
    private ArrayList list = new ArrayList();

    public void parse(Context context) throws ParseException {
    
    
        while (true) {
    
    
            if (context.currentToken() == null) {
    
    
                throw new ParseException("Missing 'end'");
            } else if (context.currentToken().equals("end")) {
    
    
                context.skipToken("end");
                break;
            } else {
    
    
                Node commandNode = new CommandNode();
                commandNode.parse(context);
                list.add(commandNode);
            }
        }
    }

    /**
     * 使用迭代器来自动执行指令
     *
     * @throws ExecuteException
     */
    public void execute() throws ExecuteException {
    
    
        Iterator it = list.iterator();
        while (it.hasNext()) {
    
    
            ((CommandNode) it.next()).execute();
        }
    }

    public String toString() {
    
    
        return list.toString();
    }
}

【Nodo de comando primitivo】

package com.atguigu.interpreter.A1.language;
public class PrimitiveCommandNode extends Node {
    
    
    private String name;
    private Executor executor;
    public void parse(Context context) throws ParseException {
    
    
        name = context.currentToken();
        context.skipToken(name);
        // 根据指令名称来找工厂获取相应的执行器
        executor = context.createExecutor(name);
    }
    public void execute() throws ExecuteException {
    
    
        if (executor == null) {
    
    
            throw new ExecuteException(name + ": is not defined");
        } else {
    
    
            executor.execute();
        }
    }
    public String toString() {
    
    
        return name;
    }
}

【Repetir nodo de comando】

package com.atguigu.interpreter.A1.language;

public class RepeatCommandNode extends Node {
    
    
    private int number;
    private Node commandListNode;

    public void parse(Context context) throws ParseException {
    
    
        context.skipToken("repeat");
        number = context.currentNumber();
        context.nextToken();
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }

    public void execute() throws ExecuteException {
    
    
        // 循环执行指令
        for (int i = 0; i < number; i++) {
    
    
            commandListNode.execute();
        }
    }

    public String toString() {
    
    
        return "[repeat " + number + " " + commandListNode + "]";
    }
}

【Contexto】

package com.atguigu.interpreter.A1.language;

import java.util.StringTokenizer;

public class Context implements ExecutorFactory {
    
    
    /**
     * 组合工厂类
     */
    private ExecutorFactory factory;
    private StringTokenizer tokenizer;
    private String currentToken;

    public Context(String text) {
    
    
        tokenizer = new StringTokenizer(text);
        nextToken();
    }

    public String nextToken() {
    
    
        if (tokenizer.hasMoreTokens()) {
    
    
            currentToken = tokenizer.nextToken();
        } else {
    
    
            currentToken = null;
        }
        return currentToken;
    }

    public String currentToken() {
    
    
        return currentToken;
    }

    public void skipToken(String token) throws ParseException {
    
    
        if (!token.equals(currentToken)) {
    
    
            throw new ParseException("Warning: " + token + " is expected, but " + currentToken + " is found.");
        }
        nextToken();
    }

    public int currentNumber() throws ParseException {
    
    
        int number = 0;
        try {
    
    
            number = Integer.parseInt(currentToken);
        } catch (NumberFormatException e) {
    
    
            throw new ParseException("Warning: " + e);
        }
        return number;
    }

    /**
     * 设置工厂
     * @param factory
     */
    public void setExecutorFactory(ExecutorFactory factory) {
    
    
        this.factory = factory;
    }

    /**
     * 使用工厂的方法来创建具体的执行器
     * @param name
     * @return
     */
    public Executor createExecutor(String name) {
    
    
        // 后面的终结符
        return factory.createExecutor(name);
    }
}

【Ejecutor】

package com.atguigu.interpreter.A1.language;

/**
 * 外观对象的窗口接口
 */
public interface Executor {
    
    
    /**
     * 向系统外部提供一个接口
     * @throws ExecuteException
     */
    public abstract void execute() throws ExecuteException;
}

【IntérpreteFachada】

package com.atguigu.interpreter.A1.language;

public class InterpreterFacade implements Executor {
    
    
    private ExecutorFactory factory;
    private Context context;
    private Node programNode;

    public InterpreterFacade(ExecutorFactory factory) {
    
    
        this.factory = factory;
    }

    /**
     * 提供给外层访问的解析接口
     * @param text
     * @return
     */
    public boolean parse(String text) {
    
    
        boolean ok = true;
        this.context = new Context(text);
        this.context.setExecutorFactory(factory);
        this.programNode = new ProgramNode();
        try {
    
    
            // 开始解析
            programNode.parse(context);
            System.out.println(programNode.toString());
        } catch (ParseException e) {
    
    
            e.printStackTrace();
            ok = false;
        }
        return ok;
    }

    public void execute() throws ExecuteException {
    
    
        try {
    
    
            // 开始执行程序
            programNode.execute();
        } catch (ExecuteException e) {
    
    
            e.printStackTrace();
        }
    }
}

【Fábrica de ejecutores】

package com.atguigu.interpreter.A1.language;

public interface ExecutorFactory {
    
    
    /**
     * 创建一个执行器
     * @param name
     * @return
     */
    public abstract Executor createExecutor(String name);
}

【Lienzo de tortuga】

package com.atguigu.interpreter.A1.turtle;


import com.atguigu.interpreter.A1.language.ExecuteException;
import com.atguigu.interpreter.A1.language.Executor;
import com.atguigu.interpreter.A1.language.ExecutorFactory;

import java.awt.*;

public class TurtleCanvas extends Canvas implements ExecutorFactory {
    
    
    /**
     * 前进时的长度单位
     */
    final static int UNIT_LENGTH = 30;
    /**
     * 上方
     */
    final static int DIRECTION_UP = 0;
    /**
     * 右方
     */
    final static int DIRECTION_RIGHT = 3;
    /**
     * 下方
     */
    final static int DIRECTION_DOWN = 6;
    /**
     * 左方
     */
    final static int DIRECTION_LEFT = 9;
    /**
     * 右转
     */
    final static int RELATIVE_DIRECTION_RIGHT = 3;
    /**
     * 左转
     */
    final static int RELATIVE_DIRECTION_LEFT = -3;
    /**
     * 半径
     */
    final static int RADIUS = 3;
    /**
     * 移动方向
     */
    private int direction = 0;
    /**
     * 小车的定位
     */
    private Point position;
    private Executor executor;

    public TurtleCanvas(int width, int height) {
    
    
        // 设置画布尺寸
        setSize(width, height);
        initialize();
    }

    public void setExecutor(Executor executor) {
    
    
        this.executor = executor;
    }

    /**
     * 修改小车的行驶方向
     *
     * @param relativeDirection
     */
    void setRelativeDirection(int relativeDirection) {
    
    
        setDirection(direction + relativeDirection);
    }

    void setDirection(int direction) {
    
    
        if (direction < 0) {
    
    
            direction = 12 - (-direction) % 12;
        } else {
    
    
            direction = direction % 12;
        }
        this.direction = direction % 12;
    }

    /**
     * 让小车移动
     *
     * @param length
     */
    void go(int length) {
    
    
        int newx = position.x;
        int newy = position.y;
        switch (direction) {
    
    
            case DIRECTION_UP:
                newy -= length;
                break;
            case DIRECTION_RIGHT:
                newx += length;
                break;
            case DIRECTION_DOWN:
                newy += length;
                break;
            case DIRECTION_LEFT:
                newx -= length;
                break;
            default:
                break;
        }
        Graphics g = getGraphics();
        if (g != null) {
    
    
            g.drawLine(position.x, position.y, newx, newy);
            g.fillOval(newx - RADIUS, newy - RADIUS, RADIUS * 2 + 1, RADIUS * 2 + 1);
        }
        position.x = newx;
        position.y = newy;
    }

    /**
     * 使用工厂模式根据指令名称创建一个对应的执行器,并将其赋值给Executor
     *
     * @param name
     * @return
     */
    public Executor createExecutor(String name) {
    
    
        if (name.equals("go")) {
    
    
            return new GoExecutor(this);
        } else if (name.equals("right")) {
    
    
            return new DirectionExecutor(this, RELATIVE_DIRECTION_RIGHT);
        } else if (name.equals("left")) {
    
    
            return new DirectionExecutor(this, RELATIVE_DIRECTION_LEFT);
        } else {
    
    
            return null;
        }
    }

    /**
     * 初始化
     */
    public void initialize() {
    
    
        Dimension size = getSize();
        // 将小车的初始位置放在画布的中心
        position = new Point(size.width / 2, size.height / 2);
        direction = 0;
        // 设置路径的颜色
        setForeground(Color.red);
        // 设置画布的背景颜色
        setBackground(Color.white);
        Graphics g = getGraphics();
        if (g != null) {
    
    
            // 清空画布
            g.clearRect(0, 0, size.width, size.height);
        }
    }

    /**
     * 绘制图像
     *
     * @param g the specified Graphics context
     */
    public void paint(Graphics g) {
    
    
        initialize();
        if (executor != null) {
    
    
            try {
    
    
                // 执行 执行器的方法 控制小车运动
                executor.execute();
            } catch (ExecuteException e) {
    
    
            }
        }
    }
}

abstract class TurtleExecutor implements Executor {
    
    
    protected TurtleCanvas canvas;

    public TurtleExecutor(TurtleCanvas canvas) {
    
    
        this.canvas = canvas;
    }

    public abstract void execute();
}

/**
 * 具体执行器:前进
 */
class GoExecutor extends TurtleExecutor {
    
    
    public GoExecutor(TurtleCanvas canvas) {
    
    
        super(canvas);
    }

    public void execute() {
    
    
        // 调用前进方法在画布中绘制小车的前进路径
        canvas.go(TurtleCanvas.UNIT_LENGTH);
    }
}

/**
 * 具体执行器:切换方向
 */
class DirectionExecutor extends TurtleExecutor {
    
    
    private int relativeDirection;

    public DirectionExecutor(TurtleCanvas canvas, int relativeDirection) {
    
    
        super(canvas);
        this.relativeDirection = relativeDirection;
    }

    public void execute() {
    
    
        // 修改小车的方向
        canvas.setRelativeDirection(relativeDirection);
    }
}

【Principal】

package com.atguigu.interpreter.A1;


import com.atguigu.interpreter.A1.language.InterpreterFacade;
import com.atguigu.interpreter.A1.turtle.TurtleCanvas;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class Main extends Frame implements ActionListener {
    
    
    private TurtleCanvas canvas = new TurtleCanvas(400, 400);
    private InterpreterFacade facade = new InterpreterFacade(canvas);
    /**
     * 默认的迷你程序
     */
    private TextField programTextField = new TextField("program repeat 3 go right go left end end");

    /**
     * 构造函数
     *
     * @param title
     */
    public Main(String title) {
    
    
        super(title);

        canvas.setExecutor(facade);

        setLayout(new BorderLayout());

        programTextField.addActionListener(this);

        this.addWindowListener(new WindowAdapter() {
    
    
            public void windowClosing(WindowEvent e) {
    
    
                System.exit(0);
            }
        });
        // 将文本输入框添加到布局的上部分
        add(programTextField, BorderLayout.NORTH);
        // 将画布放在布局的中心
        add(canvas, BorderLayout.CENTER);
        pack();
        parseAndExecute();
        show();
    }

    /**
     * 供ActionListener用,监听用户的输入,当用户输入完成并按下回车之后,方法被执行
     *
     * @param e
     */
    public void actionPerformed(ActionEvent e) {
    
    
        if (e.getSource() == programTextField) {
    
    
            parseAndExecute();
        }
    }

    /**
     * 解析迷你程序成指令,并执行指令
     */
    private void parseAndExecute() {
    
    
        // 获取用户输入的迷你程序
        String programText = programTextField.getText();
        System.out.println("programText = " + programText);
        // 直接调用外观对象所提供的上层接口来使用解释器模式来解析迷你程序
        facade.parse(programText);
        // 重新绘制结果
        canvas.repaint();
    }

    public static void main(String[] args) {
    
    
        new Main("Interpreter Pattern Sample");
    }
}

【correr】

Insertar descripción de la imagen aquí

Aplicación del patrón de intérprete en Spring Framework.

package com.atguigu.spring.test;

import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class Interpreter {
    
    

   public static void main(String[] args) {
    
    
      //创建一个 Parser 对象
      SpelExpressionParser parser = new SpelExpressionParser();
      //通过 Parser 对象 获取到一个Expression对象
      //会根据不同的 Parser 对象 ,返回不同的 Expression 对象
      Expression expression = parser.parseExpression("10 * (2 + 1) * 1 + 66"); //结果:96
      int result = (Integer) expression.getValue();
      System.out.println(result);
   }

}

Insertar descripción de la imagen aquí

Subclase de expresión

Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí

【ilustrar】

  • La interfaz Expression es una interfaz de expresión y hay diferentes clases de implementación debajo de ella, como SpelExpression o CompositeStringExpression.
  • Cuando se utiliza, se devuelven diferentes objetos de expresión en función de los diferentes objetos de analizador que cree.

Insertar descripción de la imagen aquí

  • Finalmente, use el objeto Expression obtenido para llamar a su getValue para interpretar y ejecutar la expresión para obtener el resultado.

Resumir

【ventaja】

  • Cuando hay un lenguaje que necesita ser interpretado y ejecutado, las oraciones en el lenguaje se pueden representar como un árbol de sintaxis abstracta, puede considerar usar el modo intérprete para que el programa tenga una buena escalabilidad.

【defecto】

  • El modo de intérprete provocará la expansión de clases. El modo de intérprete utiliza métodos de llamada recursivos, lo que complicará mucho la depuración y puede reducir la eficiencia.

Descripción del articulo

  • Este artículo son mis notas de estudio para estudiar Shang Silicon Valley. La mayor parte del contenido del artículo proviene de videos de Shang Silicon Valley (haga clic para conocer los cursos relacionados con Shang Silicon Valley ), y parte del contenido proviene de mi propio pensamiento. artículo para ayudar a otras personas a estudiar más cómodamente. Organice sus propias notas o aprenda conocimientos relevantes directamente a través de artículos. Si hay alguna infracción, comuníquese con nosotros para eliminarla. Finalmente, me gustaría expresar mi gratitud a Shang Silicon Valley por su cursos de alta calidad.
  • También leí simultáneamente el libro "Patrón de diseño gráfico" (Patrón de diseño gráfico/(japonés) Hiroshi Yuki; traducido por Yang Wenxuan - Beijing: People's Posts and Telecommunications Press, 2017.1), y luego integré los contenidos de los dos para hacer el conocimiento. puntos más completos.

Supongo que te gusta

Origin blog.csdn.net/laodanqiu/article/details/132258846
Recomendado
Clasificación