具体要求如下:
- 先输入表达式的形式,比如说a+b-c+d+e,要求字母不能重复。
- 再分别输入a、b、c、d、e的值
- 最后输出结果。
传统方案介绍:
- 编写一个方法,接收表达式的形式,然后根据用户输入的数值进行解析,得到结果。
- 命令模式使得请求发送者和请求接受者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
传统方案带来的问题:
- 如果加入了新的运算符,比如说*/等等,不利于扩展,让另外一个方法解析会造成程序结构不清晰,十分混乱。
- 解决方案:可以考虑使用解释器模式。即:表达式->解释器->结果
解释器模式的基本介绍:
- 在编译原理中,一个算数表达式通过词法分析器形成词法单元,而这些词法单元通过语法分析器构建语法分析树,最终形成一个抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器。
- 解释器模式(Interpreter Pattern)是指给定一个语言表达式,定义了它的文法的一种表示,并定义了一个解释器,使用该解释器来解释语言中的句子(表达式)。
- 应用场景:
- 可以将一个需要执行中的语言的句子表示为一个抽象语法树。
- 一些重复的问题可以用一种简单的语言来表达。
- 一些简单语法需要解释器的场景。
- 这样的例子还有很多:比如编译器、运算表达式、正则表达式、机器人等。
解释器模式类图:
对原理类图的说明即解释器模式的角色和职责:
- Context:是环境对象角色。它含有解释器之外的全局信息。
- AbstractExpression:是抽象表达式角色,它是一个抽象类或者接口,声明一个抽象的解释操作。这个方法为抽象语法树中所有的节点所共享。
- TerminalExpression:为终结表达式,实现与文法中的终结符相关的解释操作。
- NonTerminalExpression:为非终结表达式,为文法中的非终结符相关的解释操作。
- 说明:输入Context和TerminalExpression信息通过Client输入即可。
解释器模式完成四则运算:
- UML类图:
关键代码:
package com.xia.designmode.study.interpreterpattern;
import java.util.HashMap;
/**
* 解释器模式--TerminalExpression角色, 变量解释器
* 通过HashMap键值对可以获取到变量的值。
* */
public class VarExperssion implements Expression{
private String key;//a,b,c
public VarExperssion(String key) {
this.key = key;
}
/**
* 根据变量名称返回对应的值。
* */
@Override
public int interpret(HashMap<String, Integer> varHashMap) {
return varHashMap.get(key);
}
}
package com.xia.designmode.study.interpreterpattern;
import java.util.HashMap;
/**
* 解释器模式--具体的NonTerminalExpression角色, 加法解释器
* */
public class AddExperssion extends AbstractSymbolExperssion{
public AddExperssion(Expression leftExpression, Expression rightExpression) {
super(leftExpression, rightExpression);
}
/*
* 处理相加
* var 的值依旧是前面的 {a=20,b=10}键值对
* */
@Override
public int interpret(HashMap<String, Integer> varHashMap) {
//leftExpression.interpret(varHashMap)返回左侧表达式解析后的值
//rightExpression.interpret(varHashMap)返回右侧表达式解析后的值
return leftExpression.interpret(varHashMap)+rightExpression.interpret(varHashMap);
}
}
package com.xia.designmode.study.interpreterpattern;
import java.util.HashMap;
/**
* 解释器模式--NonTerminalExpression角色, 符号解释器
* 它是抽象的运算符号解析器,每个运算符号都和自己的左右两个数字有关系
* 但是左右两个符号有可能是两个解析结果,无论和何种类型,都是Expression的实现类
* */
public abstract class AbstractSymbolExperssion implements Expression{
Expression leftExpression;
Expression rightExpression;
public AbstractSymbolExperssion(Expression leftExpression, Expression rightExpression) {
this.leftExpression = leftExpression;
this.rightExpression = rightExpression;
}
/**
* 本身是抽象的,所以并不实现下面的方法。它是让他的子类来实现的。
* */
}
解释器模式在Spring框架中的应用源码分析:
- Spring SpelExpressionParser中就是用了解释器模式。
- 代码分析:
- 模式的角色说明:
- Expression接口,表达式接口,下面有不同的实现类,比如我们用到的SpelExpression、CompositeStringException。
- 使用的时候,根据创建的不同的parser对象,返回不同的Exception对象。
解释器模式注意事项和细节:
- 有当一个语言需要解释执行时,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序有良好的扩展性。
- 应用场景:编译器、运算表达式计算、正则表达式、机器人等。
- 使用解释器带来的问题,解释器模式会引起类膨胀。解释器模式采用递归调用方法,最终导致的是调试异常复杂,效率可能会降低。