设计模式学习笔记——解释器模式
2012-05-16 01:17
579 查看
定义:
给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
这个模式个人感觉比较难理解,需要借助例子来消化,先来看一个例子:即四则运算的模型公式,所谓模型公式就是可以自定义的一个表达式,自定义好表达式之后,对参数进行赋值,就可以计算出结果,关键是这个公式是可以自定义的,只要符合规定的文法即可,输入参数,计算结果。这就是一个解释器模式,输入的表达式就是定义中的“表示”,计算结果是由“解释器”执行的,所谓的解释器就是将输入的表达式进行解析,定义好运算顺序,然后计算结果。我们这里使用一个简单的例子,即只有加减法的运算,看类图:
这里面有几个角色需要说一下:
Expression:抽象表达式,具体的表达式由其子类完成,其子类分为两种,一种是终结型的表达式,即表达式中最小的数据单元,不能再被拆分的数据单元,再一种是非终结型的表达式,文法中的每条规则对应一个非终结表达式,非终结表达式根据逻辑的复杂程度而增加。
VarExpression:终结型表达式,其中封装的是最小数据单元的表达式,如a+b-c中的a、b、c 。
SymbolExpression:非终结型表达式,它是根据运算符号进行封装的,所谓非终结型表达式,就是由几个终结型表达式和运算符号组合成的表达式,其具体的是哪种表达式,是由其子类去实现的。
Calculator:是对解释器的封装,使其不对外暴露太多,通过构造函数,将表达式解析成对象表示的表达式。
来看一下源代码:
/*
* 抽象表达式类
*/
public abstract class Expression {
public abstract int interpreter(HashMap<String,Integer> var);
}
解释器模式的优点:
解释器是一个简单的语法分析工具,它最显著的优点就是扩展性,修改语法规则只需修改相应的非终结符表达式就可以了,若要扩展语法,则只要增加非终结符类就可以了。
解释器模式的缺点:
1、当语法规则比较复杂时,会引起类膨胀,难以维护。
2、解释器模式采用的是递归调用方法,即在对最终封装好的对象表达式进行计算时,是一层一层递归的去计算的,不仅难调试,效率也不高。
使用场景:
1、即基本的数据要素是相同的,但是组织他们的格式却不相同,则可以使用解释器模式。
2、一个简单语法需要解释的场景。
PS:当需要使用解释器模式时,可以考虑一下现成的解析工具,比如Expression4J,MESP(Math Expression String Parser),Jep等开源的解析工具,效率不错,可以实现大多数的数学运算。
给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
这个模式个人感觉比较难理解,需要借助例子来消化,先来看一个例子:即四则运算的模型公式,所谓模型公式就是可以自定义的一个表达式,自定义好表达式之后,对参数进行赋值,就可以计算出结果,关键是这个公式是可以自定义的,只要符合规定的文法即可,输入参数,计算结果。这就是一个解释器模式,输入的表达式就是定义中的“表示”,计算结果是由“解释器”执行的,所谓的解释器就是将输入的表达式进行解析,定义好运算顺序,然后计算结果。我们这里使用一个简单的例子,即只有加减法的运算,看类图:
这里面有几个角色需要说一下:
Expression:抽象表达式,具体的表达式由其子类完成,其子类分为两种,一种是终结型的表达式,即表达式中最小的数据单元,不能再被拆分的数据单元,再一种是非终结型的表达式,文法中的每条规则对应一个非终结表达式,非终结表达式根据逻辑的复杂程度而增加。
VarExpression:终结型表达式,其中封装的是最小数据单元的表达式,如a+b-c中的a、b、c 。
SymbolExpression:非终结型表达式,它是根据运算符号进行封装的,所谓非终结型表达式,就是由几个终结型表达式和运算符号组合成的表达式,其具体的是哪种表达式,是由其子类去实现的。
Calculator:是对解释器的封装,使其不对外暴露太多,通过构造函数,将表达式解析成对象表示的表达式。
来看一下源代码:
/*
* 抽象表达式类
*/
public abstract class Expression {
public abstract int interpreter(HashMap<String,Integer> var);
}
/* * 变量解析器,将运算变量进行封装,运算变量的值存储在外部的HashMap中, * 此处通过interpreter()方法从map中取出来 */ 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); } }
/* * 抽象运算符号解析器,运算符号只关心左右两边的表达式, * 对左右两边表达式具体进行什么操作是由子类去实现interpreter()方法来体现的 */ public abstract class SymbolExpression extends Expression { //运算符号左右两边的运算表达式 protected Expression left; protected Expression right; public SymbolExpression(Expression left, Expression right){ this.left=left; this.right=right; } @Override public abstract int interpreter(HashMap<String, Integer> var); }
/* * 加法解析器 */ public class AddExpression extends SymbolExpression { public AddExpression(Expression left, Expression right) { super(left, right); } //实现表达式的加法 @Override public int interpreter(HashMap<String, Integer> var) { return super.left.interpreter(var)+super.right.interpreter(var); } }
/* * 减法解析器 */ public class SubExpression extends SymbolExpression { public SubExpression(Expression left, Expression right) { super(left, right); } @Override 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<Expression>(); //表达式拆分成字符数组 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();//从栈中去出运算符号左边的表达式 //从字符数组中取出运算符合右边的第一个字符,并将其封装成VarExpression对象 right=new VarExpression(String.valueOf(charArray[++i])); stack.push(new AddExpression(left,right));//将组成的新的表达式压入栈中 break; case '-': left=stack.pop();//从栈中去出运算符号左边的表达式 //从字符数组中取出运算符合右边的第一个字符,并将其封装成VarExpression对象 right=new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left,right));//将组成的新的表达式压入栈中 break; default: stack.push(new VarExpression(String.valueOf(charArray[i])));//公式中的变量 } } this.expression=stack.pop();//将最终的运算结果弹出 } //将变量和变量对应的值传递进球,进行运算,这个运算是一个递归的运算 public int run(HashMap<String,Integer> var){ return this.expression.interpreter(var); } }
public class Client { public static void main(String[] args) throws IOException { //获得一个表达式 String expStr=getExpStr(); //对表达式进行赋值,保存在HashMap中 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 expStr) throws IOException{ HashMap<String,Integer> map=new HashMap<String,Integer>(); for(char ch:expStr.toCharArray()){ if(ch!='+' && ch!='-'){ if(!map.containsKey(String.valueOf(ch))){ System.out.print("输入"+ch+"的值:"); String value=(new BufferedReader(new InputStreamReader(System.in))).readLine(); map.put(String.valueOf(ch), Integer.parseInt(value)); } } } return map; } }
解释器模式的优点:
解释器是一个简单的语法分析工具,它最显著的优点就是扩展性,修改语法规则只需修改相应的非终结符表达式就可以了,若要扩展语法,则只要增加非终结符类就可以了。
解释器模式的缺点:
1、当语法规则比较复杂时,会引起类膨胀,难以维护。
2、解释器模式采用的是递归调用方法,即在对最终封装好的对象表达式进行计算时,是一层一层递归的去计算的,不仅难调试,效率也不高。
使用场景:
1、即基本的数据要素是相同的,但是组织他们的格式却不相同,则可以使用解释器模式。
2、一个简单语法需要解释的场景。
PS:当需要使用解释器模式时,可以考虑一下现成的解析工具,比如Expression4J,MESP(Math Expression String Parser),Jep等开源的解析工具,效率不错,可以实现大多数的数学运算。
相关文章推荐
- 设计模式学习笔记(24)——解释器模式
- 设计模式学习笔记--解释器模式
- 设计模式学习笔记——解释器(Interpreter)模式
- java/android 设计模式学习笔记(23)---解释器模式
- 再起航,我的学习笔记之JavaScript设计模式26(解释器模式)
- 再起航,我的学习笔记之JavaScript设计模式26(解释器模式)
- 步步为营 .NET 设计模式学习笔记 二十三、Interpreter(解释器模式)
- 设计模式学习笔记--解释器模式Interpreter与访问者模式Visitor(Java版)
- 设计模式学习笔记二十:解释器模式
- 设计模式学习笔记(二十三:解释器模式)
- 步步为营 .NET 设计模式学习笔记 二十三、Interpreter(解释器模式)
- 设计模式学习笔记--解释器模式
- 设计模式学习笔记-解释器模式
- 设计模式学习笔记——解释器模式
- 【设计模式学习笔记十六】【行为模式】【解释器模式(Interpreter)】
- 步步为营 .NET 设计模式学习笔记 二十三、Interpreter(解释器模式)
- 设计模式学习笔记——解释器模式
- 【设计模式】学习笔记17:代理模式之保护代理与Java反射
- 设计模式学习笔记--桥梁(Bridge)模式
- 设计模式学习笔记--享元模式