前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >21.设计模式--解释器模式(Interperter模式)

21.设计模式--解释器模式(Interperter模式)

作者头像
大猫的Java笔记
发布2021-11-19 16:15:22
3200
发布2021-11-19 16:15:22
举报
文章被收录于专栏:大猫的Java笔记大猫的Java笔记

1.定义

解释器模式是一种行为型模式,工作中基本上是用不到的,他的作用就是给定一个语言,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

现在有一个需求,大概就是计算器,首先给定一个公式,然后输入对应的值计算出最终的结果,公式不复杂,只能支持+和-操作。例如:输入a+b-c,100、200、100,然后计算结果为200。其中我们输入的a+b-c实际上可以理解为是另一种语言,那么此时就需要一个解释器进行语法解释,并计算结果。

现在需求有了我们分析一下公式中有哪些元素,其中有运算符以及参与运算的值,而对于解释器来说实际上只需要解释运算符以及参与运算的值即可,当然运算符可能又存在各种不同的运算符。例如加和减实际上对应的计算规则是不同的。

2.解释器模式结构图

VarExpression 用来解析运算的值,各个公式的运算元素的数量是不同的,每个运 算元素对应了一个 VarExpression 对象,SybmolExpression 是负责运算符号解析的,分别有两个子类 AddExpression(负责加法运算)和 SubExpression(负责减法运算)来实现。

Calculator类用于安排运算的先后顺序(加减法是不用考虑,但是乘除法呢?注意扩展性),并且还要返回结果,因此 我们需要增加一个封装类来处理进行封装,由于我们只作运算,暂时还不与业务有挂钩。

3.解释器模式实现

Expression抽象类,只是定义了一个方法interpreter方法用于进行计算,当然计算的逻辑由子类去实现,而参数HashMap中存放的key就是公式中的参数,例如a、b、c而value则代表的是对用的参数具体的值。

代码语言:javascript
复制
public abstract class Expression {
    
    //解析公式和数值,其中var中的key值是是公式中的参数,如a、b、c,value值是具体的数字
    public abstract int interpreter(HashMap<String,Integer> var);
}

VarExpression是Expression的具体实现,用于解析公式中的具体运算值,如a、b、c具体对应的值是多少,当然具体的值就是通过key从HashMap中进行获取的。

代码语言:javascript
复制
public class VarExpression extends Expression {

    private String key;

    public VarExpression(String key) {
        this.key = key;
    }

    //从map中取之
    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return var.get(this.key);
    }
}

SymbolExpression也是Expression的具体实现,用于解析公式运算符,当然对于运算符来说肯定需要知道左边是谁和右边是谁,不然怎么进行计算呢。

代码语言:javascript
复制
public abstract class SymbolExpression extends Expression {

    protected Expression left;

    protected Expression right;

    //所有的解析公式都应只关心自己左右两个表达式的结果
    public SymbolExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
}

AddExpression是具体的运算符解析器,是对SymbolExpression的具体实现,对于SymbolExpression来说只需要定义运算的左右两部分,而实际要加还是要减是子类来做的,所以AddExpression就是将左右两部分进行相加。

代码语言:javascript
复制
public class AddExpression extends SymbolExpression {

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

    //把左右两个表达式运算的结果加起来
    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return left.interpreter(var) + right.interpreter(var);
    }
}

而是说SubExpression则是将运算符的左右部分做减法。

代码语言:javascript
复制
public class SubExpression extends SymbolExpression {

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

    //左右两个表达式相减
    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return left.interpreter(var) - right.interpreter(var);
    }
}

Calculator构造函数接受一个表达式,然后把表达式转化为 char 数组, 并判断运算符号,如果是“+”则调用AddExpression,把左边的数(left 变量)和右边的数(right 变量)放入AddExpression。并且存放到Stack(栈)中。

那左边的数为什么是在堆栈中呢?原因是因为放入栈中是为了让计算有先后顺序,例如a+b-c,如果栈中已经有了a+b的AddExpression,那么如果再减法的时候应该是(a+b)作为左边,而c作为右边放入SubExpression。

