[Design Patterns - Study Notes] 23 Design Patterns - Interpreter Mode Interpreter (principle explanation + application scenario introduction + case introduction + Java code implementation)

Case introduction

Implement four arithmetic operations through interpreter mode, such as calculated a+b-cvalues, specific requirements

  • First enter the form of the expression. For example a+b+c-d+e, it is required that the letters of the expression cannot be repeated.
  • a,b,c,d,eValues ​​entered separately
  • Finally find the result

Insert image description here

traditional solution

  • Write a method to receive the form of an expression, and then parse it based on the value entered by the user to get the result.

【analyze】

If new operators are added, such as * or /, etc., it will not be conducive to expansion. In addition, allowing one method to parse will cause the program structure to be confusing and unclear.

【Improve】

Consider using the interpreter mode, i.e.表达式->解释器(可以有多种解释器)->结果

introduce

basic introduction

  • In the interpreter mode, the problem to be solved by the program will be expressed in a very simple "mini language", that is, a mini program written in the "mini language" expresses the specific problem. The mini program cannot work alone. We also need to write a program responsible for "interpreter" in Java language. The translator understands the mini-language, interprets the mini-language, and finally runs the mini-program. This translation program is also called an interpreter. In this way, when the problem that needs to be solved changes, there is no need to modify the Java language program, only the mini language program needs to be modified to deal with it.
  • In the compilation principle, an arithmetic expression is formed into lexical units through a lexical analyzer, and then these lexical units are constructed through a syntax analyzer to construct a syntax analysis tree, and finally an abstract syntax analysis tree is formed. Both the lexical analyzer and the syntax analyzer here can be regarded as interpreters.

Application scenarios

  • Sentences in a language that need to be interpreted and executed can be represented as an abstract syntax tree. Some recurring problems can be expressed in a simple language, such as the following scenarios: compilers, calculation of operational expressions, regular expressions, robots instruction……

Characters

Insert image description here

  • AbstractExpression(抽象表达式): Abstract expression, declaring an abstract interpretation operation (defining the common interface of syntax tree nodes). This method is shared by all nodes in the abstract syntax tree. The method can be named parse/interpreter, which is translated as parsing/translation
  • TerminalExpression(终结符表达式): It is a terminal expression and implements interpretation operations related to terminal symbols in the grammar.
  • NonTermialExpression(非终结符表达式): It is a non-terminal expression and implements interpretation operations for non-terminal symbols in the grammar.
  • Context(上下文): It is an environmental role that contains global information outside the interpreter and provides necessary information for the interpreter to perform grammar parsing.
  • Client(请求者): Call TerminalExpression and NonTermialExpression to derive the syntax tree

Case implementation

Case number one

Class Diagram

Insert image description here

accomplish

【Expression】

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);
}

【Variable parser】

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);
   }
}

[Abstract arithmetic symbol interpreter]

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;
   }
}

[Specific operation symbol interpreter: addition interpreter]

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);
   }
}

[Specific operation symbol interpreter: subtraction interpreter]

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);
    }
}

【calculator】

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);
   }
}

【Client】

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;
    }
}

【run】

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

【Implementation process】

  • First loop: put variable parser a on the stack
  • Second loop: Take the left expression "a" from the stack, then obtain and generate a new expression "b" from the array, and finally construct the addition expression "a+b" and store it in the stack

Insert image description here

Insert image description here

Case 2

illustrate

There is a car, and you need to write a simple small program to control the movement of the car. For example program go right go right go right go right end, after the car receives instructions, it will follow the following trajectory

Insert image description here

Class Diagram

Insert image description here

accomplish

[Abstract expression: Node]

package com.atguigu.interpreter.Sample;

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

[Customized parsing exception]

package com.atguigu.interpreter.Sample;

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

[Terminator expression: PrimitiveCommandNode]

Terminal expression: no further expansion will continue and the parse method will be called.

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;
    }
}

[Nonterminal expression: 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 + "]";
    }
}

[Nonterminal expression: 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();
    }
}

[Nonterminal expression: 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();
    }
}

[Nonterminal expression: 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 + "]";
    }
}

【Context】

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;
    }
}

【Client:Main】

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();
        }
    }
}

【program.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

【run】

迷你程序 = "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

Insert image description here

expand

  • The function of the above program is only to parse out the mini program and does not actually execute the instructions. The following will continue to improve the program so that the car can actually execute according to the instructions.
  • The following code is indeed a bit convoluted. The code not only uses the interpreter mode, but also uses the appearance mode to make the interpreter easier to use. In addition, it also uses the factory method mode to provide methods to generate corresponding execution according to the instruction name createExecutor(String name). Please take your time to appreciate it.

【ParseException】

package com.atguigu.interpreter.A1.language;

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

【ExecuteException】

package com.atguigu.interpreter.A1.language;

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

【Node】

package com.atguigu.interpreter.A1.language;

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

【ProgramNode】

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 + "]";
    }
}

【CommandNode】

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();
    }
}

【CommandListNode】

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();
    }
}

【PrimitiveCommandNode】

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;
    }
}

【RepeatCommandNode】

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 + "]";
    }
}

【Context】

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);
    }
}

【Executor】

package com.atguigu.interpreter.A1.language;

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

【InterpreterFacade】

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();
        }
    }
}

【ExecutorFactory】

package com.atguigu.interpreter.A1.language;

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

【TurtleCanvas】

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);
    }
}

【Main】

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");
    }
}

【run】

Insert image description here

Application of interpreter pattern in 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);
   }

}

Insert image description here

Expression subclass

Insert image description here

Insert image description here

【illustrate】

  • The Expression interface is an expression interface, and there are different implementation classes below it, such as SpelExpression or CompositeStringExpression.
  • When used, different Expression objects are returned based on the different Parser objects you create.

Insert image description here

  • Finally, use the obtained Expression object to call its getValue to interpret and execute the expression to get the result.

Summarize

【advantage】

  • When there is a language that needs to be interpreted and executed, the sentences in the language can be represented as an abstract syntax tree. You can consider using the interpreter mode to make the program have good scalability.

【shortcoming】

  • The interpreter mode will cause class expansion. The interpreter mode uses recursive calling methods, which will make debugging very complicated and may reduce efficiency.

Article description

  • This article is my study notes for studying Shang Silicon Valley. Most of the content in the article comes from Shang Silicon Valley videos ( click to learn Shang Silicon Valley related courses ), and some of the content comes from my own thinking. I publish this article to help other people who study more conveniently. Organize your own notes or learn relevant knowledge directly through articles. If there is any infringement, please contact us to delete it. Finally, I would like to express my gratitude to Shang Silicon Valley for its high-quality courses.
  • I also simultaneously read the book "Graphic Design Pattern" (Graphic Design Pattern/(Japanese) Hiroshi Yuki; translated by Yang Wenxuan - Beijing: People's Posts and Telecommunications Press, 2017.1), and then integrated the contents of the two to make the knowledge points more comprehensive.

Guess you like

Origin blog.csdn.net/laodanqiu/article/details/132258846