给定一个语言,定义一个文法的一种表示, 并定义一个解释器, 这个解释器使用该表示来解释语言中的句子。 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
其业务实现很简单,用类来解析每一个命令。
解释器模式在实际的系统开发中使用的很是少,由于它会引发效率、性能以及维护等问题,通常在大中型的框架型项目可以找到它的身影,好比一些数据分析工具、报表设计工具、科学计算工具等等
以上就是我对解释器模式的一些理解,有不足之处请你们指出,谢谢。