Java設計模式之java解釋器模式詳解
介紹
解釋器模式(Interpreter Pattern):定義一個語言的文法,并且建立一個解釋器來解釋該語言中的句子,這里的 “語言” 是指使用規(guī)定格式和語法的代碼。解釋器模式是一種類行為型模式。
角色
AbstractExpression(抽象解釋器):在抽象表達式中聲明了抽象的解釋操作,具體的解釋任務由各個實現(xiàn)類完成,它是所有終結(jié)符表達式和非終結(jié)符表達式的公共父類。
TerminalExpression(終結(jié)符表達式):實現(xiàn)與文法中的元素相關聯(lián)的解釋操作,通常一個解釋器模式中只有一個終結(jié)表達式,但有多個實例,對應不同的終結(jié)符。
NonterminalExpression(非終結(jié)符表達式):文法中的每條規(guī)則對應于一個非終結(jié)表達式,非終結(jié)符表達式根據(jù)邏輯的復雜程度而增加,原則上每個文法規(guī)則都對應一個非終結(jié)符表達式
Context(環(huán)境類):環(huán)境類又稱為上下文類,它用于存儲解釋器之外的一些全局信息,通常它臨時存儲了需要解釋的語句。
客戶類(Test): 客戶端,解析表達式,構(gòu)建抽象語法樹,執(zhí)行具體的解釋操作等.

計算器案例
環(huán)境類,存儲解釋器之外的一些全局信息,通常它臨時存儲了需要解釋的語句
public class Context
{
private Map<Expression, Integer> map = new HashMap<>();
//定義變量
public void add(Expression s, Integer value)
{
map.put(s, value);
}
//將變量轉(zhuǎn)換成數(shù)字
public int lookup(Expression s){
return map.get(s);
}
}
解釋器接口
public interface Expression
{
int interpreter(Context context);//一定會有解釋方法
}
抽象非終結(jié)符表達式
public abstract class NonTerminalExpression implements Expression{
Expression e1,e2;
public NonTerminalExpression(Expression e1, Expression e2){
this.e1 = e1;
this.e2 = e2;
}
}
減法表達式實現(xiàn)類
public class MinusOperation extends NonTerminalExpression {
public MinusOperation(Expression e1, Expression e2) {
super(e1, e2);
}
//將兩個表達式相減
@Override
public int interpreter(Context context) {
return this.e1.interpreter(context) - this.e2.interpreter(context);
}
}
加法表達式實現(xiàn)類
public class PlusOperation extends NonTerminalExpression {
public PlusOperation(Expression e1, Expression e2) {
super(e1, e2);
}
//將兩個表達式相加
@Override
public int interpreter(Context context) {
return this.e1.interpreter(context) + this.e2.interpreter(context);
}
}
終結(jié)符表達式(在這個例子,用來存放數(shù)字,或者代表數(shù)字的字符)
public class TerminalExpression implements Expression{
String variable;
public TerminalExpression(String variable){
this.variable = variable;
}
//獲得該變量的值
@Override
public int interpreter(Context context) {
return context.lookup(this);
}
}
測試:
public class Test {
public static void main(String[] args) {
Context context = new Context();
TerminalExpression a = new TerminalExpression("a");
TerminalExpression b = new TerminalExpression("b");
TerminalExpression c = new TerminalExpression("c");
context.add(a, 4);
context.add(b, 8);
context.add(c, 2);
//new PlusOperation(a,b).interpreter(context)--->返回12
// c.interpreter(context)--->2
//MinusOperation(12,2)..interpreter(context)--->10
System.out.println(new MinusOperation(new PlusOperation(a,b), c).interpreter(context));
}
}

UML圖

深入挖掘
非終結(jié)符表達式(相當于樹的樹杈):在這個例子中就是相加,相減的表達式,稱為非終結(jié)符,這是非常形象的,因為當運算遇到這類的表達式的時候,必須先把非終結(jié)符的結(jié)果計算出來,猶如剝繭一般,一層一層的調(diào)用,就比如上面的
new MinusOperation(new PlusOperation(a,b), c).interpreter(context)
這個MinusOperation左邊參數(shù)是new PlusOperation(a,b),是非終結(jié)符表達式,所以要調(diào)用PlusOperation,因為PlusOperation的左右兩邊都是TerminalExpression,是終結(jié)符表達式,所以計算然后返回,到最外面的MinusOperation函數(shù),發(fā)現(xiàn)右邊c是終結(jié)符表達式,所以可以計算。
終結(jié)符表達式(相當于樹的葉子):遇到這個表達式interpreter執(zhí)行能直接返回結(jié)果,不會向下繼續(xù)調(diào)用。
構(gòu)建的語法樹

