Interpreter Pattern

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):
非终结符需要依照文法规则去分割,文法中的每条规则都对应于一个非终结符表达式。

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元!

Reference

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

热门相关:英雄联盟之巅峰王座   本法官萌萌哒   隐婚试爱:娇妻,好甜!   楚氏赘婿   我是仙凡