天天看點

解釋器模式

1.解釋器模式是什麼

1.百度百科

解釋器模式(InterpreterPattern)定義一些文法規則,然後定義一個解析器去解析該文法

2.維基百科

In computer programming, the interpreter pattern is a design pattern that specifies how to evaluate sentences in a language. The basic idea is to have a class for each symbol (terminal or nonterminal) in a specialized computer language. The syntax tree of a sentence in the language is an instance of the composite pattern and is used to evaluate (interpret) the sentence for a client.See also Composite pattern.

3.lz了解

相當于定義了一些文法規則,然後定義一個能解析該文法規則的解釋器。

4.核心角色

抽象解釋器(AbstractExpression) 約定解釋器的解釋操作。其中的Interpret接口,正如其名字那樣,它是專門用來解釋該解釋器所要實作的功能。(如加法解釋器中的Interpret接口就是完成兩個操作數的相加功能)。

終結符解釋器(TerminalExpression) 用來實作文法規則中和終結符相關的操作,不再包含其他的解釋器,如果用組合模式來建構抽象文法樹的話,就相當于組合模式中的葉子對象,可以有多種終結符解釋器。

非終結符解釋器(NonterminalExpression) 用來實作文法規則中非終結符相關的操作,通常一個解釋器對應一個文法規則,可以包含其他解釋器,如果用組合模式建構抽象文法樹的話,就相當于組合模式中的組合對象。可以有多種非終結符解釋器。

上下文環境(Context) 通常包含各個解釋器需要的資料或是公共的功能。這個Context在解釋器模式中起着非常重要的作用。一般用來傳遞被所有解釋器共享的資料,後面的解釋器可以從這裡擷取這些值。

用戶端(Client) 指的是使用解釋器的用戶端,通常在這裡将按照語言的文法做的表達式轉換成使用解釋器對象描述的抽象文法樹,然後調用解釋操作。

2.解釋器模式解決了什麼問題

1、可擴充性比較好,靈活。 2、增加了新的解釋表達式的方式。 3、易于實作簡單文法。

3.解釋器模式用法

我們就以加減法的解釋器來說明問題

抽象解釋器、可以執行個體化為終結符解釋器即操作符,可以執行個體化為非終結符解釋器即變量

//抽象解釋器
public interface Expression {
	/**
	 * 以環境為準,本方法解釋給定的任何一個表達式
	 */
	int interpret(Context ctx);

	/**
	 * 将表達式轉換成字元串
	 */
	String toString();

}
           

終結符解釋器

public class Plus implements Expression {

	private Expression left, right;

	public Plus(Expression left, Expression right) {
		this.left = left;
		this.right = right;
	}

	@Override
	public int interpret(Context ctx) {
		return left.interpret(ctx) + right.interpret(ctx);
	}

	@Override
	public String toString() {
		return "(" + left.toString() + " + " + right.toString() + ")";
	}
}

//減法
public class Minus implements Expression {
	//減法表達式左邊 右邊
	private Expression left, right;

	public Minus(Expression left, Expression right) {
		this.left = left;
		this.right = right;
	}
	//從上下文環境中取值并計算
	@Override
	public int interpret(Context ctx) {

		return left.interpret(ctx) - right.interpret(ctx);
	}

	@Override
	public String toString() {
		return "(" + left.toString() + " - " + right.toString() + ")";
	}
}
           

終結符解釋器、這裡重寫了 equals和哈希code方法以便于對比兩個變量是否相等。

/**
 * 常量
 *	可以直接使用的常量
 */
public class Constant implements Expression {

	private int value;

	public Constant(int value) {
		this.value = value;
	}

	@Override
	public int interpret(Context ctx) {

		return value;
	}

	@Override
	public String toString() {
		return new Integer(value).toString();
	}

	@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();
	}

}
/**
 * 變量
 *
 * 将變量存儲到變量表中
 */
public class Variable implements Expression {

	private String name;

	public Variable(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return name;
	}

	@Override
	public int interpret(Context ctx) {
		return ctx.lookup(this);
	}

	@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();
	}

}
           

上下文環境、一般用于存儲變量的值等功能

/**
 * 上下文環境 用來存儲多次調用的資料
 *
 * 相當于變量臨時存儲的表
 */
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();
	}

}
           

用戶端調用

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, 4);
		ctx.assign(y, 3);
		//計算公式
		Expression e = new Plus(c, x);
		System.err.println(x.toString()+" = "+x.interpret(ctx));
		//System.err.println(c.toString()+" = "+c.interpret(ctx));
		System.err.println(e.toString());
		System.err.println("結果是:" + e.interpret(ctx));


		Expression exp = new Plus(new Plus(c, x), new Minus(y, x));
		System.out.println(exp.toString() + "=" + exp.interpret(ctx));
		Expression exp1 = new Plus(new Plus(c, x), c);
		System.out.println(exp1.toString() + "=" + exp1.interpret(ctx));
	}

}

           

結果

x = 4

(1 + x)

結果是:5

((1 + x) + (y - x))=4

((1 + x) + 1)=6

可以看出操作符中可以嵌套變量常量。增加新的操作符非常友善

4.解釋器模式的問題

使用場景少 可利用場景比較少。

代碼調用結構複雜 對于複雜的文法比較難維護。

類膨脹 解釋器模式會引起類膨脹。

遞歸調用 解釋器模式采用遞歸調用方法。

5.解釋器模式總結

應用場景:

1、可以将一個需要解釋執行的語言中的句子表示為一個抽象文法樹。

2、一些重複出現的問題可以用一種簡單的語言來進行表達。

3、一個簡單文法需要解釋的場景。

碎碎念:

想要明白解釋器模式首先要明白通路者模式的調用方式和對遞歸的實作原理有一個清晰的認識。解釋器模式可以通過不斷疊加的方式去解釋一個冗長的語句 并且增加新的功能非常友善完全不影響之前代碼的結構。從結構設計上來說可謂是非常精妙了。從變化的角度來說。解釋器模式封裝了調用結構,開放了具體調用方式和執行方式的拓展,雖然如此但是解釋器模式使用場景并不多。基本上能用到解釋器模式的前輩已經被我們造好。我們要做的就是愉快的調用就好了。

引用

https://www.jianshu.com/p/c138a1d2be5e