自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

設(shè)計(jì)模式系列—解釋器模式

開(kāi)發(fā) 前端
本篇和大家一起來(lái)學(xué)習(xí)解釋器模式相關(guān)內(nèi)容。

本篇和大家一起來(lái)學(xué)習(xí)解釋器模式相關(guān)內(nèi)容。

 模式定義

給分析對(duì)象定義一個(gè)語(yǔ)言,并定義該語(yǔ)言的文法表示,再設(shè)計(jì)一個(gè)解析器來(lái)解釋語(yǔ)言中的句子。也就是說(shuō),用編譯語(yǔ)言的方式來(lái)分析應(yīng)用中的實(shí)例。這種模式實(shí)現(xiàn)了文法表達(dá)式處理的接口,該接口解釋一個(gè)特定的上下文。

這里提到的文法和句子的概念同編譯原理中的描述相同,“文法”指語(yǔ)言的語(yǔ)法規(guī)則,而“句子”是語(yǔ)言集中的元素。例如,漢語(yǔ)中的句子有很多,“我是中國(guó)人”是其中的一個(gè)句子,可以用一棵語(yǔ)法樹(shù)來(lái)直觀地描述語(yǔ)言中的句子。

模式的結(jié)構(gòu)和實(shí)現(xiàn)

解釋器模式常用于對(duì)簡(jiǎn)單語(yǔ)言的編譯或分析實(shí)例中,為了掌握好它的結(jié)構(gòu)與實(shí)現(xiàn),必須先了解編譯原理中的“文法、句子、語(yǔ)法樹(shù)”等相關(guān)概念。

文法

文法是用于描述語(yǔ)言的語(yǔ)法結(jié)構(gòu)的形式規(guī)則。沒(méi)有規(guī)矩不成方圓,例如,有些人認(rèn)為完美愛(ài)情的準(zhǔn)則是“相互吸引、感情專一、任何一方都沒(méi)有戀愛(ài)經(jīng)歷”,雖然最后一條準(zhǔn)則較苛刻,但任何事情都要有規(guī)則,語(yǔ)言也一樣,不管它是機(jī)器語(yǔ)言還是自然語(yǔ)言,都有它自己的文法規(guī)則。例如,中文中的“句子”的文法如下。

  • 〈句子〉::=〈主語(yǔ)〉〈謂語(yǔ)〉〈賓語(yǔ)〉
  • 〈主語(yǔ)〉::=〈代詞〉|〈名詞〉
  • 〈謂語(yǔ)〉::=〈動(dòng)詞〉
  • 〈賓語(yǔ)〉::=〈代詞〉|〈名詞〉
  • 〈代詞〉你|我|他
  • 〈名詞〉7大學(xué)生I筱霞I英語(yǔ)
  • 〈動(dòng)詞〉::=是|學(xué)習(xí)

注:這里的符號(hào)“::=”表示“定義為”的意思,用“〈”和“〉”括住的是非終結(jié)符,沒(méi)有括住的是終結(jié)符。

句子

句子是語(yǔ)言的基本單位,是語(yǔ)言集中的一個(gè)元素,它由終結(jié)符構(gòu)成,能由“文法”推導(dǎo)出。例如,上述文法可以推出“我是大學(xué)生”,所以它是句子。

語(yǔ)法樹(shù)

語(yǔ)法樹(shù)是句子結(jié)構(gòu)的一種樹(shù)型表示,它代表了句子的推導(dǎo)結(jié)果,它有利于理解句子語(yǔ)法結(jié)構(gòu)的層次。下圖所示是“我是大學(xué)生”的語(yǔ)法樹(shù)。

解釋器模式的結(jié)構(gòu)與組合模式相似,不過(guò)其包含的組成元素比組合模式多,而且組合模式是對(duì)象結(jié)構(gòu)型模式,而解釋器模式是類行為型模式。

模式的實(shí)現(xiàn) 

