【设计模式笔记】(八)- 解释器模式

1.简述

解释器模式(Interpreter Pattern),实际应用中较少用到的行为模式。主要作用就是提供解释语言的语法或表达式的能力,从作用上来说,注定实际开发过程中会使用的少,毕竟很少有人需要构建一套自己的语法来解析吧!但是,这并不表示解释器模式我们可以忽略掉。

2.实现

解释器模式UML图

解释器模式.png
  • Context:上下文环境,包含解释器之外的全局信息
  • Client:客户端,解析表达式,构建语法树,执行具体的解释操作等
  • AbstractExpression:抽象表达式,声明一个抽象的解释操作弗雷,并定义一个抽象的解释方案,其具体的实现在各个具体的子类解释器中完成。
  • TerminalExpression:终结符表达式,实现文法中终结符有关的解释操作。文法中每一个终结符都有一个具体的终结表达式与之对应。
  • NonterminalExpression:非终结表达式,实现文法中非终结符有关的解释操作。

其中AbstractExpressioninterpret()是抽象的解析方法,参数是上下文的环境,而interpret()方法的具体实现则由TerminalExpressionNonterminalExpression实现。

具体实现

如下我们通过对算术表达式的解释来看一个解释器模式的实现, 如表达式m+n+p中,如果我们使用解释器模式对该表达式进行解释,那么mnp代表的三个字母可以看成是终结符号,而+代表的运算符则可以看成是非终结符号。

先创建抽象解释器,表示数学运算

public abstract class ArithmeticExpression {
    public abstract int interptet();
}

解释器中定义了interptet()方法,ArithmeticExpression有两个子类,分别是NumExpressionOperatorExpression

/** 对数字进行解释 */
public class NumExpression extends ArithmeticExpression {
    private int num;
        
    public NumExpression(int num) {
        this.num = num;
    }
        
    @Override 
    public int interptet() {
        return num;
    }
}

/** 对运算符进行解释 */
public abstract class OperatorExpression extends ArithmeticExpression {
    protected ArithmeticExpression mArithmeticExpression1,mArithmeticExpression2;
    
    public OperatorExpression(ArithmeticExpression arithmeticExpression1,ArithmeticExpression arithmeticExpression2) {
        mArithmeticExpression1 = arithmeticExpression1;
        mArithmeticExpression2 = arithmeticExpression2;
    }
}

基础的类已经完成了,如果需要处理加法运算还需要继承OperatorExpression并实现interptet()方法来实现加法运算器AdditionExpression

public class AdditionExpression extends OperatorExpression {
    public AdditionExpression(ArithmeticExpression arithmeticExpression1,ArithmeticExpression arithmeticExpression2) {
        super(arithmeticExpression1, arithmeticExpression2);
    }
    
    @Override
    public int interptet() {
        return mArithmeticExpression1.interptet() + mArithmeticExpression2.interptet();
    }
}

还差一个业务逻辑处理类

public class Calculator {

    //声明一个Stack栈,存储并操作所有相关的解释器
    protected Stack<ArithmeticExpression> mArithmeticExpressionStack = new Stack<>();

    public Calculator(String expression) {
        //声明两个ArithmeticExpression类型的临时变量,存储运算符左右两边的数字解释器
        ArithmeticExpression arithmeticExpression1, arithmeticExpression2;

        //根据空格分隔表达式字符串
        String[] elements = expression.split(" ");

        //循环遍历表达式元素数组
        for (int i = 0; i < elements.length; ++i) {
            switch (elements[i].charAt(0)) {
                //如果是加号
                case '+':
                    //将栈中的解释器弹出作为运算符号左边的解释器
                    arithmeticExpression1 = mArithmeticExpressionStack.pop();

                    //同时将运算符号数组下标下一个元素构造为一个数字解析器
                    arithmeticExpression2 = new NumExpression(Integer.valueOf(elements[++i]));

                    //通过上面两个数字解释器构造加法运算解释器
                    mArithmeticExpressionStack.push(new AdditionExpression(arithmeticExpression1, arithmeticExpression2));
                    break;
                default:
                    //如果不是运算符,则是数字,直接构造数字解释器并压入栈
                    mArithmeticExpressionStack.push(new NumExpression(Integer.valueOf(elements[i])));
                    break;
            }
        }
    }

    //计算结果
    public int calculate() {
        return mArithmeticExpressionStack.pop().interptet();
    }
}

这里需要注意的是,为了简化逻辑,在约定的表达式的每个元素之间必须使用空格隔开,如123 + 32 + 666这种形式的表达式,这样才能在Calculator中使用空格来拆分字符串。

如果想引入减法运算,我们只需要定义一个减法解释器

public class SubtractionExpreesion extends OperatorExpression{
    public SubtractionExpreesion(ArithmeticExpression arithmeticExpression1,ArithmeticExpression arithmeticExpression2) {
        super(arithmeticExpression1, arithmeticExpression2);
    }
    
    @Override
    public int interptet() {
        return mArithmeticExpression1.interptet() - mArithmeticExpression2.interptet();
    }
}

Calculatorswitch中添加如下代码即可

case "-":
    arithmeticExpression1 = mArithmeticExpressionStack.pop();
    arithmeticExpression2 = new NumExpression(Integer.valueOf(elements[++i]));
    
    mArithmeticExpressionStack.push(new SubtractionExpreesion(arithmeticExpression1, arithmeticExpression2));
    break;

3.小结

这里,我们能看出来解释器模式灵活性强,但是这是对于相对简单的语言;如果需要加入乘除取余等等,一并进行混合预算的话还需要考虑不同符号的运算优先级逻辑处理,所以在“简单的语言”中适用解释器模式。其实解释器模式的本质就是,将复杂的问题模块化,分离实现、解释执行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容