设计模式-解释器模式

解释器模式(InterPreter Pattern)

解释器模式是指给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用 该表示 来解释语言中的句子,并按照规定的语法进行解析的模式,属于行为型模式。

比如编译器可以将源码编译为机器码,让CPU能进行识别并运行。解释器模式的作用其实与编译器一样,都是对固定的文法进行解释,构建出一个解释句子的解释器。

解释器模式实际开发中使用较少,一般源码中会使用,比如Spring的el表达式,以及jdk对正则的支持等

适用场景

  • 一些重复出现的问题可以用一种简单的语言来进行表达
  • 一个简单语法需要解释的场景

角色

  • 抽象表达式Expression:负责定义一个解释方法interpret,交由具体子类进行具体解释
  • 终结符表达式TerminalExpression:实现文法中与终结符有关的操作。文法中的每一个终结符都有一个具体终结表达式与之相呼应。比如R=R1+R2,R1与R2就是终结符,对应的 解析R1和R2的解释器 就是终结符表达式。通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符(R1,R2)。
  • 非终结符表达式NonTerminalExpression:实现文法中与非终结符有关的解释操作。文法中的每条规则都对应于一个非终结符表达式。非终结表达式一般是文法中的运算符或者其他关键字。
  • 上下文环境类Context:包含解释器之外的全局信息。他的任务一般是用来存放文法中各个终结符所对应的具体值。

代码示例

用解释器模式实现一个数学表达式计算器

  • 抽象表达式角色:
package com.caozz.demo2.interpreter.calculate;

public interface IArithmeticInterpreter {
    int interpret();
}
  • 非终结表达式角色抽象
package com.caozz.demo2.interpreter.calculate;

public abstract class SymbolInterpreter implements IArithmeticInterpreter {

    protected IArithmeticInterpreter left;
    protected IArithmeticInterpreter right;

    public SymbolInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {
        this.left = left;
        this.right = right;
    }
}
  • 具体非终结表达式(加减乘除)
package com.caozz.demo2.interpreter.calculate;

public class AddInterpreter extends SymbolInterpreter {

    public AddInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {
        super(left, right);
    }

    public int interpret() {
        return this.left.interpret() + this.right.interpret();
    }
}
package com.caozz.demo2.interpreter.calculate;

public class SubInterpreter extends SymbolInterpreter {
    public SubInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {
        super(left, right);
    }

    public int interpret() {
        return this.left.interpret() - this.right.interpret();
    }
}
package com.caozz.demo2.interpreter.calculate;

public class MultiInterpreter extends SymbolInterpreter {

    public MultiInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right){
        super(left,right);
    }

    public int interpret() {
        return this.left.interpret() * this.right.interpret();
    }

}

package com.caozz.demo2.interpreter.calculate;

public class DivInterpreter extends SymbolInterpreter {

    public DivInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right){
        super(left,right);
    }

    public int interpret() {
        return this.left.interpret() / this.right.interpret();
    }

}

  • 终结符表达式(数字表达式)
package com.caozz.demo2.interpreter.calculate;

public class NumInterpreter implements IArithmeticInterpreter {
    private int value;

    public NumInterpreter(int value) {
        this.value = value;
    }


    public int interpret() {
        return this.value;
    }
}
  • 计算器
    parse方法对应的是不考虑优先级的,parse01是考虑优先级的
