設計模式學習筆記——解釋器(Interpreter)模式
@(設計模式)[設計模式, 解釋器模式, Interpreter]
基本介紹
給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
解釋器案例
類圖
實現代碼
Node抽象類
package com.pc.interpreter.example;
/**
* 節點類
*/
public abstract class Node {
/**
* 解析
*
* @param context 上下文
* @throws ParseException 解析異常
*/
public abstract void parse(Context context) throws ParseException;
}
ParseException類
package com.pc.interpreter.example;
/**
* 解析異常類
*/
public class ParseException extends Exception {
public ParseException(String msg) {
super(msg);
}
}
ProgramNode類
package com.pc.interpreter.example;
/**
* 程序節點類
* <program> ::= program <command list>
*/
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 "[program " + commandListNode + "]";
}
}
CommandListNode類
package com.pc.interpreter.example;
import java.util.ArrayList;
/**
* 命令列表節點類
* <command list> ::= <command>* end
*/
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 list.toString();
}
}
CommandNode類
package com.pc.interpreter.example;
/**
* 命令節點類
* <command> ::= <repeat command> | <primitive command>
*/
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 node.toString();
}
}
RepeatCommandNode類
package com.pc.interpreter.example;
/**
* 重複命令節點類
* <repeat command> ::= repeat <number> <command list>
*/
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 "[repeat " + number + " " + commandListNode + "]";
}
}
PrimitiveCommandNode類
package com.pc.interpreter.example;
/**
* 原始命令節點類
* <primitive command> ::= go | right | left
*/
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 name;
}
}
Context類
package com.pc.interpreter.example;
import java.util.StringTokenizer;
/**
* 上下文類
*/
public class Context {
/**
* 分詞器
*/
private StringTokenizer tokenizer;
/**
* 當前符號
*/
private String currentToken;
public Context(String text) {
this.tokenizer = new StringTokenizer(text);
this.nextToken();
}
/**
* 返回下一個符號
*
* @return 下一個符號
*/
public String nextToken() {
if (tokenizer.hasMoreTokens()) {
this.currentToken = this.tokenizer.nextToken();
} else {
this.currentToken = null;
}
return this.currentToken;
}
/**
* 返回當前符號
*
* @return 當前符號
*/
public String currentToken() {
return this.currentToken;
}
/**
* 跳過指定符號
*
* @param token 指定符號
* @throws ParseException 解析異常
*/
public void skipToken(String token) throws ParseException {
if (!token.equals(this.currentToken)) {
throw new ParseException("Warning: " + token + " is expected, but " + currentToken + " is found.");
}
this.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;
}
}
測試類
package com.pc.interpreter.example.test;
import com.pc.interpreter.example.Context;
import com.pc.interpreter.example.Node;
import com.pc.interpreter.example.ProgramNode;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.FileReader;
/**
* Interpreter Tester.
*
* @author Switch
* @version 1.0
*/
public class InterpreterTest {
/**
* 測試解釋器模式
*/
@Test
public void testInterpreter() {
try {
BufferedReader reader = new BufferedReader(new FileReader(System.getProperty("user.dir")
+ "/src/main/java/com/pc/interpreter/example/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);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
運行結果
text = "program end"
node = [program []]
text = "program go end"
node = [program [go]]
text = "program go right go right go right go right end"
node = [program [go, right, go, right, go, right, go, right]]
text = "program repeat 4 go right end end"
node = [program [[repeat 4 [go, right]]]]
text = "program repeat 4 repeat 3 go right go left end right end end"
node = [program [[repeat 4 [[repeat 3 [go, right, go, left]], right]]]]
解釋器模式中的角色
AbstractExpression(抽象表達式)
AbstractExpression
角色定義了語法樹節點的共同接口(API
)。在案例中,由Node
類扮演此角色。在案例中,共同接口(API
)的名字是parse
,不過在類圖中它的名字是interpreter
。
TerminalExpression(終結符表達式)
TerminalExpression
角色對應BNF
中的終結特表達式。在案例中,由PrimitiveCommandNode
類扮演此角色。
NonterminalExpression(非終結符表達式)
NonterminalExpression
角色對應BNF
中的非終結符表達式。在案例中,由ProgramNode
類、CommandNode
類、RepeatCommandNode
類和CommandListNode 類扮演此角色。
Context(文脈、上下文)
Context 角色爲解釋器進行語法解析提供了必要的信息。在案例中,由Context 類扮演此角色。
Client(請求者)
爲了推導語法樹, C li ent 角色會調用TerminalExpression 角色和Nonterm inalExpress i on 角色。在案例中,由Ma 工n 類扮演此角色。
類圖
GitHub:DesignPatternStudy
——————參考《圖解設計模式》