一、引子

其实没有什么好的例子引入解释器模式,因为它描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发编译器中;在实际应用中,我们可能很少碰到去构造一个语言的文法的情况。

虽然你几乎用不到这个模式,但是看一看还是能受到一定的启发的。

二、定义与结构

解释器模式的定义如下:定义语言的文法,并且建立一个解释器来解释该语言中的句子。它属于类的行为模式。这里的语言意思是使用规定格式和语法的代码。

在GOF的书中指出:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。而且当文法简单、效率不是关键问题的时候效果最好。

呵呵,这也就是解释器模式应用的环境了。

让我们来看看神秘的解释器模式是由什么来组成的吧。

1)        抽象表达式角色:声明一个抽象的解释操作,这个接口为所有具体表达式角色(抽象语法树中的节点)都要实现的。

什么叫做抽象语法树呢?《java与模式》中给的解释为:抽象语法树的每一个节点都代表一个语句,而在每个节点上都可以执行解释方法。这个解释方法的执行就代表这个语句被解释。由于每一个语句都代表这个语句被解释。由于每一个语句都代表一个常见的问题的实例,因此每一个节点上的解释操作都代表对一个问题实例的解答。

2)        终结符表达式角色:具体表达式。

a)        实现与文法中的终结符相关联的解释操作

b)        而且句子中的每个终结符需要该类的一个实例与之对应

3)        非终结符表达式角色:具体表达式。

a)        文法中的每条规则R::=R1R2…Rn都需要一个非终结符表带式角色

b)        对于从R1到Rn的每个符号都维护一个抽象表达式角色的实例变量

c)        实现解释操作,解释一般要递归地调用表示从R1到Rn的那些对象的解释操作

4)        上下文(环境)角色:包含解释器之外的一些全局信息。

5)        客户角色:

a)        构建(或者被给定)表示该文法定义的语言中的一个特定的句子的抽象语法树

b)        调用解释操作

放上张解释器结构类图吧,这也是来自于GOF的书中。

呵呵,对每一个角色都给出了详细的职责,而且在类图中给出五个角色之间的关系。这样实现起来也不是很困难了,下面举了一个简单的例子,希望能加深你对解释器模式的理解。

三、举例

来举一个加减乘除的例子吧,实现思路来自于《java与模式》中的例子。每个角色的功能按照上面提到的规范来实现。

  1. //上下文(环境)角色,使用HashMap来存储变量对应的数值
  2. class Context
  3. {
  4. private Map valueMap = new HashMap();
  5. public void addValue(Variable x , int y)
  6. {
  7. Integer yi = new Integer(y);
  8. valueMap.put(x , yi);
  9. }
  10. public int LookupValue(Variable x)
  11. {
  12. int i = ((Integer)valueMap.get(x)).intValue();
  13. return i ;
  14. }
  15. }
  1. //抽象表达式角色,也可以用接口来实现
  2. abstract class Expression
  3. {
  4. public abstract int interpret(Context con);
  5. }
  1. //终结符表达式角色
  2. class Constant extends Expression
  3. {
  4. private int i ;
  5. public Constant(int i)
  6. {
  7. this.i = i;
  8. }
  9. public int interpret(Context con)
  10. {
  11. return i ;
  12. }
  13. }
  1. class Variable extends Expression
  2. {
  3. public int interpret(Context con)
  4. {
  5. //this为调用interpret方法的Variable对象
  6. return con.LookupValue(this);
  7. }
  8. }
  1. //非终结符表达式角色
  2. class Add extends Expression
  3. {
  4. private Expression left ,right ;
  5. public Add(Expression left , Expression right)
  6. {
  7. this.left = left ;
  8. this.right= right ;
  9. }
  10. public int interpret(Context con)
  11. {
  12. return left.interpret(con) + right.interpret(con);
  13. }
  14. }
  1. class Subtract extends Expression
  2. {
  3. private Expression left , right ;
  4. public Subtract(Expression left , Expression right)
  5. {
  6. this.left = left ;
  7. this.right= right ;
  8. }
  9. public int interpret(Context con)
  10. {
  11. return left.interpret(con) - right.interpret(con);
  12. }
  13. }
  1. class Multiply extends Expression
  2. {
  3. private Expression left , right ;
  4. public Multiply(Expression left , Expression right)
  5. {
  6. this.left = left ;
  7. this.right= right ;
  8. }
  9. public int interpret(Context con)
  10. {
  11. return left.interpret(con) * right.interpret(con);
  12. }
  13. }
  1. class Division extends Expression
  2. {
  3. private Expression left , right ;
  4. public Division(Expression left , Expression right)
  5. {
  6. this.left = left ;
  7. this.right= right ;
  8. }
  9. public int interpret(Context con)
  10. {
  11. try{
  12. return left.interpret(con) / right.interpret(con);
  13. }catch(ArithmeticException ae)
  14. {
  15. System.out.println("被除数为0!");
  16. return -11111;
  17. }
  18. }
  19. }
  1. //测试程序,计算 (a*b)/(a-b+2)
  2. public class Test
  3. {
  4. private static Expression ex ;
  5. private static Context con ;
  6. public static void main(String[] args)
  7. {
  8. con = new Context();
  9. //设置变量、常量
  10. Variable a = new Variable();
  11. Variable b = new Variable();
  12. Constant c = new Constant(2);
  13. //为变量赋值
  14. con.addValue(a , 5);
  15. con.addValue(b , 7);
  16. //运算,对句子的结构由我们自己来分析,构造
  17. ex = new Division(new Multiply(a , b), new Add(new Subtract(a , b) , c));
  18. System.out.println("运算结果为:"+ex.interpret(con));
  19. }
  20. }

