設計模式-解釋器模式(Interpreter)-Java

設計模式-解釋器模式(Interpreter)-Java


目錄




內容

1、前言

  有朋友一直在等待我的解釋器模式文稿,微笑,現把某個版本發在博客上,歡迎大家討論!

  雖然目前計算機編程語言有好幾百種,但有時候我們還是希望能用一些簡單的語言來實現一些特定的操作,我們只要向計算機輸入一個句子或文件,它就能夠按照預先定義的文法規則來對句子或文件進行解釋,從而實現相應的功能。例如提供一個簡單的加法/減法解釋器,只要輸入一個加法/減法表達式,它就能夠計算出表達式結果,如圖18-1所示,當輸入字符串表達式爲“1 + 2 + 3 – 4 + 1”時,將輸出計算結果爲3。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-f0RxFRlb-1592140378426)(./images/加法-減法解釋器.png)]
圖1-1 加法/減法解釋器示意圖

  我們知道,像C++、Java和C#等語言無法直接解釋類似“1+ 2 + 3 – 4 + 1”這樣的字符串(如果直接作爲數值表達式時可以解釋),我們必須自己定義一套文法規則來實現對這些語句的解釋,即設計一個自定義語言。在實際開發中,這些簡單的自定義語言可以基於現有的編程語言來設計,如果所基於的編程語言是面嚮對象語言,此時可以使用解釋器模式來實現自定義語言。

2、示例案例-機器人控制程序

  Sunny軟件公司欲爲某玩具公司開發一套機器人控制程序,在該機器人控制程序中包含一些簡單的英文控制指令,每一個指令對應一個表達式(expression),該表達式可以是簡單表達式也可以是複合表達式,每一個簡單表達式由移動方向(direction),移動方式(action)和移動距離(distance)三部分組成,其中移動方向包括上(up)、下(down)、左(left)、右(right);移動方式包括移動(move)和快速移動(run);移動距離爲一個正整數。兩個表達式之間可以通過與(and)連接,形成複合(composite)表達式。

  用戶通過對圖形化的設置界面進行操作可以創建一個機器人控制指令,機器人在收到指令後將按照指令的設置進行移動,例如輸入控制指令:up move 5,則“向上移動5個單位”;輸入控制指令:down run 10 and left move 20,則“向下快速移動10個單位再向左移動20個單位”。

  Sunny軟件公司開發人員決定自定義一個簡單的語言來解釋機器人控制指令,根據上述需求描述,用形式化語言來表示該簡單語言的文法規則如下:

expression ::= direction action distance | composite //表達式
composite ::= expression 'and' expression //複合表達式
direction ::= 'up' | 'down' | 'left' | 'right' //移動方向
action ::= 'move' | 'run' //移動方式
distance ::= an integer //移動距離

  上述語言一共定義了五條文法規則,對應五個語言單位,這些語言單位可以分爲兩類,一類爲終結符(也稱爲終結符表達式),例如direction、action和distance,它們是語言的最小組成單位,不能再進行拆分;另一類爲非終結符(也稱爲非終結符表達式),例如expression和composite,它們都是一個完整的句子,包含一系列終結符或非終結符。

 emsp;我們根據上述規則定義出的語言可以構成很多語句,計算機程序將根據這些語句進行某種操作。爲了實現對語句的解釋,可以使用解釋器模式,在解釋器模式中每一個文法規則都將對應一個類,擴展、改變文法以及增加新的文法規則都很方便,下面就讓我們正式進入解釋器模式的學習,看看使用解釋器模式如何來實現對機器人控制指令的處理。

3、文法規則和抽象語法樹

  解釋器模式描述瞭如何爲簡單的語言定義一個文法,如何在該語言中表示一個句子,以及如何解釋這些句子。在正式分析解釋器模式結構之前,我們先來學習如何表示一個語言的文法規則以及如何構造一棵抽象語法樹。

  在前面所提到的加法/減法解釋器中,每一個輸入表達式,例如“1 + 2 + 3 – 4 + 1”,都包含了三個語言單位,可以使用如下文法規則來定義:

