【设计模式系列20】解释器模式原理及其在JDK和Spring源码中的体现

设计模式系列总览

设计模式飞机票
三大工厂模式登机入口
策略模式登机入口
委派模式登机入口
模板方法模式登机入口
观察者模式登机入口
单例模式登机入口
原型模式登机入口
代理模式登机入口
装饰者模式登机入口
适配器模式登机入口
建造者模式登机入口
责任链模式登机入口
享元模式登机入口
组合模式登机入口
门面模式登机入口
桥接模式登机入口
中介者模式登机入口
迭代器模式登机入口
状态模式登机入口
解释器模式登机入口
备忘录模式登机入口
命令模式登机入口
访问者模式登机入口
软件设计7大原则和设计模式总结登机入口

前言

解释器模式从字面上来理解就是需要去解释某些内容的含义,而实际上这也正是解释器模式的作用。本文主要会介绍解释器模式的原理,并会结合一个简单的示例来加深我们对解释器模式的理解,解释器模式在我们的应用开发层已经有了很多轮子,所以我们也比较少再去重造轮子。

什么是解释器模式

解释器模式(Interpreter Pattern)是指给定一门语言,定义它的文法的一种表示(如:加减乘除表达式和正则表达式等),然后再定义一个解释器,该解释器用来解释我们的文法表示(表达式)。

解释器模式是一种按照规定的语法(文法)来进行解析的一种设计模式,属于行为型模式。

好了,装逼时刻又到了:Talk is cheap,Show you the code,先看一个非常简单的例子。

终结符表达式和非终结符表达式

在解释器模式中有终结符表达式和非终结符表达式这两个概念。

  • 终结符表达式(Terminal Expression):实现文法中与终结符有关的解释操作。文法中每一个终结符都有一个具体的终结符表达式与之相对应。比如我们的R=M+N运算,M和N就是终结符,对应的解析M和N的解释器就是终结符表达式。
  • 非终符结表达式(Nonterminal Expression):实现文法中与非终结符有关的解释操作。文法中的每一条规则都对应了一个非终结符表达式。非终结表达式一般是文法中的运算符或者关键字,如上面公示:R=M+N中的“+”号就是非终结符,解析“+”号的解释器就是一个非终结符表达式。

解释器模式示例

下面我们就以简单的加减法表达式类简单写一个示例:
1、首先建立一个顶层的表达式接口:

package com.zwx.design.pattern.interpreter;

public interface IExpression {
    int interpret();
}

2、定义一个抽象的非终结表达式(比如加号和减号这种就属于非终结表达式):

package com.zwx.design.pattern.interpreter;

/**
 * 非终结表达式-抽象表达式
 */
public abstract class AbstractNonTerminalExpression implements IExpression{
    protected IExpression leftExpression;
    protected IExpression rightExpression;

    public AbstractNonTerminalExpression(IExpression leftExpression, IExpression rightExpression) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
    }
}

3、因为本次示例中只列举加法和减法,所以我们还需要一个加法类和一个减法类:

package com.zwx.design.pattern.interpreter;

/**
 * 非终结表达式-具体表达式-加法表达式
 */
public class AddExpression extends AbstractNonTerminalExpression {
    public AddExpression(IExpression leftExpression, IExpression rightExpression) {
        super(leftExpression, rightExpression);
    }

    @Override
    public int interpret() {
        return this.leftExpression.interpret() + this.rightExpression.interpret();
    }
}
package com.zwx.design.pattern.interpreter;

/**
 * 非终结表达式-具体表达式-减法表达式
 */
public class SubExpression extends AbstractNonTerminalExpression {

    public SubExpression(IExpression leftExpression, IExpression rightExpression) {
        super(leftExpression, rightExpression);
    }

    @Override
    public int interpret() {
        return this.leftExpression.interpret() - this.rightExpression.interpret();
    }
}

4、再建立一个终结表达式(如加减法运算中的数值):

package com.zwx.design.pattern.interpreter;

/**
 * 终结表达式-数值表达式
 */
public class NumberExpression implements IExpression{
    private int value;

    public NumberExpression(String value) {
        this.value = Integer.valueOf(value);
    }

    @Override
    public int interpret() {
        return this.value;
    }
}

5、最后我们还需要一个上下文信息来存储我们的运算结果:

