设计模式(二十三):行为型之解释器模式

设计模式系列文章

设计模式(一):创建型之单例模式

设计模式(二、三):创建型之工厂方法和抽象工厂模式

设计模式(四):创建型之原型模式

设计模式(五):创建型之建造者模式

设计模式(六):结构型之代理模式

设计模式(七):结构型之适配器模式

设计模式(八):结构型之装饰器模式

设计模式(九):结构型之桥接模式

设计模式(十):结构型之外观模式

设计模式(十一):结构型之组合模式

设计模式(十二):结构型之享元模式

设计模式(十三):行为型之模板方法模式

设计模式(十四):行为型之策略模式

设计模式(十五):行为型之命令模式

设计模式(十六):行为型之责任链模式

设计模式(十七):行为型之状态模式

设计模式(十八):行为型之观察者模式

设计模式(十九):行为型之中介者模式

设计模式(二十):行为型之迭代器模式

设计模式(二十一):行为型之访问者模式

设计模式(二十二):行为型之备忘录模式

设计模式(二十三):行为型之解释器模式


目录

  • 一、设计模式分类
  • 二、解释器模式
    • 1、概述
    • 2、结构
    • 3、实现
    • 4、优缺点
    • 5、使用场景


一、设计模式分类

  • 创建型模式
    • 用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”
    • 提供了单例、原型、工厂方法、抽象工厂、建造者 5 种创建型模式
  • 结构型模式
    • 用于描述如何将类或对象按某种布局组成更大的结构
    • 提供了代理、适配器、桥接、装饰、外观、享元、组合 7 种结构型模式
  • 行为型模式
    • 用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责
    • 提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器 11 种行为型模式

二、解释器模式

1、概述

设计模式(二十三):行为型之解释器模式_第1张图片

  • 如上图,设计一个软件用来进行加减计算
  • 我们第一想法就是使用工具类,提供对应的加法和减法的工具方法
//用于两个整数相加
public static int add(int a,int b){
    return a + b;
}

//用于两个整数相加
public static int add(int a,int b,int c){
    return a + b + c;
}

//用于n个整数相加
public static int add(Integer ... arr) {
    int sum = 0;
    for (Integer i : arr) {
        sum += i;
    }
    return sum;
}
  • 上面的形式比较单一、有限,如果形式变化非常多,这就不符合要求,因为加法和减法运算,两个运算符与数值可以有无限种组合方式
  • 显然,现在需要一种翻译识别机器,能够解析由数字以及 + - 符号构成的合法的运算序列
  • 如果把运算符和数字都看作节点的话,能够逐个节点的进行读取解析运算,这就是解释器模式的思维

定义

  • 给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子

文法(语法)规则:

文法是用于描述语言的语法结构的形式规则

expression ::= value | plus | minus
plus ::= expression ‘+’ expression   
minus ::= expression ‘-’ expression  
value ::= integer

注意: 这里的符号“::=”表示“定义为”的意思,竖线 | 表示或,左右的其中一个,引号内为字符本身,引号外为语法

2、结构

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

  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()
  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应
  • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式
  • 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值
  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法

3、实现

【例】设计实现加减法的软件

类图如下:

设计模式(二十三):行为型之解释器模式_第2张图片
代码如下:

  • 抽象表达式类
public abstract class AbstractExpression {
    public abstract int interpret(Context context);
}
  • 封装变量的类
public class Variable extends AbstractExpression {

    //声明存储变量名的成员变量
    private String name;

    public Variable(String name) {
        this.name = name;
    }

    public int interpret(Context context) {
        //直接返回变量的值
        return context.getValue(this);
    }

    @Override
    public String toString() {
        return name;
    }
}
  • 加法表达式类
public class Plus extends AbstractExpression {

    //+号左边的表达式
    private AbstractExpression left;
    //+号右边的表达式
    private AbstractExpression right;

    public Plus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    public int interpret(Context context) {
        //将左边表达式的结果和右边表达式的结果进行相加
        return left.interpret(context) + right.interpret(context);
    }

    @Override
    public String toString() {
        return "(" + left.toString() + " + " + right.toString() + ")";
    }
}
  • 减法表达式类
public class Minus extends AbstractExpression {

    //-号左边的表达式
    private AbstractExpression left;
    //-号右边的表达式
    private AbstractExpression right;

    public Minus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    public int interpret(Context context) {
        //将左边表达式的结果和右边表达式的结果进行相减
        return left.interpret(context) - right.interpret(context);
    }

    @Override
    public String toString() {
        return "(" + left.toString() + " - " + right.toString() + ")";
    }
}
  • 环境类
public class Context {

    //定义一个map集合,用来存储变量及对应的值
    private final Map<Variable,Integer> map = new HashMap<>();

    //添加变量的功能
    public void assign(Variable var, Integer value) {
        map.put(var,value);
    }

    //根据变量获取对应的值
    public int getValue(Variable var) {
        return map.get(var);
    }
}
  • 测试类
public class Client {
    public static void main(String[] args) {
        //创建环境对象
        Context context = new Context();

        //创建多个变量对象
        Variable a = new Variable("a");
        Variable b = new Variable("b");
        Variable c = new Variable("c");
        Variable d = new Variable("d");

        //将变量存储到环境对象中
        context.assign(a,1);
        context.assign(b,2);
        context.assign(c,3);
        context.assign(d,4);

        //获取抽象语法树   
        AbstractExpression expression = new Plus(new Minus(new Plus(a, b), c), d);

        //解释(计算)
        int result = expression.interpret(context);

        System.out.println(expression + " = " + result);
    }
}

输出结果:

(((a + b) - c) + d) = 4

4、优缺点

优点

  • 易于改变和扩展文法
    • 由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法
    • 每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言
  • 实现文法较为容易
    • 在抽象语法树中每一个表达式节点类的实现方式都是相似的
    • 这些类的代码编写都不会特别复杂
  • 增加新的解释表达式较为方便
    • 如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类
    • 原有表达式类代码无须修改,符合 “开闭原则”

缺点

  • 对于复杂文法难以维护
    • 在解释器模式中,每一条规则至少需要定义一个类
    • 因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护
  • 执行效率较低
    • 由于在解释器模式中使用了大量的循环和递归调用
    • 因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦

5、使用场景

  • 当语言的文法较为简单,且执行效率不是关键问题时
  • 当问题重复出现,且可以用一种简单的语言来进行表达时
  • 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候

你可能感兴趣的:(设计模式,设计模式,解释器模式)