解释器模式并没有说明如何创建一个抽象语法树,因此它的实现可以多种多样,在上面我们是直接在Test中提供的,当然还有更好、更专业的实现方式。

对于终结符,GOF建议采用享元模式来共享它们的拷贝,因为它们要多次重复出现。但是考虑到享元模式的使用局限性,我建议还是当你的系统中终结符重复的足够多的时候再考虑享元模式(关于享元模式,请参考我的《深入浅出享元模式》)。

四、优缺点

解释器模式提供了一个简单的方式来执行语法,而且容易修改或者扩展语法。一般系统中很多类使用相似的语法,可以使用一个解释器来代替为每一个规则实现一个解释器。而且在解释器中不同的规则是由不同的类来实现的,这样使得添加一个新的语法规则变得简单。

但是解释器模式对于复杂文法难以维护。可以想象一下,每一个规则要对应一个处理类,而且这些类还要递归调用抽象表达式角色,多如乱麻的类交织在一起是多么恐怖的一件事啊!

五、总结

这样对解释器模式应该有了些大体的认识了吧,由于这个模式使用的案例匮乏,所以本文大部分观点直接来自于GOF的原著。只是实例代码是亲自实现并调试通过的。

参考:

http://www.cnblogs.com/java-my-life/archive/2012/06/19/2552617.html
http://wuquanyin1011.iteye.com/blog/709682