解釋器模式實(shí)現(xiàn)的關(guān)鍵是定義文法規(guī)則、設(shè)計(jì)終結(jié)符類與非終結(jié)符類、畫(huà)出結(jié)構(gòu)圖,必要時(shí)構(gòu)建語(yǔ)法樹(shù),其代碼結(jié)構(gòu)如下:

  1. package com.niuh.designpattern.interpreter.v1; 
  2.  
  3. /** 
  4.  * <p> 
  5.  * 解釋器模式 
  6.  * </p> 
  7.  */ 
  8. public class InterpreterPattern { 
  9.  
  10. //抽象表達(dá)式類 
  11. interface AbstractExpression { 
  12.     public Object interpret(String info);    //解釋方法 
  13.  
  14. //終結(jié)符表達(dá)式類 
  15. class TerminalExpression implements AbstractExpression { 
  16.     public Object interpret(String info) { 
  17.         //對(duì)終結(jié)符表達(dá)式的處理 
  18.         return null
  19.     } 
  20.  
  21. //非終結(jié)符表達(dá)式類 
  22. class NonterminalExpression implements AbstractExpression { 
  23.     private AbstractExpression exp1; 
  24.     private AbstractExpression exp2; 
  25.  
  26.     public Object interpret(String info) { 
  27.         //非對(duì)終結(jié)符表達(dá)式的處理 
  28.         return null
  29.     } 
  30.  
  31. //環(huán)境類 
  32. class Context { 
  33.     private AbstractExpression exp; 
  34.  
  35.     public Context() { 
  36.         //數(shù)據(jù)初始化 
  37.     } 
  38.  
  39.     public void operation(String info) { 
  40.         //調(diào)用相關(guān)表達(dá)式類的解釋方法 
  41.     } 

解決的問(wèn)題

對(duì)于一些固定文法構(gòu)建一個(gè)解釋句子的解釋器。 

模式組成

實(shí)例說(shuō)明

實(shí)例概況

用解釋器模式設(shè)計(jì)一個(gè)北京公交車卡的讀卡器程序。

說(shuō)明:假如北京公交車讀卡器可以判斷乘客的身份,如果是“海淀區(qū)”或者“朝陽(yáng)區(qū)”的“老人” “婦女”“兒童”就可以免費(fèi)乘車,其他人員乘車一次扣 2 元。

分析:本實(shí)例用“解釋器模式”設(shè)計(jì)比較適合,首先設(shè)計(jì)其文法規(guī)則如下。

  1. <expression> ::= <city>的<person> 
  2. <city> ::= 海淀區(qū)|朝陽(yáng)區(qū) 
  3. <person> ::= 老人|婦女|兒童 

然后,根據(jù)文法規(guī)則按以下步驟設(shè)計(jì)公交車卡的讀卡器程序的類圖。

使用步驟

步驟1:定義一個(gè)抽象表達(dá)式(Expression)接口,它包含了解釋方法 interpret(String info)。

  1. //抽象表達(dá)式類 
  2. interface Expression { 
  3.     public boolean interpret(String info); 

步驟2:定義一個(gè)終結(jié)符表達(dá)式(Terminal Expression)類,它用集合(Set)類來(lái)保存滿足條件的城市或人,并實(shí)現(xiàn)抽象表達(dá)式接口中的解釋方法 interpret(Stringinfo),用來(lái)判斷被分析的字符串是否是集合中的終結(jié)符。

  1. class TerminalExpression implements Expression { 
  2.     private Set<String> set = new HashSet<String>(); 
  3.  
  4.     public TerminalExpression(String[] data) { 
  5.         for (int i = 0; i < data.length; i++) { 
  6.             set.add(data[i]); 
  7.         } 
  8.     } 
  9.  
  10.     public boolean interpret(String info) { 
  11.         if (set.contains(info)) { 
  12.             return true
  13.         } 
  14.         return false
  15.     } 

步驟3:定義一個(gè)非終結(jié)符表達(dá)式(AndExpressicm)類,它也是抽象表達(dá)式的子類,它包含滿足條件的城市的終結(jié)符表達(dá)式對(duì)象和滿足條件的人員的終結(jié)符表達(dá)式對(duì)象,并實(shí)現(xiàn) interpret(String info) 方法,用來(lái)判斷被分析的字符串是否是滿足條件的城市中的滿足條件的人員。

  1. class AndExpression implements Expression { 
  2.     private Expression city = null
  3.     private Expression person = null
  4.  
  5.     public AndExpression(Expression city, Expression person) { 
  6.         this.city = city; 
  7.         this.person = person; 
  8.     } 
  9.  
  10.     public boolean interpret(String info) { 
  11.         String s[] = info.split("的"); 
  12.         return city.interpret(s[0]) && person.interpret(s[1]); 
  13.     } 

步驟4:定義一個(gè)環(huán)境(Context)類,它包含解釋器需要的數(shù)據(jù),完成對(duì)終結(jié)符表達(dá)式的初始化,并定義一個(gè)方法 freeRide(String info) 調(diào)用表達(dá)式對(duì)象的解釋方法來(lái)對(duì)被分析的字符串進(jìn)行解釋。

  1. class Context { 
  2.     private String[] citys = {"海淀區(qū)""朝陽(yáng)區(qū)"}; 
  3.     private String[] persons = {"老人""婦女""兒童"}; 
  4.     private Expression cityPerson; 
  5.  
  6.     public Context() { 
  7.         Expression city = new TerminalExpression(citys); 
  8.         Expression person = new TerminalExpression(persons); 
  9.         cityPerson = new AndExpression(city, person); 
  10.     } 
  11.  
  12.     public void freeRide(String info) { 
  13.         boolean ok = cityPerson.interpret(info); 
  14.         if (ok) { 
  15.             System.out.println("您是" + info + ",您本次乘車免費(fèi)!"); 
  16.         } else { 
  17.             System.out.println(info + ",您不是免費(fèi)人員,本次乘車扣費(fèi)2元!"); 
  18.         } 
  19.     } 

步驟5:客戶端測(cè)試

  1. public class InterpreterPattern { 
  2.     public static void main(String[] args) { 
  3.         Context bus = new Context(); 
  4.         bus.freeRide("海淀區(qū)的老人"); 
  5.         bus.freeRide("海淀區(qū)的年輕人"); 
  6.         bus.freeRide("朝陽(yáng)區(qū)的婦女"); 
  7.         bus.freeRide("朝陽(yáng)區(qū)的兒童"); 
  8.         bus.freeRide("南京的年輕人"); 
  9.     } 

輸出結(jié)果

  • 您是海淀區(qū)的老人,您本次乘車免費(fèi)!
  • 海淀區(qū)的年輕人,您不是免費(fèi)人員,本次乘車扣費(fèi)2元!
  • 您是朝陽(yáng)區(qū)的婦女,您本次乘車免費(fèi)!
  • 您是朝陽(yáng)區(qū)的兒童,您本次乘車免費(fèi)!
  • 南京的年輕人,您不是免費(fèi)人員,本次乘車扣費(fèi)2元!

優(yōu)點(diǎn)

解釋器模式是一種類行為型模式,其主要優(yōu)點(diǎn)如下。

  1. 擴(kuò)展性好。由于在解釋器模式中使用類來(lái)表示語(yǔ)言的文法規(guī)則,因此可以通過(guò)繼承等機(jī)制來(lái)改變或擴(kuò)展文法。
  2. 容易實(shí)現(xiàn)。在語(yǔ)法樹(shù)中的每個(gè)表達(dá)式節(jié)點(diǎn)類都是相似的,所以實(shí)現(xiàn)其文法較為容易。

缺點(diǎn)

  1. 執(zhí)行效率較低。解釋器模式中通常使用大量的循環(huán)和遞歸調(diào)用,當(dāng)要解釋的句子較復(fù)雜時(shí),其運(yùn)行速度很慢,且代碼的調(diào)試過(guò)程也比較麻煩。
  2. 會(huì)引起類膨脹。解釋器模式中的每條規(guī)則至少需要定義一個(gè)類,當(dāng)包含的文法規(guī)則很多時(shí),類的個(gè)數(shù)將急劇增加,導(dǎo)致系統(tǒng)難以管理與維護(hù)。
  3. 可應(yīng)用的場(chǎng)景比較少。在軟件開(kāi)發(fā)中,需要定義語(yǔ)言文法的應(yīng)用實(shí)例非常少,所以這種模式很少被使用到。

應(yīng)用場(chǎng)景

  1. 當(dāng)語(yǔ)言的文法較為簡(jiǎn)單,且執(zhí)行效率不是關(guān)鍵問(wèn)題時(shí)。
  2. 當(dāng)問(wèn)題重復(fù)出現(xiàn),且可以用一種簡(jiǎn)單的語(yǔ)言來(lái)進(jìn)行表達(dá)時(shí)。
  3. 當(dāng)一個(gè)語(yǔ)言需要解釋執(zhí)行,并且語(yǔ)言中的句子可以表示為一個(gè)抽象語(yǔ)法樹(shù)的時(shí)候,如 XML 文檔解釋。

模式的擴(kuò)展

在項(xiàng)目開(kāi)發(fā)中,如果要對(duì)數(shù)據(jù)表達(dá)式進(jìn)行分析與計(jì)算,無(wú)須再用解釋器模式進(jìn)行設(shè)計(jì)了,Java 提供了以下強(qiáng)大的數(shù)學(xué)公式解析器:Expression4J、MESP(Math Expression String Parser) 和 Jep 等,它們可以解釋一些復(fù)雜的文法,功能強(qiáng)大,使用簡(jiǎn)單。

現(xiàn)在以 Jep 為例來(lái)介紹該工具包的使用方法。Jep 是 Java expression parser 的簡(jiǎn)稱,即 Java 表達(dá)式分析器,它是一個(gè)用來(lái)轉(zhuǎn)換和計(jì)算數(shù)學(xué)表達(dá)式的 Java 庫(kù)。通過(guò)這個(gè)程序庫(kù),用戶可以以字符串的形式輸入一個(gè)任意的公式,然后快速地計(jì)算出其結(jié)果。而且 Jep 支持用戶自定義變量、常量和函數(shù),它包括許多常用的數(shù)學(xué)函數(shù)和常量。 

使用前先配置依賴包:

  1. <!-- https://mvnrepository.com/artifact/jep/jep --> 
  2. <dependency> 
  3.     <groupId>jep</groupId> 
  4.     <artifactId>jep</artifactId> 
  5.     <version>2.24</version> 
  6. </dependency> 

下面來(lái)看一個(gè)案例:

  1. package com.niuh.designpattern.interpreter.v3; 
  2.  
  3.  
  4. import org.nfunk.jep.JEP; 
  5.  
  6. /** 
  7.  * <p> 
  8.  * JepDemo 
  9.  * </p> 
  10.  */ 
  11. public class JepDemo { 
  12.  
  13.     public static void main(String[] args) { 
  14.         JEP jep = new JEP(); //一個(gè)數(shù)學(xué)表達(dá)式 
  15.         String exp = "((a+b)*(c+b))/(c+a)/b"; //給變量賦值 
  16.         jep.addVariable("a", 10); 
  17.         jep.addVariable("b", 10); 
  18.         jep.addVariable("c", 10); 
  19.         try { //執(zhí)行 
  20.             jep.parseExpression(exp); 
  21.             Object result = jep.getValueAsObject(); 
  22.             System.out.println("計(jì)算結(jié)果: " + result); 
  23.         } catch (Throwable e) { 
  24.             System.out.println("An error occured: " + e.getMessage()); 
  25.         } 
  26.  
  27.     } 

程序運(yùn)行結(jié)果如下:

  • 計(jì)算結(jié)果: 2.0

源碼中的應(yīng)用

SpelExpressionParser中解釋器模式應(yīng)用分析

類圖分析

在下面的類圖中,Expression是一個(gè)接口,相當(dāng)于我們解釋器模式中的非終結(jié)符表達(dá)式,而ExpressionParser相當(dāng)于終結(jié)符表達(dá)式。根據(jù)不同的Parser對(duì)象,返回不同的Expression對(duì)象。

部分源碼分析 

Expression接口

  1. //抽象的非終結(jié)符表達(dá)式 
  2. public interface Expression { 
  3.  Object getValue() throws EvaluationException; 
  4.   
  5.  Object getValue(Object rootObject) throws EvaluationException; 

SpelExpression類

  1. //具體的非終結(jié)符表達(dá)式 
  2. public class SpelExpression implements Expression { 
  3.  @Override 
  4.  public Object getValue() throws EvaluationException { 
  5.   Object result; 
  6.   if (this.compiledAst != null) { 
  7.    try { 
  8.     TypedValue contextRoot = evaluationContext == null ? null : evaluationContext.getRootObject(); 
  9.     return this.compiledAst.getValue(contextRoot == null ? null : contextRoot.getValue(), evaluationContext); 
  10.    } 
  11.    catch (Throwable ex) { 
  12.     // If running in mixed mode, revert to interpreted 
  13.     if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { 
  14.      this.interpretedCount = 0; 
  15.      this.compiledAst = null
  16.     } 
  17.     else { 
  18.      // Running in SpelCompilerMode.immediate mode - propagate exception to caller 
  19.      throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION); 
  20.     } 
  21.    } 
  22.   } 
  23.   ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration); 
  24.   result = this.ast.getValue(expressionState); 
  25.   checkCompile(expressionState); 
  26.   return result; 
  27.  } 

CompositeStringExpression

  1. //具體的非終結(jié)符表達(dá)式 
  2. public class CompositeStringExpression implements Expression { 
  3.  @Override 
  4.  public String getValue() throws EvaluationException { 
  5.   StringBuilder&nbsp;sb&amp;nbsp;= new StringBuilder(); 
  6.   for (Expression expression : this.expressions) { 
  7.    String value = expression.getValue(String.class); 
  8.    if (value != null) { 
  9.    &nbsp;sb.append(value); 
  10.    } 
  11.   } 
  12.   return&nbsp;sb.toString(); 
  13.  } 

ExpressionParser接口

  1. public interface ExpressionParser { 
  2.  //解析表達(dá)式 
  3.  Expression parseExpression(String expressionString) throws ParseException; 
  4.  Expression parseExpression(String expressionString, ParserContext context) throws ParseException; 

TemplateAwareExpressionParser類

  1. public abstract class TemplateAwareExpressionParser implements ExpressionParser { 
  2.  @Override 
  3.  public Expression parseExpression(String expressionString) throws ParseException { 
  4.   return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT); 
  5.  } 
  6.  //根據(jù)不同的parser返回不同的Expression對(duì)象 
  7.  @Override 
  8.  public Expression parseExpression(String expressionString, ParserContext context) 
  9.    throws ParseException { 
  10.   if (context == null) { 
  11.    context = NON_TEMPLATE_PARSER_CONTEXT; 
  12.   } 
  13.   if (context.isTemplate()) { 
  14.    return parseTemplate(expressionString, context); 
  15.   } 
  16.   else { 
  17.    return doParseExpression(expressionString, context); 
  18.   } 
  19.  } 
  20.  private Expression parseTemplate(String expressionString, ParserContext context) 
  21.    throws ParseException { 
  22.   if (expressionString.length() == 0) { 
  23.    return new LiteralExpression(""); 
  24.   } 
  25.   Expression[] expressions = parseExpressions(expressionString, context); 
  26.   if (expressions.length == 1) { 
  27.    return expressions[0]; 
  28.   } 
  29.   else { 
  30.    return new CompositeStringExpression(expressionString, expressions); 
  31.   } 
  32.  } 
  33.  //抽象的,由子類去實(shí)現(xiàn) 
  34.  protected abstract Expression doParseExpression(String expressionString, 
  35.    ParserContext context) throws ParseException; 

SpelExpressionParser類

  1. public class SpelExpressionParser extends TemplateAwareExpressionParser { 
  2.  @Override 
  3.  protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException { 
  4.   //這里返回了一個(gè)InternalSpelExpressionParser, 
  5.   return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context); 
  6.  } 

InternalSpelExpressionParser類

  1. class InternalSpelExpressionParser extends TemplateAwareExpressionParser { 
  2.  @Override 
  3.  protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException { 
  4.   try { 
  5.    this.expressionString = expressionString; 
  6.    Tokenizer tokenizer = new Tokenizer(expressionString); 
  7.    tokenizer.process(); 
  8.    this.tokenStream = tokenizer.getTokens(); 
  9.    this.tokenStreamLength = this.tokenStream.size(); 
  10.    this.tokenStreamPointer = 0; 
  11.    this.constructedNodes.clear(); 
  12.    SpelNodeImpl ast = eatExpression(); 
  13.    if (moreTokens()) { 
  14.     throw new SpelParseException(peekToken().startPos, SpelMessage.MORE_INPUT, toString(nextToken())); 
  15.    } 
  16.    Assert.isTrue(this.constructedNodes.isEmpty()); 
  17.    return new SpelExpression(expressionString, ast, this.configuration); 
  18.   } 
  19.   catch (InternalParseException ex) { 
  20.    throw ex.getCause(); 
  21.   } 
  22.  } 

PS:以上代碼提交在 Github : 

https://github.com/Niuh-Study/niuh-designpatterns.git

 

責(zé)任編輯:姜華 來(lái)源: 今日頭條
相關(guān)推薦

2023-05-15 08:51:46

解釋器模式定義

2010-04-21 08:38:18

解釋器模式PHP設(shè)計(jì)模式

2021-06-22 15:27:13

設(shè)計(jì)模式迭代器模式Java

2022-01-19 08:21:12

設(shè)計(jì)裝飾器模式

2022-01-12 13:33:25

工廠模式設(shè)計(jì)

2020-10-23 09:40:26

設(shè)計(jì)模式

2020-11-03 13:05:18

命令模式

2020-11-04 08:54:54

狀態(tài)模式

2020-11-06 09:01:46

迭代器模式

2023-03-03 08:12:07

設(shè)計(jì)模式語(yǔ)言

2021-06-09 08:53:34

設(shè)計(jì)模式策略模式工廠模式

2020-10-19 09:28:00

抽象工廠模式

2021-09-29 13:53:17

抽象工廠模式

2022-01-14 09:22:22

設(shè)計(jì)模式橋接

2013-11-26 15:48:53

Android設(shè)計(jì)模式SDK

2021-03-02 08:50:31

設(shè)計(jì)單例模式

2020-10-21 14:29:15

原型模式

2021-07-07 10:31:19

對(duì)象池模式解釋器模式設(shè)計(jì)模式

2020-10-20 13:33:00

建造者模式

2012-01-13 15:59:07

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)