解释器模式(二十三)

相信自己,请一定要相信自己

上一章简单介绍了 备忘录模式(二十二), 如果没有看过, 请观看上一章

一. 解释器模式

引用 菜鸟教程里面 解释器模式介绍: https://www.runoob.com/design-pattern/interpreter-pattern.html

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。

这种模式实现了一个表达式接口,该接口解释一个特定的上下文。

这种模式被用在 SQL 解析、符号处理引擎等。

一.一 介绍

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

主要解决: 对于一些固定文法构建一个解释句子的解释器。

何时使用: 如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。
这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

如何解决: 构建语法树,定义终结符与非终结符。

关键代码: 构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。

应用实例: 编译器、运算表达式计算。

优点:
1、可扩展性比较好,灵活。
2、增加了新的解释表达式的方式。
3、易于实现简单文法。

缺点:
1、可利用场景比较少。
2、对于复杂的文法比较难维护。
3、解释器模式会引起类膨胀。
4、解释器模式采用递归调用方法。

使用场景:
1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
2、一些重复出现的问题可以用一种简单的语言来进行表达。
3、一个简单语法需要解释的场景。

注意事项: 可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。


组成角色 具体 关系
抽象表达式(AbstractExpression) MathExpression 抽象表达式, 声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享
终结符表达式(TerminalExpression) BaseMathExpression 为终结符表达式, 实现与文法中的终结符相关的解释操作
非终结符表达式(NonTermialExpression) VarExpression 非终结符表达式,为文法中的非终结符实现解释操作.
环境角色(Context) MathCalculator 环境角色,含有解释器之外的全局信息

image-20230615194033150

二. 解释器模式实例

二. 一 抽象表达式 MathExpression

public abstract class MathExpression {
    
    
    /**
     处理表达式
     * @param paramMap
     */
    public abstract int interpreter (Map<String, Integer> paramMap);
}

二.二 非终结符表达式 VarExpression

public class VarExpression extends MathExpression {
    
    
    private String key;
    public VarExpression (String key) {
    
    
        this.key = key;
    }

    @Override
    public int interpreter(Map<String, Integer> paramMap) {
    
    
        return paramMap.get(key);
    }
}

二.三 终结符表达式 和其子类

二.三.一 终结符表达式 BaseMathExpression

public class BaseMathExpression extends MathExpression{
    
    

    private MathExpression left;
    private MathExpression right;

    public BaseMathExpression (MathExpression left, MathExpression right) {
    
    
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpreter(Map<String, Integer> paramMap) {
    
    
        // 是个空方法,让子类进行重写.
        return 0;
    }

    public MathExpression getLeft() {
    
    
        return left;
    }

    public MathExpression getRight() {
    
    
        return right;
    }
}

二.三.二 终结符表达式子类

相加

public class AddExpression extends BaseMathExpression{
    
    

    public AddExpression(MathExpression left, MathExpression right) {
    
    
        super(left, right);
    }

    @Override
    public int interpreter(Map<String, Integer> paramMap) {
    
    
        return super.getLeft().interpreter(paramMap) + super.getRight().interpreter(paramMap);
    }
}

相减

public class SubExpression extends BaseMathExpression{
    
    

    public SubExpression(MathExpression left, MathExpression right) {
    
    
        super(left, right);
    }

    @Override
    public int interpreter(Map<String, Integer> paramMap) {
    
    
        return super.getLeft().interpreter(paramMap) - super.getRight().interpreter(paramMap);
    }
}

二.四 环境角色 MathCalculator

public class MathCalculator {
    
    

    private MathExpression mathExpression;

    /**
    构造方法传入一个表达式
     */
    public MathCalculator (String expStr) {
    
    
   // 安排运算先后顺序
        Stack<MathExpression> stack = new Stack<>();
        // 表达式拆分成字符数组
        // 进行拆分
        char[] charArray = expStr.toCharArray();
        MathExpression left = null;
        MathExpression right = null;
        //遍历我们的字符数组, 即遍历  [a, +, b]
        //针对不同的情况,做处理
        for (int i = 0; i < charArray.length; i++) {
    
    
            switch (charArray[i]) {
    
    
                case '+':{
    
    
                    left = stack.pop();// 从stack取出left => "a"
                    right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表达式 "b"
                    stack.push(new AddExpression(left, right));// 然后根据得到left 和 right 构建 AddExpresson加入stack
                    break;
                }
                case '-':{
    
    
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new SubExpression(left, right));
                    break;
                }
                default:{
    
    
                    //如果是一个 Var 就创建要给 VarMathExpression 对象,并push到 stack
                    stack.push(new VarExpression(String.valueOf(charArray[i])));
                    break;
                }
            }
        }
        //当遍历完整个 charArray 数组后,stack 就得到最后MathExpression
        this.mathExpression = stack.pop();


    }
    /**
     对外提供的一个方法, 用于获取结果
     */
    public int exec (Map<String,Integer> paramMap) {
    
    
        return this.mathExpression.interpreter(paramMap);
    }
}

二.五 客户端调用

   @Test
    public void oneTest() {
    
    
        MathCalculator mathCalculator = new MathCalculator("a+b-c+d-e");

        // 定义参数
        Map<String,Integer> paramMap = new HashMap<>();
        paramMap.put("a",1);
        paramMap.put("b",2);
        paramMap.put("c",3);
        paramMap.put("d",4);
        paramMap.put("e",5);

        int result = mathCalculator.exec(paramMap);
        log.info("最后结果是:{}",result);

    }

INFO [main] (InterpreterTest.java:33) - 最后结果是:-1

三. JEP 处理数学表达式

三.一 引入jep 依赖

<dependency>
            <groupId>jep</groupId>
            <artifactId>jep</artifactId>
            <version>2.24</version>
        </dependency>

三.二 JEP使用测试

@Test
    public void twoTest() {
    
    
        JEP jep = new JEP();
        // 需要先添加变量, 再处理表达式。
        jep.addVariable("a",1);
        jep.addVariable("b",2);
        jep.addVariable("c",3);
        jep.addVariable("d",4);
        jep.addVariable("e",5);
        jep.parseExpression("a+b-c+d-e");
        // 进行获取结果
        log.info(">>> 最后结果: {}", jep.getValue());
    }

INFO [main] (InterpreterTest.java:48) - >>> 最后结果: -1.0

较复杂的公式可以使用 SpEL 处理。 SpEL 可以看老蝴蝶之前写的文章: SpringBoot SpEL表达式(五十二)


本章节的代码放置在 github 上:


https://github.com/yuejianli/DesignPattern/tree/develop/Interpreter


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

猜你喜欢

转载自blog.csdn.net/yjltx1234csdn/article/details/131234811