expression ::= value | operation
operation ::= expression '+' expression | expression '-' expression
value ::= an integer //一個整數值

  該文法規則包含三條語句,第一條表示表達式的組成方式,其中value和operation是後面兩個語言單位的定義,每一條語句所定義的字符串如operation和value稱爲語言構造成分或語言單位,符號“::=”表示“定義爲”的意思,其左邊的語言單位通過右邊來進行說明和定義,語言單位對應終結符表達式和非終結符表達式。如本規則中的operation是非終結符表達式,它的組成元素仍然可以是表達式,可以進一步分解,而value是終結符表達式,它的組成元素是最基本的語言單位,不能再進行分解。

  在文法規則定義中可以使用一些符號來表示不同的含義,如使用“|”表示或,使用“{”和“}”表示組合,使用“*”表示出現0次或多次等,其中使用頻率最高的符號是表示“或”關係的“|”,如文法規則“boolValue ::= 0 | 1”表示終結符表達式boolValue的取值可以爲0或者1。除了使用文法規則來定義一個語言,在解釋器模式中還可以通過一種稱之爲抽象語法樹(Abstract Syntax Tree, AST)的圖形方式來直觀地表示語言的構成,每一棵抽象語法樹對應一個語言實例,如加法/減法表達式語言中的語句“1+ 2 + 3 – 4 + 1”,可以通過如圖18-2所示抽象語法樹來表示:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-F1HdFIdl-1592140378430)(./images/抽象語法樹示意圖.png)]
圖3-1 抽象語法樹示意圖

  在該抽象語法樹中,可以通過終結符表達式value和非終結符表達式operation組成複雜的語句,每個文法規則的語言實例都可以表示爲一個抽象語法樹,即每一條具體的語句都可以用類似圖3-1所示的抽象語法樹來表示,在圖中終結符表達式類的實例作爲樹的葉子節點,而非終結符表達式類的實例作爲非葉子節點,它們可以將終結符表達式類的實例以及包含終結符和非終結符實例的子表達式作爲其子節點。抽象語法樹描述瞭如何構成一個複雜的句子,通過對抽象語法樹的分析,可以識別出語言中的終結符類和非終結符類。

4、解釋器模式概述

  解釋器模式是一種使用頻率相對較低單學習難度很大的設計模式,它用於描述如何使用面嚮對象語言構成一個簡單的語言解釋器。在某些情況下,爲了更好地描述某一些特定類型的問題,我們可以創建一種新的語言,這種語言擁有自己的表達式和結構,既文法規則。對解釋器模式的學習能夠加深對面向對象思想的理解,並且掌握編程語言中文法規則的解釋過程。

4.1、解釋器模式定義

  • 解釋器模式(Interpreter Pattern):定義一個語言的文法,並且建立一個解釋器來解釋該語言中的句子,這裏的“語言”是指使用規定格式和語法的代碼。解釋器模式是一種類行爲型模式。

4.2、解釋器模式結構

  由於表達式可分爲終結符表達式和非終結符表達式,因此解釋器模式的結構與組合模式的結構有些類似,但在解釋器模式中包含更多的組成元素,它的結構如圖4.2-1所示:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5z4Gy1Pq-1592140378431)(./images/model_interpreter.png)]
圖4.2-1 解釋器模式結構圖

4.3、解釋器模式結構圖中角色

  在解釋器模式結構圖中包含如下幾個角色:

  • AbstractExpression(抽象表達式):在抽象表達式中聲明瞭抽象的解釋操作,它是所有終結符表達式和非終結符表達式的公共父類。
  • TerminalExpression(終結符表達式):終結符表達式是抽象表達式的子類,它實現了與文法中的終結符相關聯的解釋操作,在句子中的每一個終結符都是該類的一個實例。通常在一個解釋器模式中只有少數幾個終結符表達式類,它們的實例可以通過非終結符表達式組成較爲複雜的句子。
  • NonterminalExpression(非終結符表達式):非終結符表達式也是抽象表達式的子類,它實現了文法中非終結符的解釋操作,由於在非終結符表達式中可以包含終結符表達式,也可以繼續包含非終結符表達式,因此其解釋操作一般通過遞歸的方式來完成。
  • Context(環境類):環境類又稱爲上下文類,它用於存儲解釋器之外的一些全局信息,通常它臨時存儲了需要解釋的語句。

4.4、解釋器模式典型實現

  在解釋器模式中,每一種終結符和非終結符都有一個具體類與之對應,正因爲使用類來表示每一條文法規則,所以系統將具有較好的靈活性和可擴展性。對於所有的終結符和非終結符,我們首先需要抽象出一個公共父類,即抽象表達式類,其典型代碼如下所示:

abstract class AbstractExpression {
public abstract void interpret(Context ctx);
}

  終結符表達式和非終結符表達式類都是抽象表達式類的子類,對於終結符表達式,其代碼很簡單,主要是對終結符元素的處理,其典型代碼如下所示:

