案例引入
通過解釋器模式來實現四則運算,如計算a+b-c
的值,具體要求
- 先輸入表達式的形式,比如
a+b+c-d+e
,要求表達式的字母不能重復 - 在分別輸入
a,b,c,d,e
的值 - 最后求出結果
傳統方案
- 編寫一個方法,接收表達式的形式,然后根據用戶輸入的數值進行解析,得到結果
【分析】
如果加入新的運算符,比如*或/等等,不利于擴展,另外讓一個方法來解析會造成程序結構混亂,不夠清晰
【改進】
可以考慮使用解釋器模式,即表達式->解釋器(可以有多種解釋器)->結果
介紹
基本介紹
- 在解釋器模式中,程序要解決的問題會被用非常簡單的“迷你語言”表述出來,即用“迷你語言”編寫的迷你程序把具體的問題表述出來。迷你程序是無法單獨工作的,我們還需要用Java語言編寫一個負責“翻譯”(interpreter)的程序。翻譯程序會理解迷你語言并解釋迷你語言,最后運行迷你程序。這段翻譯程序也被稱為解釋器。這樣,當需要解決的問題發生變化時,不需要修改 Java語言程序,只需要修改迷你語言程序即可應對
- 在編譯原理中,一個算術表達式通過詞法分析器形成詞法單元,然后這些詞法單元再通過語法分析器構建語法分析樹,最終形成一顆抽象的語法分析樹。這里的詞法分析器和語法分析器都可以看做是解釋器
應用場景
- 可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹,一些重復出現的問題可以用一種簡單的語言來表達,比如下列場景:編譯器、運算表達式計算、正則表達式、機器人指令……
登場角色
AbstractExpression(抽象表達式)
:抽象表達式,聲明一個抽象的解釋操作(定義了語法樹節點的共同接口),這個方法為抽象語法樹中所有的節點所共享,方法可以取名為parse/interpreter,譯為解析/翻譯TerminalExpression(終結符表達式)
:為終結符表達式,實現與文法中的終結符相關的解釋操作NonTermialExpression(非終結符表達式)
:為非終結符表達式,為文法中的非終結符實現解釋操作Context(上下文)
:是環境角色,含有解釋器之外的全局信息,為解釋器進行語法解析提供了必要的信息Client(請求者)
:調用TerminalExpression和NonTermialExpression來推導語法樹
案例實現
案例一
類圖
實現
【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);
}
【變量解析器】
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*/@Overridepublic int interpreter(HashMap<String, Integer> var) {return var.get(this.key);}
}
【抽象的運算符號解釋器】
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*/@Overridepublic int interpreter(HashMap<String, Integer> var) {// 默認實現return 0;}
}
【具體的運算符號解釋器:加法解釋器】
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);}
}
【具體的運算符號解釋器:減法解釋器】
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);}
}
【計算器】
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加入stackstack.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到stackstack.push(new VarExpression(String.valueOf(charArray[i])));break;}}//當遍歷完整個charArray數組后,stack就得到最終的Expressionthis.expression = stack.pop();}public int run(HashMap<String, Integer> var) {//最后將表達式 a+b 和 var={a=10,b=20}//然后傳遞給expression的interpreter進行解釋執行return this.expression.interpreter(var);}
}
【客戶端】
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+bString 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;}
}
【運行】
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
【執行過程】
- 第一次循環:將變量解析器a放入到棧中
- 第二次循環: 從stack取出左表達式"a",接著中數組中獲取并生成新的表達式"b",最后構建加法表達式"a+b"存儲到棧中
案例二
說明
有一輛小車,需要編寫一個簡單的小程序控制小車的移動,比如program go right go right go right go right end
,小車收到指令之后,就會走出如下的軌跡
類圖
實現
【抽象表達式:Node】
package com.atguigu.interpreter.Sample;/*** 語法樹中各個部分(節點)中最頂層的類*/
public abstract class Node {/*** 進行語法解析處理** @param context 語法解析上下文的類* @throws ParseException*/public abstract void parse(Context context) throws ParseException;
}
【自定義的 解析異常】
package com.atguigu.interpreter.Sample;public class ParseException extends Exception {public ParseException(String msg) {super(msg);}
}
【終結符表達式:PrimitiveCommandNode】
終結符表達式:不會進一步展開繼續調用parse方法
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;}
}
【非終結符表達式: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的值變成endcontext.skipToken("program");commandListNode = new CommandListNode();commandListNode.parse(context);}public String toString() {// 等效于 return "[program " + commandListNode.toString() + "]";return "[program " + commandListNode + "]";}
}
【非終結符表達式: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();}
}
【非終結符表達式: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();}
}
【非終結符表達式: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
【運行】
迷你程序 = "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
拓展
- 上面的程序的功能只是將迷你程序解析出來,并沒有真正執行其中的指令,下面將繼續完善這個程序,讓小車可以真正根據指令執行起來
- 下面的代碼屬實有點繞,代碼不只是使用了解釋器模式,還使用了外觀模式來讓解釋器更加便于使用,除此之外,還使用工廠方法模式來提供
createExecutor(String name)
方法來根據指令名稱生成相應的執行器,請大伙們慢慢欣賞
【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");}
}
【運行】
解釋器模式在Spring框架中的應用
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"); //結果:96int result = (Integer) expression.getValue();System.out.println(result);}}
Expression子類
【說明】
- Expression接口是表達式接口,其下面有不同的實現類,比如SpelExpression或者CompositeStringExpression
- 使用的時候,根據你創建的不同的Parser對象,返回不同的Expression對象
- 最后使用得到的Expression對象,調用其getValue解釋執行表達式,來得到結果
總結
【優點】
- 當有一個語言需要解釋執行,可將該語言中的句子表示為一個抽象語法樹,就可以考慮使用解釋器模式,讓程序具有良好的擴展性
【缺點】
- 解釋器模式會引起類膨脹、解釋器模式采用遞歸調用方法,將會導致調試非常復雜、效率可能降低
文章說明
- 本文章為本人學習尚硅谷的學習筆記,文章中大部分內容來源于尚硅谷視頻(點擊學習尚硅谷相關課程),也有部分內容來自于自己的思考,發布文章是想幫助其他學習的人更方便地整理自己的筆記或者直接通過文章學習相關知識,如有侵權請聯系刪除,最后對尚硅谷的優質課程表示感謝。
- 本人還同步閱讀《圖解設計模式》書籍(圖解設計模式/(日)結城浩著;楊文軒譯–北京:人民郵電出版社,2017.1),進而綜合兩者的內容,讓知識點更加全面