情景引入 学习到这里,大家应该已经掌握了不少设计模式。设计模式的目的之一就是提高类的可复用 性。可复用性是指不用做太大修改(甚至是不做任何修改)就可以在多种应用场景使用之前编写 的类。
在本章中,我们将学习Interpreter模式。 在Interpreter模式中,程序要解决的问题会被用非常简单的“迷你语言”表述出来,即用“迷 你语言”编写的“迷你程序”把具体的问题表述出来。迷你程序是无法单独工作的,我们还需要用 Java语言编写一个负责“翻译”( interpreter )的程序。翻译程序会理解迷你语言,并解释和运行迷 你程序。这段翻译程序也被称为解释器。这样,当需要解决的问题发生变化时,不需要修改Java语 言程序,只需要修改迷你语言程序即可应对。
下面,我们用图示展示一下当问题发生变化时,需要哪个级别的代码。使用Java语言编程时, 需要修改的代码如图23-1所示。虽然我们希望需要修改的代码尽量少,但是多多少少都必须修改 Java代码。
但是,在使用Interpreter模式后,我们就无需修改Java程序,只需修改用迷你语言编写的迷你 程序即可(图23-2)。
示例程序 功能描述 使用迷你语言控制玩具车
类的一览表
名字
说明
Node
表示语法树“节点”的类
ProgramNode
对应的类
CommandListNode
对应 的类
CommandNode
对应 的类
RepeatCommandNode
对应 的类
PrimitiveCommandNode
对应 的类
Context
表示语法解析上下文的类
ParseException
表示语法解析中可能会发生的异常的类
Main
测试程序行为的类
UML
主要代码 Node 类 1 2 3 public abstract class Node { public abstract void parse (Context context) throws ParseException; }
ProgramNode 类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class ProgramNode extends Node { private Node commandListNode; @Override public void parse (Context context) throws ParseException { context.skipToken("Program" ); commandListNode = new CommandListNode (); commandListNode.parse(context); } @Override public String toString () { return "ProgramNode{" + "commandListNode=" + commandListNode + '}' ; } }
CommandListNode 类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class CommandListNode extends Node { private ArrayList list = new ArrayList (); @Override 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); } } } @Override public String toString () { return "CommandListNode{" + "list=" + list + '}' ; } }
CommandNode 类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class CommandNode extends Node { private Node node; @Override 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); } } @Override public String toString () { return "CommandNode{" + "node=" + node.toString() + '}' ; } }
RepeatCommandNode 类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class RepeatCommandNode extends Node { private int number; private Node commandListNode; @Override public void parse (Context context) throws ParseException { context.skipToken(" repeat" ); number = context.currentNumber(); context.nextToken(); commandListNode = new CommandListNode (); commandListNode.parse(context); } @Override public String toString () { return "RepeatCommandNode{" + "number=" + number + ", commandListNode=" + commandListNode + '}' ; } }
PrimitiveCommandNode 类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class PrimitiveCommandNode extends Node { private String name; @Override 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" ) ; } } @Override public String toString () { return "PrimitiveCommandNode{" + "name='" + name + '\'' + '}' ; } }
Context 类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class Context { 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; } }
ParseException 类 1 2 3 4 5 public class ParseException extends Exception { public ParseException (String msg) { super (msg); } }
Main 类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Main { public static void main (String[] args) { try { BufferedReader reader = new BufferedReader (new FileReader ("program.txt" )); String text; while ((text = reader.readLine()) != null ) { System.out.println("text = \"" + text + "\"" ); Node node = new ProgramNode (); node.parse(new Context (text)); System.out.println("node =" + node); } reader.close(); } catch (Exception e) { e.printStackTrace(); } } }
program.txt 1 2 3 4 5 6 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