package com.zwx.design.pattern.interpreter;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class ExpressionContext {
    private Integer currValue;//记录当前运算结果,空表示暂未运算
    private Stack<IExpression> stack = new Stack<>();

    public ExpressionContext(String expression) {
        this.parse(expression);
    }

    private void parse(String expression) {
        String[] elementArr = expression.split(" ");
        for (int i=0;i<elementArr.length;i++){
            String element = elementArr[i];
            if (element.equals("+")){
                IExpression leftExpression = stack.pop();//栈内元素出栈
                IExpression rightExpression = new NumberExpression(elementArr[++i]);//取出+号后的下一个元素
                IExpression addExpression = new AddExpression(leftExpression,rightExpression);
                stack.push(new NumberExpression(addExpression.interpret() + ""));//将计算结果入栈
            }else if (element.equals("-")){
                IExpression leftExpression = stack.pop();//栈内元素出栈
                IExpression rightExpression = new NumberExpression(elementArr[++i]);//取出-号后的下一个元素
                IExpression subExpression = new SubExpression(leftExpression,rightExpression);
                stack.push(new NumberExpression(subExpression.interpret() + ""));//将计算结果入栈
            }else{
                stack.push(new NumberExpression(element));//如果是数字则直接入栈
            }
        }
    }

    public int calcuate(){
        return stack.pop().interpret();//经过前面解析,到这里stack内只会剩下唯一一个数字,即运算结果
    }
}

6、最后新建一个测试类来测试一下:

package com.zwx.design.pattern.interpreter;

public class TestInterpreter {
    public static void main(String[] args) {
        ExpressionContext context = new ExpressionContext("666 + 888 - 777");
        System.out.println(context.calcuate());
        context = new ExpressionContext("123 - 456 + 11");
        System.out.println(context.calcuate());

    }
}

运行结果为:

777
-322

解释器模式角色

从上面示例中,我们可以得出解释器模式主要有4个角色:

  • 抽象表达式(Expression):一般会定义一个解释方法,具体如何解析会交由子类进行实现(如示例中的IExpression)。
  • 终结符表达式(Terminal Expression):实现文法中与终结符有关的解释操作(如示例中的AddExpression,SubExpression)。
  • 非终符结表达式(Nonterminal Expression):实现文法中与非终结符有关的解释操作(如示例中的NumberExpression)。
  • 上下文环境(Context):包含解释器之外的全局信息。一般用来存放文法中各个终结符所对应的具体值。

解释器模式在JDK和Spring源码中应用

这个在源码中的应用大家也应该可以猜测的出来了,比如说JDK中的正则表达式:Pattern类。还有Spring里面的ExpressionParse接口,Spring的这个表达式不知道大家有没有用过,这里面其实也是可以进行加减乘除运算的。如下就是一个简单的应用示例:
在这里插入图片描述
所以我们一开始就提到了,像这种解释器模式我们一般在业务开发中用的还是相对较少的,常用的表达式都有人已经帮我们解析好了,直接用就可以了,除非我们从事底层开发自己需要去定义较为复杂的表达式。

解释器模式应用场景

解释器模式主要可以运用在如下场景:

  • 1、当我们有一些需要重复解析的问题可以考虑定义一个表达式来进行表达。
  • 2、一些简单的语法如果需要解释的话也可以考虑解释器模式。

解释器模式优缺点

优点:

  • 1、扩展性比较强。从我们上面的示例中可以看出来了,我们每一个表达式都是一个类,所以如果需要修改某一个规则只需要修改对应的表达式类就可以了,扩展的时候新增一个新类就可以了。

缺点

  • 1、当文法规则比较复杂时,会引起类膨胀,比较难维护。
  • 2、当文法规则比较复杂时,如果出错了,调试比较困难。
  • 3、执行效率比较低下。因为当表达式比较复杂,结果层层依赖的话会采用递归方式进行解析。

总结

本文主要介绍了解释器模式的基本使用,并通过一个简单的示例来帮助更好的理解解释器模式,当然这个示例写的比较简单,很多业务逻辑是没有考虑的,但是这些并不是本文的重点,本文的重点是介绍解释器模式的思想。最后我们还介绍了解释器模式模式在相关JDK源码中的体现,希望通过本文,大家可以更深刻的理解解释器模式的设计思想。

请关注我,和孤狼一起学习进步

  • 13
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

双子孤狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值