设计模式 - interpreter
interpreter模式在GOF中的定义是:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
定义看起来是非常晦涩。简单来讲,解释器模式通常用于处理特定格式的问题表示,通过定义解释器,能够解释该问题问题表示。例如我们工作过程中遇到的公式计算,公式可以看做是一种特定的问题表示,它具有通用性,正确的公式就应该被正确解析。如公式a+b+c,解释器模式就是要能够处理形如此种的文法,且能够提供相当的扩展性,如支持*,/功能等。
解释器在项目中使用较少,它多用于框架和工具类的开发,类图如下:
- AbstractExpression 抽象解释器
具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和NonterminalExpression完成。
- TerminalExpression终结符表达式
实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。具体到我们例子就是VarExpression类,表达式中的每个终结符都在堆栈中产生了一个VarExpression对象。
- NonterminalExpression 非终结符表达式
文法中的每条规则对应于一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
- Context 环境角色
具体到我们的例子中是采用HashMap代替
优点:
解释器模式的最突出的优点在于其扩展性,修改语法时只要修改相应的解释器就行了,扩展也非常容易,新增解释器即可。
缺点:
解释器会引起类膨胀,如果语法不断扩展,我们必须新增解释器,这样无可避免的导致类的膨胀;另外,解释器模式使用递归计算,这必然导致效率低下。
最佳实践:
解释器模式在实际使用中非常少,因其会导致性能和维护问题,一般它只会出现在数据分析报表设计等使用场景。对于平时的应用时,可以考虑已有的Expression4J、MESP(Math Expression String Parser)、Jep等开源的解析工具包,避免自己开发。
下面给出一个通过解释器实现四则运算的例子(http://www.cnblogs.com/cbf4life/archive/2009/12/17/1626125.html)
抽象表达式类:提供统一的接口
1 2 3 4 5 6 7 8 | package com.inspur.jiyq.designpattern.interpreter; import java.util.Map; public abstract class Expression { //解析公式和数值,其中var中的key值是是公式中的参数,value值是具体的数字 public abstract int interpreter(Map<String, Integer> var); } |
抽象符号表达式类:
1 2 3 4 5 6 7 8 9 10 11 12 13 | package com.inspur.jiyq.designpattern.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; } } |
加法解析器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package com.inspur.jiyq.designpattern.interpreter; import java.util.Map; public class AddExpression extends SymbolExpression { public AddExpression(Expression left, Expression right) { super (left, right); } @Override public int interpreter(Map<String, Integer> var) { return super .left.interpreter(var) + super .right.interpreter(var); } } |
减法解析器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package com.inspur.jiyq.designpattern.interpreter; import java.util.Map; public class SubExpression extends SymbolExpression { public SubExpression(Expression left, Expression right) { super (left, right); } @Override public int interpreter(Map<String, Integer> var) { return super .left.interpreter(var) - super .right.interpreter(var); } } |
变量解析器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package com.inspur.jiyq.designpattern.interpreter; import java.util.Map; public class VarExpression extends Expression{ private String key; public VarExpression(String key) { this .key = key; } @Override public int interpreter(Map<String, Integer> var) { return var.get( this .key); } } |
解析器封装类:(实现运算逻辑)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | package com.inspur.jiyq.designpattern.interpreter; import java.util.Map; import java.util.Stack; 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(); 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]))); } } this .expression = stack.pop(); } //开始运算 public int run(Map<String, Integer> var) { return this .expression.interpreter(var); } } |
驱动测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | package com.inspur.jiyq.designpattern.interpreter; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; public class Client { // 运行四则运算 public static void main(String[] args) throws IOException { String expStr = getExpStr(); // 赋值 Map<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.println( "请输入表达式:" ); return ( new BufferedReader( new InputStreamReader(System.in))) .readLine(); } // 获得值映射 public static Map<String, Integer> getValue(String exprStr) throws IOException { Map<String, Integer> map = new HashMap<String, Integer>(); // 解析有几个参数要传递 for ( char ch : exprStr.toCharArray()) { if (ch != '+' && ch != '-' ) { // 解决重复参数的问题 if (!map.containsKey(String.valueOf(ch))) { System.out.print( "请输入" + ch + "的值:" ); String in = ( new BufferedReader( new InputStreamReader( System.in))).readLine(); map.put(String.valueOf(ch), Integer.valueOf(in)); } } } return map; } } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Linux系统下SQL Server数据库镜像配置全流程详解
· 现代计算机视觉入门之:什么是视频
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· .NET Core GC压缩(compact_phase)底层原理浅谈
· Winform-耗时操作导致界面渲染滞后
· Phi小模型开发教程:C#使用本地模型Phi视觉模型分析图像,实现图片分类、搜索等功能
· 语音处理 开源项目 EchoSharp