1、解釋器模式概念
1.1 介紹
解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬于行為型模式。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。
1.2 定義
解釋器模式是類的行為模式。給定一個語言之后,解釋器模式可以定義出其文法的一種表示,并同時提供一個解釋器。客戶端可以使用這個解釋器來解釋這個語言中的句子。
- 文法:即語法規則。在解釋器模式中每一個語法都將對應一個解釋器對象,用來處理相應的語法規則。它對于擴展、改變文法以及增加新的文法規則都很方便。
- 解釋器模式描述了如何為簡單的語言定義一個文法,如何在該語言中表示一個句子,以及如何解釋這些句子。
- 在解釋器模式中可以通過一種稱之為抽象語法樹(Abstract Syntax Tree, AST)的圖形方式來直觀地表示語言的構成,每一棵抽象語法樹對應一個語言實例
1.3 使用場景
- 有多個子類共有的方法,且邏輯相同。
- 重要的、復雜的方法,可以考慮作為模板方法。
2、命令模式UML類圖
AbstractExpression:定義解釋器的接口,約定解釋器的解釋操作。其中的Interpret接口,正如其名字那樣,它是專門用來解釋該解釋器所要實現的功能。(如加法解釋器中的Interpret接口就是完成兩個操作數的相加功能)。
TerminalExpression:終結符解釋器,用來實現語法規則中和終結符相關的操作,不再包含其他的解釋器,如果用組合模式來構建抽象語法樹的話,就相當于組合模式中的葉子對象,可以有多種終結符解釋器。
NonterminalExpression:非終結符解釋器,用來實現語法規則中非終結符相關的操作,通常一個解釋器對應一個語法規則,可以包含其他解釋器,如果用組合模式構建抽象語法樹的話,就相當于組合模式中的組合對象。可以有多種非終結符解釋器。
Context:上下文,通常包含各個解釋器需要的數據或是公共的功能。這個Context在解釋器模式中起著非常重要的作用。一般用來傳遞被所有解釋器共享的數據,后面的解釋器可以從這里獲取這些值。
Client:客戶端,指的是使用解釋器的客戶端,通常在這里將按照語言的語法做的表達式轉換成使用解釋器對象描述的抽象語法樹,然后調用解釋操作。
3、命令模式代碼實現
加減算法
AbstractExpression:
public abstract class Expression {
/**
* 以環境為準,本方法解釋給定的任何一個表達式
*/
public abstract int interpret(Context ctx);
/**
* 檢驗兩個表達式在結構上是否相同
*/
public abstract boolean equals(Object obj);
/**
* 返回表達式的hash code
*/
public abstract int hashCode();
/**
* 將表達式轉換成字符串
*/
public abstract String toString();
}
NonterminalExpression:
public class Minus extends Expression {
private Expression left, right;
public Minus(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public boolean equals(Object obj) {
if (obj != null && obj instanceof Minus) {
return left.equals(((Minus) obj).left) && right.equals(((Minus) obj).right);
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public int interpret(Context ctx) {
return left.interpret(ctx) - right.interpret(ctx);
}
@Override
public String toString() {
return "(" + left.toString() + " - " + right.toString() + ")";
}
}
public class Plus extends Expression {
private Expression left,right;
public Plus(Expression left , Expression right){
this.left = left;
this.right = right;
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj instanceof Plus)
{
return left.equals(((Plus)obj).left) &&
right.equals(((Plus)obj).right);
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public int interpret(Context ctx) {
return left.interpret(ctx) + right.interpret(ctx);
}
@Override
public String toString() {
return "(" + left.toString() + " + " + right.toString() + ")";
}
}
TerminalExpression:
public class Variable extends Expression {
private String name;
public Variable(String name){
this.name = name;
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj instanceof Variable)
{
return this.name.equals(
((Variable)obj).name);
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public String toString() {
return name;
}
@Override
public int interpret(Context ctx) {
return ctx.lookup(this);
}
}
public class Constant extends Expression{
private int value;
public Constant(int value){
this.value = value;
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj instanceof Constant){
return this.value == ((Constant)obj).value;
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public int interpret(Context ctx) {
return value;
}
@Override
public String toString() {
return new Integer(value).toString();
}
}
Receiver:
public class Receiver implements Serializable {
public void onLeft() {
System.out.println("向左");
}
public void onRight() {
System.out.println("向右");
}
public void onBottom() {
System.out.println("向下");
}
public void onTransformation() {
System.out.println("變形");
}
}
Context:
public class Context {
private Map<Variable,Integer> map = new HashMap<Variable,Integer>();
public void assign(Variable var , int value){
map.put(var, new Integer(value));
}
public int lookup(Variable var) throws IllegalArgumentException{
Integer value = map.get(var);
if(value == null){
throw new IllegalArgumentException();
}
return value.intValue();
}
}
Client :
public class Client {
public static void main(String[] args) {
Context ctx = new Context();
Variable x = new Variable("x");
Variable y = new Variable("y");
Constant c = new Constant(1);
ctx.assign(x, 2);
ctx.assign(y, 3);
Expression exp = new Plus(new Plus(c,x) , new Minus(y,x));
System.out.println(exp.toString() + "=" + exp.interpret(ctx));
}
}
結果輸出:
((1 + x) + (y - x))=4
4、模式總結
4.1 優點
易于改變和擴展文法。
每一條文法規則都可以表示為一個類,因此可以方便地實現一個簡單的語言。
實現文法較為容易。在抽象語法樹中每一個表達式節點類的實現方式都是相似的,這些類的代碼編寫都不會特別復雜,還可以通過一些工具自動生成節點類代碼。
增加新的解釋表達式較為方便。如果用戶需要增加新的解釋表達式只需要對應增加一個新的終結符表達式或非終結符表達式類,原有表達式類代碼無須修改,符合“開閉原則”。
4.2 缺點
對于復雜文法難以維護。在解釋器模式中,每一條規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數將會急劇增加,導致系統難以管理和維護,此時可以考慮使用語法分析程序等方式來取代解釋器模式。
執行效率較低。由于在解釋器模式中使用了大量的循環和遞歸調用,因此在解釋較為復雜的句子時其速度很慢,而且代碼的調試過程也比較麻煩。