給定一個語言,定義一個文法的一種表示, 並定義一個解釋器, 這個解釋器使用該表示來解釋語言中的句子。 java
解釋器模式是23種設計模式中惟一一種處理語法解析的設計模式。node
當須要求是,須要解釋某種語法來執行業務,而且你可將該語言中的句子表示爲一個抽象語法樹,可使用解釋器模式。設計模式
向《圖解設計模式》裏在解釋器模式這章節提到的例子,使用BNF式描述語法,例子是經過一種語法來移動小車:bash
<program> ::= program <command list> <command list> ::= <command>* end <command> ::= <repeat command> | <primitive command> <repeat command> ::= repeat <number> <command list> <primitive command> ::= go | right | left
這種語法就能夠作成抽象語法樹,好比要控制小車走路,能夠輸入語法:program repeat 3 go right end end。框架
意思爲重複執行3次go和right。工具
下面看看如何使用Java程序實現解釋器模式的。性能
// 語法樹節點的抽象類 public abstract class Node { abstract void parse(Context context); } // <program> ::= program <command list> public class ProgramNode extends Node { private Node commandListNode; void parse(Context context) { // 解析程序命令,必須是program才能夠向下執行 // 等於從命令中跳過program context.skipToken("program"); // 將後面的命令交給<command list>去解析 commandListNode = new CommandListNode(); commandListNode.parse(context); } public String toString() { return "[program " + commandListNode + "]"; } } // <command list> ::= <command>* end public class CommandListNode extends Node { ArrayList list = new ArrayList(); void parse(Context context) { // 命令是由<command>* end組成的,因此要循環把*都解析,直到end while (true) { if (context.currentToken() == null) { // 缺乏end報錯 throw new IllegalArgumentException("missing end"); } else if (context.currentToken().equals("end")) { // 解析end,跳出循環 context.skipToken("end"); break; } else { // 將命令交給<command>解析 Node commandNode = new CommandNode(); commandNode.parse(context); // 便於打印結果定義了一個集合存放命令 list.add(commandNode); } } } public String toString() { return list.toString(); } } // <command> ::= <repeat command> | <primitive command> public class CommandNode extends Node { private Node node; void parse(Context context) { // 判斷是<repeat command> 仍是 <primitive command>,而後找到想對應的解析對象 if (context.currentToken().equals("repeat")) { node = new RepeatCommandNode(); node.parse(context); } else { node = new PrimitiveCommandNode(); node.parse(context); } } public String toString() { return node.toString(); } } // <repeat command> ::= repeat <number> <command list> public class RepeatCommandNode extends Node { int number; private Node commandListNode; void parse(Context context) { // 解析repeat,在得到number用於打印,而後把number也解析(用的nextToken) context.skipToken("repeat"); number = context.currentNumber(); context.nextToken(); // 接着將命令交給<command list> commandListNode = new CommandListNode(); commandListNode.parse(context); } public String toString() { return "[repeat +" + number + " " + commandListNode + "]"; } } // <primitive command> ::= go | right | left public class PrimitiveCommandNode extends Node { String name; void parse(Context context) { // 解析這個命令 name = context.currentToken(); context.skipToken(name); // 若是不識別就報錯 if (!name.equals("go") && !name.equals("right") && !name.equals("left")) { throw new IllegalArgumentException("token is undefined."); } } public String toString() { return name; } } public class Context { StringTokenizer tokenizer; 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) { if (!token.equals(currentToken)) { throw new IllegalArgumentException("token is expected, but currentToken is found."); } nextToken(); } public int currentNumber() { int number = 0; try { number = Integer.parseInt(currentToken); } catch (NumberFormatException e) { throw new IllegalArgumentException("number is bad param."); } return number; } }
使用方法以下:spa
public class Client { public static void main(String[] args) { String command5 = "program repeat 4 repeat 3 go right go right end right end end"; Node node = new ProgramNode(); node.parse(new Context(command5)); System.out.println(node); } } // 輸出 // [program [[repeat +4 [[repeat +3 [go, right, go, right]], right]]]]
解釋器模式爲方法中的每一條規則至少定義了一個類, 所以包含許多規則的方法可能難以管理和維護。 所以當方法很是複雜時, 使用其餘的技術如 語法分析程序 或 編譯器生成器來處理。設計
解釋器模式的關鍵在於如何將需求抽象成語法樹。code
其業務實現很簡單,用類來解析每一個命令。
解釋器模式在實際的系統開發中使用的很是少,由於它會引發效率、性能以及維護等問題,通常在大中型的框架型項目可以找到它的身影,好比一些數據分析工具、報表設計工具、科學計算工具等等
以上就是我對解釋器模式的一些理解,有不足之處請你們指出,謝謝。