设计模式学习笔记——解释器模式

原文:http://blog.csdn.net/hackerain/article/details/7570904

定义:

给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。


这个模式个人感觉比较难理解,需要借助例子来消化,先来看一个例子:即四则运算的模型公式,所谓模型公式就是可以自定义的一个表达式,自定义好表达式之后,对参数进行赋值,就可以计算出结果,关键是这个公式是可以自定义的,只要符合规定的文法即可,输入参数,计算结果。这就是一个解释器模式,输入的表达式就是定义中的“表示”,计算结果是由“解释器”执行的,所谓的解释器就是将输入的表达式进行解析,定义好运算顺序,然后计算结果。我们这里使用一个简单的例子,即只有加减法的运算,看类图:


这里面有几个角色需要说一下:

Expression:抽象表达式,具体的表达式由其子类完成,其子类分为两种,一种是终结型的表达式,即表达式中最小的数据单元,不能再被拆分的数据单元,再一种是非终结型的表达式,文法中的每条规则对应一个非终结表达式,非终结表达式根据逻辑的复杂程度而增加。

VarExpression:终结型表达式,其中封装的是最小数据单元的表达式,如a+b-c中的a、b、c 。

SymbolExpression:非终结型表达式,它是根据运算符号进行封装的,所谓非终结型表达式,就是由几个终结型表达式和运算符号组合成的表达式,其具体的是哪种表达式,是由其子类去实现的。

Calculator:是对解释器的封装,使其不对外暴露太多,通过构造函数,将表达式解析成对象表示的表达式。


来看一下源代码:

[java]  view plain copy
  1. /* 
  2.  * 抽象表达式类 
  3.  */  
  4. public abstract class Expression {  
  5.     public abstract int interpreter(HashMap<String,Integer> var);  
  6. }  
[java]  view plain copy
  1. /* 
  2.  * 变量解析器,将运算变量进行封装,运算变量的值存储在外部的HashMap中, 
  3.  * 此处通过interpreter()方法从map中取出来 
  4.  */  
  5. public class VarExpression extends Expression {  
  6.     private String key;  
  7.       
  8.     public VarExpression(String key){  
  9.         this.key=key;  
  10.     }  
  11.       
  12.     //根据键值,从map中取出数值  
  13.     @Override  
  14.     public int interpreter(HashMap<String, Integer> var) {  
  15.         return var.get(this.key);  
  16.     }  
  17.   
  18. }  
[java]  view plain copy
  1. /* 
  2.  * 抽象运算符号解析器,运算符号只关心左右两边的表达式, 
  3.  * 对左右两边表达式具体进行什么操作是由子类去实现interpreter()方法来体现的 
  4.  */  
  5. public abstract class SymbolExpression extends Expression {  
  6.   
  7.     //运算符号左右两边的运算表达式  
  8.     protected Expression left;  
  9.     protected Expression right;  
  10.       
  11.     public SymbolExpression(Expression left, Expression right){  
  12.         this.left=left;  
  13.         this.right=right;  
  14.     }  
  15.       
  16.     @Override  
  17.     public abstract int interpreter(HashMap<String, Integer> var);  
  18.   
  19. }  
[java]  view plain copy
  1. /* 
  2.  * 加法解析器 
  3.  */  
  4. public class AddExpression extends SymbolExpression {  
  5.   
  6.     public AddExpression(Expression left, Expression right) {  
  7.         super(left, right);  
  8.     }  
  9.   
  10.     //实现表达式的加法  
  11.     @Override  
  12.     public int interpreter(HashMap<String, Integer> var) {  
  13.         return super.left.interpreter(var)+super.right.interpreter(var);  
  14.     }  
  15.   
  16. }  
[java]  view plain copy
  1. /* 
  2.  * 减法解析器 
  3.  */  
  4. public class SubExpression extends SymbolExpression {  
  5.   
  6.     public SubExpression(Expression left, Expression right) {  
  7.         super(left, right);  
  8.     }  
  9.   
  10.     @Override  
  11.     public int interpreter(HashMap<String, Integer> var) {  
  12.         return super.left.interpreter(var)-super.right.interpreter(var);  
  13.     }  
  14.   
  15. }  