葉子節(jié)點即為終結(jié)符,樹杈即為非終結(jié)符,遇到非終結(jié)符要繼續(xù)往下解析,遇到終結(jié)符則返回。a+b-c(4+8-2)
上面的語法樹是手動建的(new MinusOperation(new PlusOperation(a,b), c).interpreter(context)),實際情況,客戶輸入的都是(4+8-2)這樣的式子,所以,接下來寫的就是解析的輸入式子然后自動構(gòu)建語法樹,然后計算結(jié)果.
public class Context {
private Map<Expression, Integer> map = new HashMap<>();
public void add(Expression s, Integer value){
map.put(s, value);
}
public Integer lookup(Expression s){
return map.get(s);
}
//構(gòu)建語法樹的主要方法
public static Expression build(String str) {
//主要利用棧來實現(xiàn)
Stack<Expression> objects = new Stack<>();
for (int i = 0; i < str.length(); i++){
char c = str.charAt(i);
//遇到運算符號+號時候
if (c == '+'){
//先出棧
Expression pop = objects.pop();
//將運算結(jié)果入棧
objects.push(new PlusOperation(pop, new TerminalExpression(String.valueOf(str.charAt(++i)))));
} else if (c == '-'){
//遇到減號類似加號
Expression pop = objects.pop();
objects.push(new MinusOperation(pop, new TerminalExpression(String.valueOf(str.charAt(++i)))));
} else {
//遇到非終結(jié)符直接入棧(基本就是第一個數(shù)字的情況)
objects.push(new TerminalExpression(String.valueOf(str.charAt(i))));
}
}
//把最后的棧頂元素返回
return objects.pop();
}
}
public class TerminalExpression implements Expression {
String variable;
public TerminalExpression(String variable){
this.variable = variable;
}
@Override
public int interpreter(Context context) {
//因為要兼容之前的版本
Integer lookup = context.lookup(this);
if (lookup == null)
//若在map中能找到對應的數(shù)則返回
return Integer.valueOf(variable);
//找不到則直接返回(認為輸入的就是數(shù)字)
return lookup;
}
}
public class Test {
public static void main(String[] args) {
Context context = new Context();
TerminalExpression a = new TerminalExpression("a");
TerminalExpression b = new TerminalExpression("b");
TerminalExpression c = new TerminalExpression("c");
String str = "4+8-2+9+9-8";
Expression build = Context.build(str);
System.out.println("4+8-2+9+9-8=" + build.interpreter(context));
context.add(a, 4);
context.add(b, 8);
context.add(c, 2);
System.out.println(new MinusOperation(new PlusOperation(a,b), c).interpreter(context));
}
}
解釋器模式總結(jié)
解釋器模式為自定義語言的設計和實現(xiàn)提供了一種解決方案,它用于定義一組文法規(guī)則并通過這組文法規(guī)則來解釋語言中的句子。雖然解釋器模式的使用頻率不是特別高,但是它在正則表達式、XML文檔解釋等領域還是得到了廣泛使用。
主要優(yōu)點
- 易于改變和擴展文法。由于在解釋器模式中使用類來表示語言的文法規(guī)則,因此可以通過繼承等機制來改變或擴展文法。
- 每一條文法規(guī)則都可以表示為一個類,因此可以方便地實現(xiàn)一個簡單的語言。
- 實現(xiàn)文法較為容易。在抽象語法樹中每一個表達式節(jié)點類的實現(xiàn)方式都是相似的,這些類的代碼編寫都不會特別復雜,還可以通過一些工具自動生成節(jié)點類代碼。
- 增加新的解釋表達式較為方便。如果用戶需要增加新的解釋表達式只需要對應增加一個新的終結(jié)符表達式或非終結(jié)符表達式類,原有表達式類代碼無須修改,符合"開閉原則"。
主要缺點
- 對于復雜文法難以維護。在解釋器模式中,每一條規(guī)則至少需要定義一個類,因此如果一個語言包含太多文法規(guī)則,類的個數(shù)將會急劇增加,導致系統(tǒng)難以管理和維護,此時可以考慮使用語法分析程序等方式來取代解釋器模式。
- 執(zhí)行效率較低。由于在解釋器模式中使用了大量的循環(huán)和遞歸調(diào)用,因此在解釋較為復雜的句子時其速度很慢,而且代碼的調(diào)試過程也比較麻煩。
適用場景
- 可以將一個需要解釋執(zhí)行的語言中的句子表示為一個抽象語法樹。
- 一些重復出現(xiàn)的問題可以用一種簡單的語言來進行表達。
- 一個語言的文法較為簡單。
- 對執(zhí)行效率要求不高。
解釋器模式的典型應用
Spring EL表達式中的解釋器模式
在下面的類圖中,Expression是一個接口,相當于我們解釋器模式中的非終結(jié)符表達式,而ExpressionParser相當于終結(jié)符表達式。根據(jù)不同的Parser對象,返回不同的Expression對象