package com.caozz.demo2.interpreter.calculate;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class GPCalculator {
    private Stack<IArithmeticInterpreter> stack = new Stack<>();
    Queue<IArithmeticInterpreter> finalNum = new LinkedList<>();

    public GPCalculator(String expression) {
//        this.parse(expression);
        this.parse01(expression);
    }

    private void parse(String expression) {
        String [] elements = expression.split(" ");
        IArithmeticInterpreter leftExpr, rightExpr;

        for (int i = 0; i < elements.length ; i++) {
            String operator = elements[i];
            if (OperatorUtil.isOperator(operator)){
                leftExpr = this.stack.pop();
                rightExpr = new NumInterpreter(Integer.valueOf(elements[++i]));
                System.out.println("出栈: " + leftExpr.interpret() + " 和 " + rightExpr.interpret());
                this.stack.push(OperatorUtil.getInterpreter(leftExpr, rightExpr,operator));
                System.out.println("应用运算符: " + operator);
            }
            else{
                NumInterpreter numInterpreter = new NumInterpreter(Integer.valueOf(elements[i]));
                this.stack.push(numInterpreter);
                System.out.println("入栈: " + numInterpreter.interpret());
            }
        }
    }


    private void parse01(String expression) {
        String [] elements = expression.split(" ");
        Queue<String> symbols = new LinkedList<>();
        Queue<String> nums = new LinkedList<>();

        for (int i = 0; i < elements.length ; i++) {
            String ele = elements[i];
            if (OperatorUtil.isOperator(ele)){
                symbols.offer(ele);
            } else {
                nums.offer(ele);
            }
        }

        //先计算乘法
        Stack<String> nsymbols = new Stack<>();
        Stack<IArithmeticInterpreter> nnums = new Stack<>();
        nnums.push(new NumInterpreter(Integer.valueOf(nums.poll())));
        int symbolLen = symbols.size();
        for (int j = 0; j < symbolLen; j++) {
            String operator = symbols.poll();
            IArithmeticInterpreter next = new NumInterpreter(Integer.valueOf(nums.poll()));
            if (OperatorUtil.isAddSub(operator)) {
                nsymbols.push(operator);
                nnums.push(next);
            } else {
                IArithmeticInterpreter previous = nnums.pop();
                nnums.push(new MultiInterpreter(previous,next));
            }
        }


        //乘法计算完成后的
        int len = nsymbols.size();
        IArithmeticInterpreter first = nnums.pop();
        finalNum.offer(first);
        for (int h = 0; h < len; h++) {
            String e = nsymbols.pop();
            IArithmeticInterpreter pre = finalNum.poll();
            IArithmeticInterpreter next = nnums.pop();
            if ("+".equals(e)) {
                finalNum.offer(new AddInterpreter(pre,next));
            } else {
                finalNum.offer(new SubInterpreter(next,pre));
            }
        }
    }



    public int calculate() {
//        return this.stack.pop().interpret();
        IArithmeticInterpreter poll = this.finalNum.poll();
        return poll.interpret();
    }
}
  • 测试
package com.caozz.demo2.interpreter.calculate;

public class Test {

    public static void main(String[] args) {
        System.out.println("result(10 + 30): " + new GPCalculator("10 + 30").calculate());
        System.out.println("result(       50 + 30 - 20       ): " + new GPCalculator("50 + 30 - 20").calculate());
        System.out.println("result(        2 + 5 * 3         ): " + new GPCalculator("2 + 5 * 3").calculate());
        System.out.println("result(       2 * 3 + 4 * 5      ): " + new GPCalculator("2 * 3 + 4 * 5").calculate());
        System.out.println("result(    2 * 3 * 5 + 4 * 5     ): " + new GPCalculator("2 * 3 * 5 + 4 * 5").calculate());
        System.out.println("result(    2 * 5 + 4 * 5 * 6     ): " + new GPCalculator("2 * 5 + 4 * 5 * 6").calculate());
        //15 + 15 + 60 = 90
        System.out.println("result(3 * 5 + 6 + 9 + 2 * 3 * 10): " + new GPCalculator("3 * 5 + 6 + 9 + 2 * 3 * 10").calculate());
    }

}

  • 测试结果
D:\software\java\jdk-17.0.2\bin\java.exe ...
result(10 + 30): 40
result(       50 + 30 - 20       ): 60
result(        2 + 5 * 3         ): 17
result(       2 * 3 + 4 * 5      ): 26
result(    2 * 3 * 5 + 4 * 5     ): 50
result(    2 * 5 + 4 * 5 * 6     ): 130
result(3 * 5 + 6 + 9 + 2 * 3 * 10): 90

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

推荐阅读更多精彩内容