[java]  view plain copy
  1. /* 
  2.  * 解析器封装类 
  3.  */  
  4. public class Calculator {  
  5.     //定义最终的表达式,即用对象表示的最终的表达式  
  6.     private Expression expression;  
  7.       
  8.     //构造函数传参,并解析  
  9.     public Calculator(String expStr){  
  10.         //定义一个栈,用来安排运算的先后顺序  
  11.         Stack<Expression> stack=new Stack<Expression>();  
  12.           
  13.         //表达式拆分成字符数组  
  14.         char[] charArray=expStr.toCharArray();  
  15.           
  16.         Expression left=null;  
  17.         Expression right=null;  
  18.           
  19.         for(int i=0;i<charArray.length;i++){  
  20.             switch(charArray[i]){  
  21.             case '+':  
  22.                 left=stack.pop();//从栈中去出运算符号左边的表达式  
  23.                   
  24.                 //从字符数组中取出运算符合右边的第一个字符,并将其封装成VarExpression对象  
  25.                 right=new VarExpression(String.valueOf(charArray[++i]));  
  26.                   
  27.                 stack.push(new AddExpression(left,right));//将组成的新的表达式压入栈中  
  28.                 break;  
  29.             case '-':  
  30.                 left=stack.pop();//从栈中去出运算符号左边的表达式  
  31.                   
  32.                 //从字符数组中取出运算符合右边的第一个字符,并将其封装成VarExpression对象  
  33.                 right=new VarExpression(String.valueOf(charArray[++i]));  
  34.                   
  35.                 stack.push(new SubExpression(left,right));//将组成的新的表达式压入栈中  
  36.                 break;  
  37.             default:  
  38.                 stack.push(new VarExpression(String.valueOf(charArray[i])));//公式中的变量  
  39.             }  
  40.         }  
  41.         this.expression=stack.pop();//将最终的运算结果弹出  
  42.     }  
  43.       
  44.     //将变量和变量对应的值传递进球,进行运算,这个运算是一个递归的运算  
  45.     public int run(HashMap<String,Integer> var){  
  46.         return this.expression.interpreter(var);  
  47.     }  
  48. }  
[java]  view plain copy
  1. public class Client {  
  2.     public static void main(String[] args) throws IOException {  
  3.           
  4.         //获得一个表达式  
  5.         String expStr=getExpStr();  
  6.           
  7.         //对表达式进行赋值,保存在HashMap中  
  8.         HashMap<String,Integer> var=getValue(expStr);  
  9.           
  10.         //解析表达式,将表达式封装成一个对象表示的表达式  
  11.         Calculator cal=new Calculator(expStr);  
  12.           
  13.         //对象表达式进行计算  
  14.         System.out.println("运算结果为:"+expStr+"="+cal.run(var));  
  15.     }  
  16.       
  17.     //输入表达式  
  18.     public static String getExpStr() throws IOException{  
  19.         System.out.print("输入表达式:");  
  20.         return (new BufferedReader(new InputStreamReader(System.in))).readLine();  
  21.     }  
  22.       
  23.     //对表达式赋值  
  24.     public static HashMap<String,Integer> getValue(String expStr) throws IOException{  
  25.           
  26.         HashMap<String,Integer> map=new HashMap<String,Integer>();  
  27.           
  28.         for(char ch:expStr.toCharArray()){  
  29.             if(ch!='+' && ch!='-'){  
  30.                 if(!map.containsKey(String.valueOf(ch))){  
  31.                     System.out.print("输入"+ch+"的值:");  
  32.                     String value=(new BufferedReader(new InputStreamReader(System.in))).readLine();  
  33.                     map.put(String.valueOf(ch), Integer.parseInt(value));  
  34.                 }  
  35.             }  
  36.         }  
  37.         return map;  
  38.     }  
  39. }  


解释器模式的优点:

解释器是一个简单的语法分析工具,它最显著的优点就是扩展性,修改语法规则只需修改相应的非终结符表达式就可以了,若要扩展语法,则只要增加非终结符类就可以了。

解释器模式的缺点:

1、当语法规则比较复杂时,会引起类膨胀,难以维护。

2、解释器模式采用的是递归调用方法,即在对最终封装好的对象表达式进行计算时,是一层一层递归的去计算的,不仅难调试,效率也不高。


使用场景:

1、即基本的数据要素是相同的,但是组织他们的格式却不相同,则可以使用解释器模式。

2、一个简单语法需要解释的场景。


PS:当需要使用解释器模式时,可以考虑一下现成的解析工具,比如Expression4J,MESP(Math Expression String Parser),Jep等开源的解析工具,效率不错,可以实现大多数的数学运算。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值