解释器解释器模式不是完全一样的概念,只能说有相似的地方。下面先了解一下解释器,以便后面更好的理解解释器模式。

It can explain what ?

如下是解释器要解释的主体:

  • 加减乘除等运算,3+4/9+6*8
  • 摩尔斯电码
  • 正则表达式
  • El表达式
  • OGNL表达式
  • 小明是北京人
  • 小红是一名售货员
  • 部门领导下发一则通知
  • ...

How explain ?

解释器模式常用于对简单语言集的编译或分析,例如:

  • 我是大学生
  • 张强学习编程
  • 小明是北京人
  • 部门领导下发一则通知

为了掌握好它的结构与实现,需要先了解编译原理中的文法句子语法树等相关概念。(其实不了解也行,可以直接看code,在回来看)

这里提到的文法和句子的概念同编译原理中的描述相同,

  • 文法指语言的语法规则
    如何解释语言的规则
  • 句子是语言集中的元素
    例如,汉语中的句子有很多,“我是大学生”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。

Grammar Concept

这个是分析的关键。

〈句子〉::=〈主语〉〈谓语〉〈宾语〉
〈主语〉::=〈代词〉|〈名词〉
〈谓语〉::=〈动词〉
〈宾语〉::=〈代词〉|〈名词〉
〈代词〉::= 你 | 我 | 他
〈名词〉::= 大学生 | 小明 | 英语 | 张强
〈动词〉::= 是 | 学习

注意:

  • ::= 表示“定义为”的意思;
  • 括住的是非终结符没有括住的是终结符;
  • | 是 逻辑符号or.

Sentence

句子是语言的基本单位,是语言集中的一个元素,它由终结符构成,能由"文法"推导出
例如,上述文法可以推出“我是大学生”,所以它是句子。

Syntax Tree

语法树是句子结构的一种树型表示,它代表了句子的推导结果,它有利于理解句子语法结构的层次。

“我是大学生”的语法树如图所示:

<expression> ::= <代词>是<名词>
<代词> ::= 我 | 你 | 他 | 她
<名词> ::= 大学生 | 程序员 | 牲口 | 懦夫

终结符和非终结符

终结符表达式(Terminal Expression):
就是一个最终的元素,不可在向下分割。

非终结符表达式(Nonterminal Expression):
非终结符需要依照文法规则去分割,文法中的每条规则都对应于一个非终结符表达式。


解释器模式(Interpreter Pattern)是一种行为设计模式,用于定义一种语言的语法,并且通过解释器来解释该语言的句子。这个模式主要用于处理编程语言、脚本语言、或者复杂表达式的解释和计算。

和解释器使用的方式不同,解释器模式采用类和对象的方式去处理语句。

解释器模式的思想

解释器模式的核心思想是将一个语言的语法规则定义为类,然后利用这些类解释语法规则,从而理解和执行该语言的句子。具体来说,解释器模式主要包含以下几个部分:

  1. 抽象表达式(AbstractExpression):这是一个接口或抽象类,声明了一个解释的方法,这个方法用来解释上下文。

  2. 终结符表达式(TerminalExpression):具体实现了抽象表达式接口,表示语言中的基本元素,如词法单元或基本语法。

  3. 非终结符表达式(NonterminalExpression):具体实现了抽象表达式接口,表示语言中的复杂结构,通常由多个终结符或其他非终结符组成。

  4. 上下文(Context):保存解释过程中的信息,比如当前状态、环境等。

解决的问题

解释器模式主要解决以下几个问题:

  1. 如何定义一个语言的语法:通过定义一系列表达式来描述语言的规则和结构。

  2. 如何解释和处理语言的句子:通过具体的表达式类来解析和计算语言中的句子或指令。

要解释的内容

解释器模式要解释的内容包括:

  • 语言的语法规则:这些规则通过不同的表达式类来定义,并构成整个语言的语法树。

  • 具体的语句或表达式:解释器将根据语法规则处理具体的句子或表达式,并生成相应的结果或执行指令。

举个例子

