為人處事是一門大學問,察言觀色、聽懂弦外之音都是非常重要的,老板跟你說“XX你最近表現平平啊,還得要多努力”,如果你不當回事,平常對待,可能下次就是“XX,恩,你人還是不錯,平常工作也很努力,但是我想這份工作可能不是很適合你…..”。又比如你老大說“XX,你最近表現不錯,工作積極性很高啊!繼續保持啊!”,你高興樂呵着心想是不是老板要給我加工資了,可能你等到花都謝了也沒有,得到的可能會是更多的工作量。對於我們剛剛入社會的人不夠圓滑,不會察言觀色,更聽不懂老板的弦外之音,所以我們期待如果有一個翻譯機該多好,直接將別人的弦外之音給翻譯出來就好了。
在我們實際的生活中是這樣,在軟件的世界里也同樣存在着翻譯機,只不過在軟件中我們稱之為解釋器。在系統中如果某一特定類型的問題在頻繁的發生,此時我們就有必要將這些問題的實例表述為一個語言中句子,因此可以構建一個解釋器,然后利用該解釋器來解釋這些句子來解決這些問題。
一、 模式定義
所謂解釋器模式就是定義語言的文法,並且建立一個解釋器來解釋該語言中的句子。
在這里我們將語言理解成使用規定格式和語法的代碼。
在前面我們知道可以構建解釋器來解決那些頻繁發生的某一特定類型的問題,在這我們將這些問題的實例表述為一個語言中句子。例如我經常利用正則表達式來檢測某些字符串是否符合我們規定的格式。這里正則表達式就是解釋器模式的應用,解釋器為正則表達式定義了一個文法,如何表示一個特定的正則表達式,以及如何解釋這個正則表達式。
解釋器模式描述了如何構成一個簡單的語言解釋器,主要應用在使用面向對象語言開發的編譯器中。它描述了如何為簡單的語言定義一個文法,如何在該語言中表示一個句子,以及如何解釋這些句子。
在解釋器模式中除了能夠使用文法規則來定義一個語言,還有通過一個更加直觀的方法來表示——使用抽象語法樹。抽象語法樹能夠更好地,更直觀地表示一個語言的構成,每一顆抽象語法樹對應一個語言實例。
二、 模式結構
下圖是解釋器模式的UML結構圖。
解釋器模式主要包含如下幾個角色:
AbstractExpression: 抽象表達式。聲明一個抽象的解釋操作,該接口為抽象語法樹中所有的節點共享。
TerminalExpression: 終結符表達式。實現與文法中的終結符相關的解釋操作。實現抽象表達式中所要求的方法。文法中每一個終結符都有一個具體的終結表達式與之相對應。
NonterminalExpression: 非終結符表達式。為文法中的非終結符相關的解釋操作。
Context: 環境類。包含解釋器之外的一些全局信息。
Client: 客戶類。
抽象語法樹描述了如何構成一個復雜的句子,通過對抽象語法樹的分析,可以識別出語言中的終結符和非終結符類。 在解釋器模式中由於每一種終結符表達式、非終結符表達式都會有一個具體的實例與之相對應,所以系統的擴展性比較好。
三、 模式實現
現在我們用解釋器模式來實現一個基本的加、減、乘、除和求模運算。例如用戶輸入表達式“3 * 4 / 2 % 4”,輸出結果為2。下圖為該實例的UML結構圖:
抽象語法樹
實現過程:
抽象表達式:Node.java。
public interface Node { public int interpret(); }
非終結表達式:ValueNode.java。主要用解釋該表達式的值。
public class ValueNode implements Node { private int value; public ValueNode(int value) { this.value=value; } public int interpret() { return this.value; } }
終結表達式抽象類,由於該終結表達式需要解釋多個運算符號,同時用來構建抽象語法樹:
public abstract class SymbolNode implements Node { protected Node left; protected Node right; public SymbolNode(Node left,Node right) { this.left=left; this.right=right; } }
MulNode.java
public class MulNode extends SymbolNode { public MulNode(Node left,Node right) { super(left,right); } public int interpret() { return left.interpret() * right.interpret(); } }
ModNode.java
public class ModNode extends SymbolNode{ public ModNode(Node left,Node right){ super(left,right); } public int interpret(){ return super.left.interpret() % super.right.interpret(); } }
DivNode.java
public class DivNode extends SymbolNode{ public DivNode(Node left,Node right){ super(left,right); } public int interpret(){ return super.left.interpret() / super.right.interpret(); } }
Calculator.java
public class Calculator{ private String statement; private Node node; public void build(String statement){ Node left=null,right=null; Stack stack=new Stack(); String[] statementArr=statement.split(" "); for(int i=0;i<statementArr.length;i++){ if(statementArr[i].equalsIgnoreCase("*")){ left=(Node)stack.pop(); int val=Integer.parseInt(statementArr[++i]); right=new ValueNode(val); stack.push(new MulNode(left,right)); } else if(statementArr[i].equalsIgnoreCase("/")){ left=(Node)stack.pop(); int val=Integer.parseInt(statementArr[++i]); right=new ValueNode(val); stack.push(new DivNode(left,right)); } else if(statementArr[i].equalsIgnoreCase("%")){ left=(Node)stack.pop(); int val=Integer.parseInt(statementArr[++i]); right=new ValueNode(val); stack.push(new ModNode(left,right)); } else{ stack.push(new ValueNode(Integer.parseInt(statementArr[i]))); } } this.node=(Node)stack.pop(); } public int compute() return node.interpret(); } }
客戶端:Client.java
public class Client{ public static void main(String args[]){ String statement = "3 * 2 * 4 / 6 % 5"; Calculator calculator = new Calculator(); calculator.build(statement); int result = calculator.compute(); System.out.println(statement + " = " + result); } }
運行結果:3 * 2 * 4 / 6 % 5 = 4
四、 模式優缺點
優點
1、 可擴展性比較好,靈活。
2、 增加了新的解釋表達式的方式。
3、 易於實現文法。
缺點
1、 執行效率比較低,可利用場景比較少。
2、 對於復雜的文法比較難維護。
五、 模式適用場景
1、可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹。
2、一些重復出現的問題可以用一種簡單的語言來進行表達。
3、文法較為簡單。
六、 模式總結
1、在解釋器模式中由於語法是由很多類表示的,所以可擴展性強。
2、雖然解釋器的可擴展性強,但是如果語法規則的數目太大的時候,該模式可能就會變得異常復雜。所以解釋器模式適用於文法較為簡單的。
3、解釋器模式可以處理腳本語言和編程語言。常用於解決某一特定類型的問題頻繁發生情況。
對於這個模式由於LZ水平有限,理解不是很清楚,而且案例實在比較少,所以博文主要以摘抄、記錄為主,有點兒囫圇吞棗!!望諒解….