class TerminalExpression extends AbstractExpression {
public void interpret(Context ctx) {
//終結符表達式的解釋操作
}
}

  對於非終結符表達式,其代碼相對比較複雜,因爲可以通過非終結符將表達式組合成更加複雜的結構,對於包含兩個操作元素的非終結符表達式類,其典型代碼如下:

class NonterminalExpression extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
public NonterminalExpression(AbstractExpression left,AbstractExpression right) {
this.left=left;
this.right=right;
}
public void interpret(Context ctx) {
//遞歸調用每一個組成部分的interpret()方法
//在遞歸調用時指定組成部分的連接方式,即非終結符的功能
}
}

  除了上述用於表示表達式的類以外,通常在解釋器模式中還提供了一個環境類Context,用於存儲一些全局信息,通常在Context中包含了一個HashMap或ArrayList等類型的集合對象(也可以直接由HashMap等集合類充當環境類),存儲一系列公共信息,如變量名與值的映射關係(key/value)等,用於在進行具體的解釋操作時從中獲取相關信息。其典型代碼片段如下:

class Context {
private HashMap map = new HashMap();
public void assign(String key, String value) {
//往環境類中設值
}
public String lookup(String key) {
//獲取存儲在環境類中的值
}

}

  當系統無須提供全局公共信息時可以省略環境類,可根據實際情況決定是否需要環境類。

5、解釋器模式完整解決方案

  爲了能夠解釋機器人控制指令,Sunny軟件公司開發人員使用解釋器模式來設計和實現機器人控制程序。針對五條文法規則,分別提供五個類來實現,其中終結符表達式direction、action和distance對應DirectionNode類、ActionNode類和DistanceNode類,非終結符表達式expression和composite對應SentenceNode類和AndNode類。

  我們可以通過抽象語法樹來表示具體解釋過程,例如機器人控制指令“down run 10 and leftmove 20”對應的抽象語法樹如圖5-1所示
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-tCXyzMZi-1592140378432)(./images/機器人控制程序抽象語法樹.png)]
圖5-1 機器人控制程序抽象語法樹示例

  機器人控制程序實例基本結構如圖5-2所示:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Rc1cvlHk-1592140378433)(./images/機器人控制程序結構圖.png)]
