Java设计模式之 [19] 行为型模式 - 解释器模式

简介

1.在编译原理中,一个算数表达式通常是词法分析器形成词法单元,而后这些词法单元在通过 语法分析器 构建语法分析树,最终形成一个抽象的语法分析树.这里的词法分析器和语法分析器都可以看成是解释器
2.解释器模式(Interpreter Pattern):是指定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
3.应用场景

  • 应用可以将一个需要执行的语言中的句子表示为一个抽象语法树.
  • 一些重复出现的问题可以用一种简单的语言来表达
  • 一个简单的语法需要解释的场景
    4.这样的例子还有:比如编译器.运算表达式,正则表达式,机器人等
原理类图与角色分析

1.类图


类图

2.角色分析
Context:是环境角色,含有解释器之外的全局信息
AbstractExpression:抽象表达式,声明一个抽象的解释操作,这个方法为抽象语法树中所有节点共享
TerminalExpression: 为终结者表达式.实现与文法中的终结符相关的解释操作
NoTerminalExpression : 为非终结者表达式,为文法中的非终结符实现的解释操作
说明:输入Context 和 TerminalExpression信息通过Cilent 输入即可

案例分析 四则运算问题

通过解释器模式来实现四则运算,如计算 a+b-c 的值 具体要求
1.先输入表达式的形式 比如 a+b+c-d-e 要求表达式的字母不能重复,
2,分别输入 a,b,c,d,e 的值
3.然后计算出结果


计算结果
问题分析

1.编写一个方法,接收表达式的形式,然后根据用户输入的数值进行解析计算,得到结果
2.问题分析:如果输入新的运算符.比如 / * 等等 不利于拓展 另外一个方法来解析会造成程序结构混乱
3.解决方案:可以考虑使用解释器模式 即 : 表达式 ==>> 解释器 (可以有多种) ==>> 结果

解释器模式实现四则运算

1.思路和图解分析


类图

2.代码实现

public class AddExpression extends SymbolExpression  {

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

    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) + super.right.interpreter(var);
    }
}
public class Calculator {

    // 定义表达式
    private Expression expression;

    // 构造函数传参,并解析
    public Calculator(String expStr) {
        // 安排运算先后顺序
        Stack<Expression> stack = new Stack<>();
        char[] charArray = expStr.toCharArray();

        Expression left = null;
        Expression right = null;
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
            case '+': //
                left = stack.pop();//
                right = new VarExpression(String.valueOf(charArray[++i]));//
                stack.push(new AddExpression(left, right));//
                break;
            case '-': // 
                left = stack.pop();
                right = new VarExpression(String.valueOf(charArray[++i]));
                stack.push(new SubExpression(left, right));
                break;
            default: 
                stack.push(new VarExpression(String.valueOf(charArray[i])));
                break;
            }
        }
        this.expression = stack.pop();
    }

    public int run(HashMap<String, Integer> var) {
        return this.expression.interpreter(var);
    }
}
public abstract class Expression {

    public abstract int interpreter(HashMap<String, Integer> var);
}
public class SubExpression extends SymbolExpression {

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

    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) - super.right.interpreter(var);
    }
}
/**
 * 抽象运算符号解析器 这里,每个运算符合都只和自己左右两个数字有关系,
 * 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是Expression类的实现类
 * 
 * @author Administrator
 *
 */
public class SymbolExpression extends Expression {

    protected Expression left;
    protected Expression right;

    public SymbolExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        // TODO Auto-generated method stub
        return 0;
    }
}
public class VarExpression extends Expression {

    private String key; 

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

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return var.get(this.key);
    }
}

测试

public class ClientTest {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        String expStr = getExpStr(); 
        HashMap<String, Integer> var = getValue(expStr);
        Calculator calculator = new Calculator(expStr);
        System.out.println("运算结果:" + expStr + "=" + calculator.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 expStr) throws IOException {
        HashMap<String, Integer> map = new HashMap<>();

        for (char ch : expStr.toCharArray()) {
            if (ch != '+' && ch != '-') {
                if (!map.containsKey(String.valueOf(ch))) {
                    System.out.print("请输入" + String.valueOf(ch) + "的值:");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    map.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }

        return map;
    }
}

测试结果

请输入表达式:a+b+c-d-e
请输入a的值:10
请输入b的值:50
请输入c的值:12
请输入d的值:78
请输入e的值:20
运算结果:a+b+c-d-e=-26
解释器设计模式在Spring框架应用的源码分析

1.Spring框架中,SpelExpressionParser 就使用到解释器模式

import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class Interpreter {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //创建一个 Parser 对象
        SpelExpressionParser parser = new SpelExpressionParser();
        //
        //通过 Parser 对象 获取到一个Expression对象
        //会根据不同的  Parser 对象 ,返回不同的 Expression对象
        Expression expression = parser.parseExpression("10 * (2 + 1) * 1 + 66"); //96
        int result = (Integer) expression.getValue();
        System.out.println(result);

    }

}
解释器模式的注意事项和细节

1.当有一个语言需要解释执行.可将该语言中的句子表示为一个抽象语法树,就可以考虑使用编译器模式,让程序具有良好的拓展性
2.应用场景:编译器,运算表达式计算.正则表达式,机器人等
3.使用解释器可能带来的问题:解释器模式会引起类膨胀,解释器模式采用递归调用方法,将会导致调试程序非常复杂,效率可能降低

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

推荐阅读更多精彩内容