前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式实战 - 解释器模式(Interpreter Pattern)

设计模式实战 - 解释器模式(Interpreter Pattern)

作者头像
JavaEdge
发布2018-12-17 16:23:14
8990
发布2018-12-17 16:23:14
举报
文章被收录于专栏:JavaEdgeJavaEdge

0 讲讲运算的核心——模型公式及其如何实现

0.1 业务需求:输入一个模型公式(加、减运算),然后输入模型中的参数,运算出结果

设计要求

● 公式可以运行时编辑,并且符合正常算术书写方式,例如a+b-c ● 高扩展性,未来增加指数、开方、极限、求导等运算符号时较少改动 ● 效率可以不用考虑,晚间批量运算

需求不复杂,若仅仅对数字采用四则运算,每个程序员都可以写出来 但是增加了增加模型公式就复杂了

先解释一下为什么需要公式,而不采用直接计算的方法,例如有如下3个公式 ● 业务种类1的公式:a+b+c-d ● 业务种类2的公式:a+b+e-d ● 业务种类3的公式:a-f 其中,a、b、c、d、e、f参数的值都可以取得,如果使用直接计算数值的方法需要为每个品种写一个算法,目前仅仅是3个业务种类,那上百个品种呢?凉透了吧!建立公式,然后通过公式运算才是王道

以实现加减法公式为例,说明如何解析一个固定语法逻辑 采用逐步分析方法,带领大家了解实现过程

想想公式中有什么?运算元素和运算符号

  • 运算元素 指a、b、c等符号,需要具体赋值的对象,也叫做终结符号,为什么叫终结符号呢? 因为这些元素除了需要赋值外,不需要做任何处理,所有运算元素都对应一个具体的业务参数,这是语法中最小的单元逻辑,不可再分
  • 运算符号 就是加减符号,需要我们编写算法进行处理,每个运算符号都要对应处理单元,否则公式无法运行,运算符号也叫做非终结符号

共同点是都要被解析,不同点是所有运算元素具有相同的功能,可以用一个类表示 而运算符号则是需要分别进行解释,加法需要加法解析器,减法需要减法解析器

初步分析加减法类图

这是一个很简单的类图 VarExpression用来解析运算元素,各个公式能运算元素的数量是不同的,但每个运算元素都对应一个VarExpression对象 SybmolExpression负责解析符号,由两个子类

  • AddExpression(负责加法运算)
  • SubExpression(负责减法运算) 解析器的开发工作已经完成了,但是需求还没有完全实现。我们还需要对解析器进行封装, 来实现

解析的工作完成了,我们还需要把安排运行的先后顺序(加减法不用考虑,但是乘除法呢?注意扩展性),并且还要返回结果,因此我们需要增加一个封装类来进行封装处理,由于我们只做运算,暂时还不与业务有关联,定义为Calculator

优化后加减法类图

Calculator的作用是封装,根据迪米特法则,Client只与直接的朋友Calculator交流,与其他类没关系

完整加减法类图

代码实现

  • Expression抽象类
  • 变量解析器
  • 抽象运算符号解析器

每个运算符号都只和自己左右两个数字有关系,但左右两个数字有可能也是一个解析的结果,无论何种类型,都是Expression的实现类,于是在对运算符解析的子类中增加了一个构造函数,传递左右两个表达式。

  • 加法解析器
  • 减法解析器
  • Calculator 我们还需要对解析器进行封装

Calculator构造函数接收一个表达式,然后把表达式转化为char数组,并判断运算符号,如果是“+”则进行加法运算,把左边的数(left变量)和右边的数(right变量)加起来就可以了 那左边的数为什么是在栈中呢?例如这个公式:a+b-c,根据for循环,首先被压入栈中的应该是有a元素生成的VarExpression对象,然后判断到加号时,把a元素的对象VarExpression从栈中弹出,与右边的数组b进行相加,b又是怎么得来的呢?当前的数组游标下移一个单元格即可,同时为了防止该元素再次被遍历,则通过++i的方式跳过下一个遍历——于是一个加法的运行结束。减法也采用相同的运行原理。

  • 客户模拟类 为了满足业务要求,我们设置了一个Client类来模拟用户情况,用户要求可以扩展,可以修改公式,那就通过接收键盘事件来处理

其中,getExpStr是从键盘事件中获得的表达式,getValue方法是从键盘事件中获得表达式中的元素映射值 ● 首先,要求输入公式。 ● 其次,要求输入公式中的参数。 ● 最后,运行出结果

我们是不是可以修改公式?当然可以,我们只要输入公式,然后输入相应的值就可以了,公式是在运行时定义的,而不是在运行前就制定好的 先公式,然后赋值,运算出结果

需求已经开发完毕,公式可以自由定义,只要符合规则(有变量有运算符合)就可以运算出结果;若需要扩展也非常容易,只要增加BaseSymbolExpression的子类就可以了,这就是解释器模式。

1 定义与类型

  • 解释器模式 Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.(给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。) 一种按照规定语法进行解析的方案,在现在项目中使用较少

解释器模式通用类图

● AbstractExpression——抽象解释器 具体的解释任务由各个实现类完成 具体的解释器分别由TerminalExpressionNon-terminalExpression完成

● TerminalExpression——终结符表达式 实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符 具体到我们例子就是VarExpression类,表达式中的每个终结符都在栈中产生了一个VarExpression对象

● NonterminalExpression——非终结符表达式

文法中的每条规则对应于一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。

● Context——环境角色

具体到我们的例子中是采用HashMap代替。

解释器是一个比较少用的模式,以下为其通用源码,可以作为参考。抽象表达式通常只有一个方法,如代码清单27-8所示。

适用场景

优点

缺点

相关设计模式

适配器模式不需要预先知道要适配的规则 而解释器模式则需要将规则写好,并根据规则进行解释

实际应用

Java 正则对象

Spring解析器

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.11.26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0 讲讲运算的核心——模型公式及其如何实现
    • 0.1 业务需求:输入一个模型公式(加、减运算),然后输入模型中的参数,运算出结果
      • 设计要求
      • 代码实现
  • 1 定义与类型
  • 适用场景
  • 优点
  • 缺点
  • 相关设计模式
  • 实际应用
    • Java 正则对象
      • Spring解析器
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档