《JAVA与模式》之解释器模式 (转载)的更多相关文章

  1. Java设计模式学习记录-解释器模式

    前言 这次介绍另一个行为模式,解释器模式,都说解释器模式用的少,其实只是我们在日常的开发中用的少,但是一些开源框架中还是能见到它的影子,例如:spring的spEL表达式在解析时就用到了解释器模式,以 ...

  2. Java设计模式之九 ----- 解释器模式和迭代器模式

    前言 在上一篇中我们学习了行为型模式的责任链模式(Chain of Responsibility Pattern)和命令模式(Command Pattern).本篇则来学习下行为型模式的两个模式, 解 ...

  3. java设计模式-----16、解释器模式

    概念: Interpreter模式也叫解释器模式,是行为模式之一,它是一种特殊的设计模式,它建立一个解释器,对于特定的计算机程序设计语言,用来解释预先定义的文法.简单地说,Interpreter模式是 ...

  4. 行为型模式(十一) 解释器模式(Interpreter)

    一.动机(Motivate) 在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化.在这种情况下,将特定领域的问题表达为某种语法规 ...

  5. 《Java设计模式》之解释器模式

    解释器模式是类的行为模式.给定一个语言之后,解释器模式能够定义出其文法的一种表示,并同一时候提供一个解释器. client能够使用这个解释器来解释这个语言中的句子. 解释器模式的结构 以下就以一个示意 ...

  6. 《JAVA设计模式》之解释器模式(Interpreter)

    在阎宏博士的<JAVA与模式>一书中开头是这样描述解释器(Interpreter)模式的: 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个 ...

  7. [19/04/30-星期二] GOF23_行为型模式(中介者模式、命令模式、解释器模式、访问者模式)

    一.中介者模式(meditor) [中介] /*** * 抽象中介者接口和其具体实现类"经理"类 */ package cn.sxt.meditor; import java.ut ...

  8. 设计模式22---设计模式之解释器模式(Interpreter)(行为型)

    1.讲解解释器模式 1.1解释器模式定义 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 1.2解释器模式要点 解析器:把描述客户端调用要求的表达式, ...

  9. Java设计模式(24)——行为模式之解释器模式(Interpreter)

    一.概述 概念 自己定义文法,实际中还是很少出现的,作了解 给出一篇网友的参考博文:http://blog.csdn.net/ylchou/article/details/7594135

  10. 十一个行为模式之解释器模式(Interpreter Pattern)

    定义: 定义一个语言的文法,可以使用一个解释器来解释其文法.定义终结符和非终结符的统一接口,并使用抽象对象建立非终结符与其它元素的关联. 结构图: AbstractExpression:抽象表达式类, ...

随机推荐

  1. [PureScript] Break up Expressions into Cases in PureScript using Simple Pattern Matching

    Pattern matching in functional programming languages is a way to break up expressions into individua ...

  2. [Algorithm] Print 2-D array in spiral order

    The idea to solve the problem is set five variable, first direction, we need to switch direction aft ...

  3. Windows 环境 cygwin 安装 SSH

    本文内容 安装环境 安装 cygwin 安装 SSH 服务 启动 sshd 服务 SSH 免密码登录 验证 SSH 是否已安装成功 验证 SSH 是否可以免密码登录本机 安装环境 Windows 20 ...

  4. Elasticsearch - 理解字段分析过程(_analyze与_explain)

    我们经常会遇到问题.为什么指定的文档没有被搜索到.许多情况下, 这都归因于映射的定义和分析例程配置存在问题. 针对分析过程的调试,ElasticSearch提供了专用的REST API. _analy ...

  5. 【树莓派】树莓派刷Android系统

    树莓派3安装Android TV系统图文教程 http://www.mz6.net/news/android/6866.html 树莓派3 Android TV系统怎样安装?树莓派3一个重要用途就是当 ...

  6. [置顶] 在Visual Studio 2008上调试C语言程序

    C语言的地位和重要性就不用说了,但,很多人学习C语言,还在使用Visual C++ 6.0,甚至还有人使用Turbo C,很无语,只说一句吧:“OUT了". 让我们体验一下华丽的Visual ...

  7. ajax 与springmvc交互返回数据

    1.controller将数据封装成json格式返回页面 @RequestMapping("/dataList") public void datalist(CsoftCunsto ...

  8. ES6学习笔记十:模块的导入、导出

    一:模块导入 1) import { 要导入的属性.方法民 } from '模块路径'; 2)该种方法需要有配置文件,指明模块所在路径 import { 要导入的属性.方法民 } from '模块名' ...

  9. 【Fanvas技术解密】HTML5 canvas实现脏区重绘

    先说明一下,fanvas是笔者在企鹅公司开发的,即将开源的flash转canvas工具. 脏区重绘(dirty rectangle)并不是一门新鲜的技术了,这在最早2D游戏诞生的时候就已经存在. 复杂 ...

  10. 写了一个简单的Linux Shell用来下载文件

    #!/bin/sh ; i<; i=i+ )); do # 利用spider来探测请求的资源是否存在,并把请求的结果写入到一个文件 wget --spider --http-user=usern ...