假设我们有一个简单的数学表达式语言,如 "1 + 2 - 3",我们可以使用解释器模式来解析和计算这个表达式:

  1. 定义语法规则:比如加法、减法运算的规则。

  2. 实现表达式类:定义终结符表达式(如数字)和非终结符表达式(如加法、减法操作)。

  3. 构造上下文:保存表达式中的数字和运算符等信息。

  4. 解释执行:通过解析这些表达式并根据定义的规则计算结果。

解释器模式适用于以下情况:

  • 语言的语法比较简单或语言的变化不频繁。

  • 需要解释执行的文本需要根据语法规则进行解析和计算。

但是,这种模式也有局限性,如:

  • 对于复杂或变化频繁的语言,解释器模式可能会变得难以维护。

  • 对于复杂语法,可能需要大量的表达式类来处理,增加了系统的复杂性。

总结来说,解释器模式的思想是通过定义和实现一组表达式类来解析和执行特定语言或表达式,从而解决语言处理和计算的问题。

Key Elements

解释器模式包含以下主要角色。

  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。

  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。

  • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。

  • 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。

  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

Example —— 公交车身份识别系统

需求描述:每个人坐车都需要缴费,每个人上车后需要刷自己的公交卡卡,每个人的卡里都有每个人的信息,信息如下:

  • "韶关的老人";
  • "韶关的年轻人";
  • "广州的妇女";
  • "广州的儿童";
  • "山东的儿童";

你需要写一个解释器去识别每个人的身份,如果是“韶关”或者“广州”的“老人” “妇女”“儿童”就可以免费乘车,其他人员乘车一次扣 2 元。

分析:本实例用“解释器模式”设计比较适合,首先设计其文法规则如下:

<expression> ::= <city>的<person>
<city> ::= 韶关 | 广州
<person> ::= 老人 | 妇女 | 儿童

Client

public static void main(String[] args){
      Context bus=new Context();
      bus.freeRide("韶关的老人");
      bus.freeRide("韶关的年轻人");
      bus.freeRide("广州的妇女");
      bus.freeRide("广州的儿童");
      bus.freeRide("山东的儿童");
}

Context

class Context {
    private String[] citys = {"韶关", "广州"};
    private String[] persons = {"老人", "妇女", "儿童"};
    private Expression cityPerson;

    public Context() {
        Expression city = new TerminalExpression(citys);
        Expression person = new TerminalExpression(persons);
        cityPerson = new AndExpression(city, person);
    }

    public void freeRide(String info) {
        boolean ok = cityPerson.interpret(info);
        if (ok) System.out.println("您是" + info + ",您本次乘车免费!");
        else System.out.println(info + ",您不是免费人员,本次乘车扣费2元!");
    }
}
//抽象表达式类
interface Expression {
    public boolean interpret(String info);
}

//终结符表达式类
class TerminalExpression implements Expression {
    private Set<String> set = new HashSet<String>();

    public TerminalExpression(String[] data) {
        for (int i = 0; i < data.length; i++) set.add(data[i]);
    }

    public boolean interpret(String info) {
        if (set.contains(info))
            return true;
        return false;
    }
}

//非终结符表达式类
class AndExpression implements Expression {
    private Expression city = null;
    private Expression person = null;

    public AndExpression(Expression city, Expression person) {
        this.city = city;
        this.person = person;
    }

    // 以"的"作为分隔
    public boolean interpret(String info) {
        String s[] = info.split("的");
        System.out.println(city.interpret(s[0]) && person.interpret(s[1]));
        return city.interpret(s[0]) && person.interpret(s[1]);
    }
}

out:

true
您是韶关的老人,您本次乘车免费!
false
韶关的年轻人,您不是免费人员,本次乘车扣费2元!
true
您是广州的妇女,您本次乘车免费!
true
您是广州的儿童,您本次乘车免费!
false
山东的儿童,您不是免费人员,本次乘车扣费2元!

Interpreter Example

如果你有一个配置文件,其中包含多个逻辑语句,我应该如何处理它们?

{
  "logic_statements": [
    "condition1 and condition2",
    "condition3 or condition4",
    "condition5 and condition6 or condition7"
  ]
}

todo

Reference

设计模式之解释器模式(Interpreter)详解及代码示例
https://www.cnblogs.com/jing99/p/12610089.html

posted on 2023-09-08 13:48  Mysticbinary  阅读(42)  评论(0编辑  收藏  举报