圖5-2 機器人控制程序結構圖

  在圖5-2中,AbstractNode充當抽象表達式角色,DirectionNode、ActionNode和DistanceNode充當終結符表達式角色,AndNode和SentenceNode充當非終結符表達式角色。完整代碼如下所示:

  • AbstractNode類代碼5-1:抽象表達式

      package interpreter;
    
      //抽象表達式
      public abstract class AbstractNode {
      	public abstract String interpret();
      }
    
  • AndNode類代碼5-2:And解釋-非終結符表達式

      package interpreter;
    
      //And解釋:非終結符表達式
      public class AndNode extends AbstractNode {
      	private AbstractNode left; //And的左表達式
      	private AbstractNode right; //And的右表達式
      	public AndNode(AbstractNode left, AbstractNode right) {
      		this.left = left;
      		this.right = right;
      	}
      	//And表達式解釋操作
      	public String interpret() {
      		return left.interpret() + "再" + right.interpret();
      	}
      }
    
  • SentenceNode類代碼5-3:簡單句子解釋-非終結符表達式

      package interpreter;
    
      //簡單句子解釋:非終結符表達式
      public class SentenceNode extends AbstractNode {
      	private AbstractNode direction;
      	private AbstractNode action;
      	private AbstractNode distance;
      	public SentenceNode(AbstractNode direction,AbstractNode action,AbstractNode distance) {
      		this.direction = direction;
      		this.action = action;
      		this.distance = distance;
      	}
      	//簡單句子的解釋操作
      	public String interpret() {
      		return direction.interpret() + action.interpret() + distance.interpret();
      	}
      }
    
  • ActionNode類代碼5-4:動作解釋-終結符表達式

      package interpreter;
    
      //動作解釋:終結符表達式
      public class ActionNode extends AbstractNode{
      	private String action;
      	public ActionNode(String action) {
      		this.action = action;
      	}
      	//動作(移動方式)表達式的解釋操作
      	public String interpret() {
      		if (action.equalsIgnoreCase("move")) 
      			return "移動";
      		else if (action.equalsIgnoreCase("run"))
      			return "快速移動";
      		else
      			return "無效指令";
      	}
      }
    
  • DirectionNode類代碼5-5:方向解釋-終結符表達式

      package interpreter;
    
      //方向解釋:終結符表達式
      public class DirectionNode extends AbstractNode {
      	private String direction;
      	public DirectionNode(String direction) {
      	this.direction = direction;
      	}
      	//方向表達式的解釋操作
      	public String interpret() {
      		switch(this.direction) {
      			case "up":
      				return "向上";
      			case "down":
      				return "向下";
      			case "left":
      				return "向左";
      			case "right":
      				return "向右";
      			default:
      				return "無效指令";
      		}
      //		if (direction.equalsIgnoreCase("up"))
      //			return "向上";
      //		else if (direction.equalsIgnoreCase("down"))
      //			return "向下";
      //		else if (direction.equalsIgnoreCase("left"))
      //			return "向左";
      //		else if (direction.equalsIgnoreCase("right"))
      //			return "向右";
      //		else
      //			return "無效指令";
      	}
      }
    
  • DistanceNode類代碼5-6:距離解釋-終結符表達式

      package interpreter;
    
      //距離解釋:終結符表達式
      public class DistanceNode extends AbstractNode {
      	private String distance;
      	public DistanceNode(String distance) {
      		this.distance = distance;
      	}
      	//距離表達式的解釋操作
      	public String interpret() {
      		return this.distance;
      	}
      }
    
  • Instructionhandler類代碼5-7:指令處理類-工具類

      package interpreter;
    
      import java.util.Stack;
    
      //指令處理類:工具類
      public class InstructionHandler {
      	private String instruction;
      	private AbstractNode node;
    
      	public void handle(String instruction) {
      		AbstractNode left = null, right = null;
      		AbstractNode direction = null, action = null, distance = null;
      		Stack<AbstractNode> stack = new Stack<>(); //聲明一個棧對象用於存儲抽象語法樹
      		String[] words = instruction.split(" "); //以空格分隔指令字符串
      		for (int i = 0; i < words.length; i++) {
      		//本實例採用棧的方式來處理指令,如果遇到“and”,則將其後的三個單詞作爲三個終結符表達式連成一個簡單句子SentenceNode作爲“and”的右表達式,而將從棧頂彈出的表達式作爲“and”的左表達式,最後將新的“and”表達式壓入棧中。
      			if (words[i].equalsIgnoreCase("and")) {
      				left = (AbstractNode)stack.pop(); //彈出棧頂表達式作爲左表達式
      				String word1= words[++i];
      				direction = new DirectionNode(word1);
      				String word2 = words[++i];
      				action = new ActionNode(word2);
      				String word3 = words[++i];
      				distance = new DistanceNode(word3);
      				right = new SentenceNode(direction,action,distance); //右表達式
      				stack.push(new AndNode(left,right)); //將新表達式壓入棧中
      			} else {
      				//如果是從頭開始進行解釋,則將前三個單詞組成一個簡單句子SentenceNode並將該句子壓入棧中
      				String word1 = words[i];
      				direction = new DirectionNode(word1);
      				String word2 = words[++i];
      				action = new ActionNode(word2);
      				String word3 = words[++i];
      				distance = new DistanceNode(word3);
      				left = new SentenceNode(direction,action,distance);
      				stack.push(left); //將新表達式壓入棧中
      			}
      		}
      		this.node = (AbstractNode)stack.pop(); //將全部表達式從棧中彈出
      	}
    
      	public String output() {
      		String result = node.interpret(); //解釋表達式
      		return result;
      	}
      }
    
  • Client類代碼5-8:客戶端

      package interpreter;
    
      public class Client {
      	public static void main(String args[]) {
      		String instruction = "up move 5 and down run 10 and left move 5";
      		InstructionHandler handler = new InstructionHandler();
      		handler.handle(instruction);
      		String outString;
      		outString = handler.output();
      		System.out.println(outString);
      	}
      }
    
  • 測試結果:

      向上移動5再向下快速移動10再向左移動5
    

6、Context的作用

  在解釋器模式中,環境類Context用於存儲解釋器之外的一些全局信息,它通常作爲參數被傳遞到所有表達式的解釋方法interpret()中,可以在Context對象中存儲和訪問表達式解釋器的狀態,向表達式解釋器提供一些全局的、公共的數據,此外還可以在Context中增加一些所有表達式解釋器都共有的功能,減輕解釋器的職責。

  在上面的機器人控制程序實例中,我們省略了環境類角色,下面再通過一個簡單實例來說明環境類的用途:

  Sunny軟件公司開發了一套簡單的基於字符界面的格式化指令,可以根據輸入的指令在字符界面中輸出一些格式化內容,例如輸入“LOOP 2 PRINT楊過 SPACE SPACE PRINT 小龍女 BREAK END PRINT郭靖 SPACE SPACE PRINT 黃蓉”,將輸出如下結果:

楊過 小龍女
楊過 小龍女
郭靖 黃蓉

  其中關鍵詞LOOP表示“循環”,後面的數字表示循環次數;PRINT表示“打印”,後面的字符串表示打印的內容;SPACE表示“空格”;BREAK表示“換行”;END表示“循環結束”。每一個關鍵詞對應一條命令,計算機程序將根據關鍵詞執行相應的處理操作。

  現使用解釋器模式設計並實現該格式化指令的解釋,對指令進行分析並調用相應的操作執行指令中每一條命令。

  Sunny軟件公司開發人員通過分析,根據該格式化指令中句子的組成,定義瞭如下文法規則:

expression ::= command* //表達式,一個表達式包含多條命令
command ::= loop | primitive //語句命令
loop ::= 'loopnumber' expression 'end' //循環命令,其中number爲自然數
primitive ::= 'printstring' | 'space' | 'break' //基本命令,其中string爲字符串

  根據以上文法規則,通過進一步分析,繪製如圖6-1所示結構圖:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-luhDQWOF-1592140378434)(./images/格式化指令.png)]
圖6-1 格式化指令結構圖

在圖6-1中,Context充當環境角色,Node充當抽象表達式角色,ExpressionNode、CommandNode和LoopCommandNode充當非終結符表達式角色,PrimitiveCommandNode充當終結符表達式角色。完整代碼如下所示:

import java.util.*;
//環境類:用於存儲和操作需要解釋的語句,在本實例中每一個需要解釋的單詞可以稱爲一個動作標記(Action Token)或命令
class Context {
private StringTokenizer tokenizer; //StringTokenizer類,用於將字符串分解爲更小的字符串標記(Token),默認情況下以空格作爲分隔符
private String currentToken; //當前字符串標記
public Context(String text) {
tokenizer = new StringTokenizer(text); //通過傳入的指令字符串創建StringTokenizer對象
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) {
if (!token.equals(currentToken)) {
System.err.println("錯誤提示:" + currentToken + "解釋錯誤!");
}
nextToken();
}
//如果當前的標記是一個數字,則返回對應的數值
public int currentNumber() {
int number = 0;
try{
number = Integer.parseInt(currentToken); //將字符串轉換爲整數
}
catch(NumberFormatException e) {
System.err.println("錯誤提示:" + e);
}
return number;
}
}
//抽象節點類:抽象表達式
abstract class Node {
public abstract void interpret(Context text); //聲明一個方法用於解釋語句
public abstract void execute(); //聲明一個方法用於執行標記對應的命令
}
//表達式節點類:非終結符表達式
class ExpressionNode extends Node {
private ArrayList<Node> list = new ArrayList<Node>(); //定義一個集合用於存儲多條命令
public void interpret(Context context) {
//循環處理Context中的標記
while (true){
//如果已經沒有任何標記,則退出解釋
if (context.currentToken() == null) {
break;
}
//如果標記爲END,則不解釋END並結束本次解釋過程,可以繼續之後的解釋
else if (context.currentToken().equals("END")) {
context.skipToken("END");
break;
}
//如果爲其他標記,則解釋標記並將其加入命令集合
else {
Node commandNode = new CommandNode();
commandNode.interpret(context);
list.add(commandNode);
}
}
}
//循環執行命令集合中的每一條命令
public void execute() {
Iterator iterator = list.iterator();
while (iterator.hasNext()){
((Node)iterator.next()).execute();
}
}
}
//語句命令節點類:非終結符表達式
class CommandNode extends Node {
private Node node;
public void interpret(Context context) {
//處理LOOP循環命令
if (context.currentToken().equals("LOOP")) {
node = new LoopCommandNode();
node.interpret(context);
}
//處理其他基本命令
else {
node = new PrimitiveCommandNode();
node.interpret(context);
}
}
public void execute() {
node.execute();
}
}
//循環命令節點類:非終結符表達式
class LoopCommandNode extends Node {
private int number; //循環次數
private Node commandNode; //循環語句中的表達式
//解釋循環命令
public void interpret(Context context) {
context.skipToken("LOOP");
number = context.currentNumber();
context.nextToken();
commandNode = new ExpressionNode(); //循環語句中的表達式
commandNode.interpret(context);
}
public void execute() {
for (int i=0;i<number;i++)
commandNode.execute();
}
}
//基本命令節點類:終結符表達式
class PrimitiveCommandNode extends Node {
private String name;
private String text;
//解釋基本命令
public void interpret(Context context) {
name = context.currentToken();
context.skipToken(name);
if (!name.equals("PRINT") && !name.equals("BREAK") && !name.equals ("SPACE")){
System.err.println("非法命令!");
}
if (name.equals("PRINT")){
text = context.currentToken();
context.nextToken();
}
}
public void execute(){
if (name.equals("PRINT"))
System.out.print(text);
else if (name.equals("SPACE"))
System.out.print(" ");
else if (name.equals("BREAK"))
System.out.println();
}
}

  在本實例代碼中,環境類Context類似一個工具類,它提供了用於處理指令的方法,如nextToken()、currentToken()、skipToken()等,同時它存儲了需要解釋的指令並記錄了每一次解釋的當前標記(Token),而具體的解釋過程交給表達式解釋器類來處理。我們還可以將各種解釋器類包含的公共方法移至環境類中,更好地實現這些方法的重用和擴展。

  針對本實例代碼,我們編寫如下客戶端測試代碼:

class Client{
public static void main(String[] args){
String text = "LOOP 2 PRINT 楊過 SPACE SPACE PRINT 小龍女 BREAK END PRINT 郭靖 SPACE SPACE PRINT 黃蓉";
Context context = new Context(text);
Node node = new ExpressionNode();
node.interpret(context);
node.execute();
}
}

編譯並運行程序,輸出結果如下:

楊過 小龍女
楊過 小龍女
郭靖 黃蓉

7、總結

  解釋器模式爲自定義語言的設計和實現提供了一種解決方案,它用於定義一組文法規則並通過這組文法規則來解釋語言中的句子。雖然解釋器模式的使用頻率不是特別高,但是它在正則表達式、XML文檔解釋等領域還是得到了廣泛使用。與解釋器模式類似,目前還誕生了很多基於抽象語法樹的源代碼處理工具,例如Eclipse中的Eclipse AST,它可以用於表示Java語言的語法結構,用戶可以通過擴展其功能,創建自己的文法規則。

6.1、優缺點

  解釋器模式的主要優點如下:

  • (1) 易於改變和擴展文法。由於在解釋器模式中使用類來表示語言的文法規則,因此可以通過繼承等機制來改變或擴展文法。
  • (2) 每一條文法規則都可以表示爲一個類,因此可以方便地實現一個簡單的語言。
  • (3) 實現文法較爲容易。在抽象語法樹中每一個表達式節點類的實現方式都是相似的,這些類的代碼編寫都不會特別複雜,還可以通過一些工具自動生成節點類代碼。
  • (4) 增加新的解釋表達式較爲方便。如果用戶需要增加新的解釋表達式只需要對應增加一個新的終結符表達式或非終結符表達式類,原有表達式類代碼無須修改,符合“開閉原則”。

  解釋器模式的主要缺點如下:

  • (1) 對於複雜文法難以維護。在解釋器模式中,每一條規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數將會急劇增加,導致系統難以管理和維護,此時可以考慮使用語法分析程序等方式來取代解釋器模式。
  • (2) 執行效率較低。由於在解釋器模式中使用了大量的循環和遞歸調用,因此在解釋較爲複雜的句子時其速度很慢,而且代碼的調試過程也比較麻煩。

6.2、適用場景

  在以下情況下可以考慮使用解釋器模式:

  • (1) 可以將一個需要解釋執行的語言中的句子表示爲一個抽象語法樹。
  • (2) 一些重複出現的問題可以用一種簡單的語言來進行表達。
  • (3) 一個語言的文法較爲簡單。
  • (4) 執行效率不是關鍵問題。【注:高效的解釋器通常不是通過直接解釋抽象語法樹來實現的,而是需要將它們轉換成其他形式,使用解釋器模式的執行效率並不高。】

後記

  設計模式部分參考設計模式(劉偉).pdf,作者博客地址:https://blog.csdn.net/LoveLion
  本項目爲參考某馬視頻開發,相關視頻及配套資料可自行度娘或者聯繫本人。上面爲自己編寫的開發文檔,持續更新。歡迎交流,本人QQ:806797785

前端項目源代碼地址:https://gitee.com/gaogzhen/vue-leyou
後端JAVA源代碼地址:https://gitee.com/gaogzhen/JAVA
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章