Expression接口:
//抽象的非終結(jié)符表達式
public interface Expression {
Object getValue() throws EvaluationException;
Object getValue(Object rootObject) throws EvaluationException;
}
SpelExpression類:
//具體的非終結(jié)符表達式
public class SpelExpression implements Expression {
@Override
public Object getValue() throws EvaluationException {
Object result;
if (this.compiledAst != null) {
try {
TypedValue contextRoot = evaluationContext == null ? null : evaluationContext.getRootObject();
return this.compiledAst.getValue(contextRoot == null ? null : contextRoot.getValue(), evaluationContext);
}
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.interpretedCount = 0;
this.compiledAst = null;
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
}
}
}
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration);
result = this.ast.getValue(expressionState);
checkCompile(expressionState);
return result;
}
}
CompositeStringExpression:
//具體的非終結(jié)符表達式
public class CompositeStringExpression implements Expression {
@Override
public String getValue() throws EvaluationException {
StringBuilder sb = new StringBuilder();
for (Expression expression : this.expressions) {
String value = expression.getValue(String.class);
if (value != null) {
sb.append(value);
}
}
return sb.toString();
}
}
ExpressionParser接口:
public interface ExpressionParser {
//解析表達式
Expression parseExpression(String expressionString) throws ParseException;
Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
}
TemplateAwareExpressionParser類:
public abstract class TemplateAwareExpressionParser implements ExpressionParser {
@Override
public Expression parseExpression(String expressionString) throws ParseException {
return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT);
}
//根據(jù)不同的parser返回不同的Expression對象
@Override
public Expression parseExpression(String expressionString, ParserContext context)
throws ParseException {
if (context == null) {
context = NON_TEMPLATE_PARSER_CONTEXT;
}
if (context.isTemplate()) {
return parseTemplate(expressionString, context);
}
else {
return doParseExpression(expressionString, context);
}
}
private Expression parseTemplate(String expressionString, ParserContext context)
throws ParseException {
if (expressionString.length() == 0) {
return new LiteralExpression("");
}
Expression[] expressions = parseExpressions(expressionString, context);
if (expressions.length == 1) {
return expressions[0];
}
else {
return new CompositeStringExpression(expressionString, expressions);
}
}
//抽象的,由子類去實現(xiàn)
protected abstract Expression doParseExpression(String expressionString,
ParserContext context) throws ParseException;
}
SpelExpressionParser類:
public class SpelExpressionParser extends TemplateAwareExpressionParser {
@Override
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
//這里返回了一個InternalSpelExpressionParser,
return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context);
}
}
InternalSpelExpressionParser類:
class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@Override
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
try {
this.expressionString = expressionString;
Tokenizer tokenizer = new Tokenizer(expressionString);
tokenizer.process();
this.tokenStream = tokenizer.getTokens();
this.tokenStreamLength = this.tokenStream.size();
this.tokenStreamPointer = 0;
this.constructedNodes.clear();
SpelNodeImpl ast = eatExpression();
if (moreTokens()) {
throw new SpelParseException(peekToken().startPos, SpelMessage.MORE_INPUT, toString(nextToken()));
}
Assert.isTrue(this.constructedNodes.isEmpty());
return new SpelExpression(expressionString, ast, this.configuration);
}
catch (InternalParseException ex) {
throw ex.getCause();
}
}
}
參考文章
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內(nèi)容!
相關文章
使用SpringBoot簡單了解Druid的監(jiān)控系統(tǒng)的配置方法
這篇文章主要介紹了使用SpringBoot簡單了解Druid的監(jiān)控系統(tǒng)的配置,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06
解析spring事務管理@Transactional為什么要添加rollbackFor=Exception.class
這篇文章主要介紹了spring事務管理@Transactional為什么要添加rollbackFor=Exception.class,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-11-11
Java Scaner類詳解_動力節(jié)點Java學院整理
Java.util.Scanner是Java5.0的新特征,主要功能是簡化文本掃描。下面通過本文給大家分享java scaner類相關知識,需要的朋友下吧2017-04-04
SpringBoot整合Spring Security構(gòu)建安全的Web應用
pring Security是一個強大的身份驗證和訪問控制框架,本文主要介紹了SpringBoot整合Spring Security構(gòu)建安全的Web應用,具有一定的參考價值,感興趣的可以了解一下2024-01-01
java 遍歷Map及Map轉(zhuǎn)化為二維數(shù)組的實例
這篇文章主要介紹了java 遍歷Map及Map轉(zhuǎn)化為二維數(shù)組的實例的相關資料,希望通過本文能幫助到大家,實現(xiàn)這樣的功能,需要的朋友可以參考下2017-08-08
詳解SpringBoot Controller接收參數(shù)的幾種常用方式
這篇文章主要介紹了詳解SpringBoot Controller接收參數(shù)的幾種常用方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10