代码语言:javascript
复制
public class Calculator {
    //定义的表达式
    private Expression expression;

    //构造函数传参,并解析
    public Calculator(String expStr) {
        //定义一个堆栈,安排运算的先后顺序
        Stack<Expression> stack = new Stack<Expression>();

        //表达式拆分为字符数组
        char[] charArray = expStr.toCharArray();

        //运算
        Expression left = null;
        Expression right = null;
        for (int i = 0; i < charArray.length; i++) {

            switch (charArray[i]) {
                case '+': //加法
                    //拿到加法的左边值,如a+b-c,拿到a
                    left = stack.pop();
                    //拿到加法的右边值,如a+b-c,拿到b
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    //将做加法的实体存入栈中
                    stack.push(new AddExpression(left, right));
                    break;
                case '-':
                    //拿到减法的左边值,如a+b-c,拿到(a+b)
                    left = stack.pop();
                    //拿到减法的右边值,如a+b-c,拿到c
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    //将做减法的实体存入栈中
                    stack.push(new SubExpression(left, right));
                    break;
                default: //公式中的变量,即a或b或c
                    stack.push(new VarExpression(String.valueOf(charArray[i])));
            }
        }

        //将需要计算的实体取出来,如a+b-c,则拿到(a+b)的实体和(a+b)-c的实体
        this.expression = stack.pop();
    }

    //开始运算
    public int run(HashMap<String, Integer> var) {
        return this.expression.interpreter(var);
    }
}

Test类来模拟用户情况,用户要求可以扩展,可以修改公式,那就通过接受键盘事件来处理,Client 类的源代码如下。

代码语言:javascript
复制
public class Test {
    //运行四则运算
    public static void main(String[] args) throws IOException {
        String expStr = getExpStr();
        //赋值
        HashMap<String,Integer> var = getValue(expStr);

        Calculator cal = new Calculator(expStr);
        System.out.println("运算结果为:"+expStr +"="+cal.run(var));
    }

    //获得表达式
    public static String getExpStr() throws IOException{
        System.out.print("请输入表达式:");
        return (new BufferedReader(new
                InputStreamReader(System.in))).readLine();
    }

    //获得值映射
    public static HashMap<String,Integer> getValue(String exprStr) throws
            IOException {
        HashMap<String, Integer> map = new HashMap<String, Integer>();

        //解析有几个参数要传递
        for (char ch : exprStr.toCharArray()) {
            if (ch != '+' && ch != '-') {
                if (!map.containsKey(String.valueOf(ch))) { //解决重复参数的问题
                    System.out.print("请输入" + ch + "的值:");
                    String in = (new BufferedReader(new
                            InputStreamReader(System.in))).readLine();
                    map.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }

        return map;
    }
}
代码语言:javascript
复制
请输入表达式:a+b-c
请输入a的值:100
请输入b的值:200
请输入c的值:20
运算结果为:a+b-c=280

解释器模式中的角色

AbstractExpression(抽象解释器),具体的解释任务由各个实现类完成。

concreteExpression(具体的解释器)具体的解释器分为两大类:TerminalExpression:终结符表达式,实现与文法中的元素相关联的解释操作,通常一个解释器模式 中只有一个终结符表达式,但有多个实例,对应不同的终结符。具体到我们例子就是 VarExpression 类, 表达式中的每个终结符都在堆栈中产生了一个 VarExpression 对象。 NonterminalExpression(非终结符表达式),文法中的每条规则对应一个非终结表达式,具体到我们的 例子就是加减法规则分别对应到 AddExpression 和 SubExpression 两个类。非终结符表达式根据逻辑的复 杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。 Context(环境角色),具体到我们的例子中是采用 HashMap 代替。

参考文献《设计模式之禅》

代码获取地址:https://gitee.com/bughong/design-pattern

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大猫的Java笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档