《JAVA与模式》之解释器模式 (转载)
一、引子
其实没有什么好的例子引入解释器模式,因为它描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发编译器中;在实际应用中,我们可能很少碰到去构造一个语言的文法的情况。
虽然你几乎用不到这个模式,但是看一看还是能受到一定的启发的。
二、定义与结构
解释器模式的定义如下:定义语言的文法,并且建立一个解释器来解释该语言中的句子。它属于类的行为模式。这里的语言意思是使用规定格式和语法的代码。
在GOF的书中指出:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。而且当文法简单、效率不是关键问题的时候效果最好。
呵呵,这也就是解释器模式应用的环境了。
让我们来看看神秘的解释器模式是由什么来组成的吧。
1) 抽象表达式角色:声明一个抽象的解释操作,这个接口为所有具体表达式角色(抽象语法树中的节点)都要实现的。
什么叫做抽象语法树呢?《java与模式》中给的解释为:抽象语法树的每一个节点都代表一个语句,而在每个节点上都可以执行解释方法。这个解释方法的执行就代表这个语句被解释。由于每一个语句都代表这个语句被解释。由于每一个语句都代表一个常见的问题的实例,因此每一个节点上的解释操作都代表对一个问题实例的解答。
2) 终结符表达式角色:具体表达式。
a) 实现与文法中的终结符相关联的解释操作
b) 而且句子中的每个终结符需要该类的一个实例与之对应
3) 非终结符表达式角色:具体表达式。
a) 文法中的每条规则R::=R1R2…Rn都需要一个非终结符表带式角色
b) 对于从R1到Rn的每个符号都维护一个抽象表达式角色的实例变量
c) 实现解释操作,解释一般要递归地调用表示从R1到Rn的那些对象的解释操作
4) 上下文(环境)角色:包含解释器之外的一些全局信息。
5) 客户角色:
a) 构建(或者被给定)表示该文法定义的语言中的一个特定的句子的抽象语法树
b) 调用解释操作
放上张解释器结构类图吧,这也是来自于GOF的书中。

呵呵,对每一个角色都给出了详细的职责,而且在类图中给出五个角色之间的关系。这样实现起来也不是很困难了,下面举了一个简单的例子,希望能加深你对解释器模式的理解。
三、举例
来举一个加减乘除的例子吧,实现思路来自于《java与模式》中的例子。每个角色的功能按照上面提到的规范来实现。
- //上下文(环境)角色,使用HashMap来存储变量对应的数值
- class Context
- {
- private Map valueMap = new HashMap();
- public void addValue(Variable x , int y)
- {
- Integer yi = new Integer(y);
- valueMap.put(x , yi);
- }
- public int LookupValue(Variable x)
- {
- int i = ((Integer)valueMap.get(x)).intValue();
- return i ;
- }
- }
- //抽象表达式角色,也可以用接口来实现
- abstract class Expression
- {
- public abstract int interpret(Context con);
- }
- //终结符表达式角色
- class Constant extends Expression
- {
- private int i ;
- public Constant(int i)
- {
- this.i = i;
- }
- public int interpret(Context con)
- {
- return i ;
- }
- }
- class Variable extends Expression
- {
- public int interpret(Context con)
- {
- //this为调用interpret方法的Variable对象
- return con.LookupValue(this);
- }
- }
- //非终结符表达式角色
- class Add extends Expression
- {
- private Expression left ,right ;
- public Add(Expression left , Expression right)
- {
- this.left = left ;
- this.right= right ;
- }
- public int interpret(Context con)
- {
- return left.interpret(con) + right.interpret(con);
- }
- }
- class Subtract extends Expression
- {
- private Expression left , right ;
- public Subtract(Expression left , Expression right)
- {
- this.left = left ;
- this.right= right ;
- }
- public int interpret(Context con)
- {
- return left.interpret(con) - right.interpret(con);
- }
- }
- class Multiply extends Expression
- {
- private Expression left , right ;
- public Multiply(Expression left , Expression right)
- {
- this.left = left ;
- this.right= right ;
- }
- public int interpret(Context con)
- {
- return left.interpret(con) * right.interpret(con);
- }
- }
- class Division extends Expression
- {
- private Expression left , right ;
- public Division(Expression left , Expression right)
- {
- this.left = left ;
- this.right= right ;
- }
- public int interpret(Context con)
- {
- try{
- return left.interpret(con) / right.interpret(con);
- }catch(ArithmeticException ae)
- {
- System.out.println("被除数为0!");
- return -11111;
- }
- }
- }
- //测试程序,计算 (a*b)/(a-b+2)
- public class Test
- {
- private static Expression ex ;
- private static Context con ;
- public static void main(String[] args)
- {
- con = new Context();
- //设置变量、常量
- Variable a = new Variable();
- Variable b = new Variable();
- Constant c = new Constant(2);
- //为变量赋值
- con.addValue(a , 5);
- con.addValue(b , 7);
- //运算,对句子的结构由我们自己来分析,构造
- ex = new Division(new Multiply(a , b), new Add(new Subtract(a , b) , c));
- System.out.println("运算结果为:"+ex.interpret(con));
- }
- }
解释器模式并没有说明如何创建一个抽象语法树,因此它的实现可以多种多样,在上面我们是直接在Test中提供的,当然还有更好、更专业的实现方式。
对于终结符,GOF建议采用享元模式来共享它们的拷贝,因为它们要多次重复出现。但是考虑到享元模式的使用局限性,我建议还是当你的系统中终结符重复的足够多的时候再考虑享元模式(关于享元模式,请参考我的《深入浅出享元模式》)。
四、优缺点
解释器模式提供了一个简单的方式来执行语法,而且容易修改或者扩展语法。一般系统中很多类使用相似的语法,可以使用一个解释器来代替为每一个规则实现一个解释器。而且在解释器中不同的规则是由不同的类来实现的,这样使得添加一个新的语法规则变得简单。
但是解释器模式对于复杂文法难以维护。可以想象一下,每一个规则要对应一个处理类,而且这些类还要递归调用抽象表达式角色,多如乱麻的类交织在一起是多么恐怖的一件事啊!
五、总结
这样对解释器模式应该有了些大体的认识了吧,由于这个模式使用的案例匮乏,所以本文大部分观点直接来自于GOF的原著。只是实例代码是亲自实现并调试通过的。
参考:
http://www.cnblogs.com/java-my-life/archive/2012/06/19/2552617.html
http://wuquanyin1011.iteye.com/blog/709682
《JAVA与模式》之解释器模式 (转载)的更多相关文章
- Java设计模式学习记录-解释器模式
前言 这次介绍另一个行为模式,解释器模式,都说解释器模式用的少,其实只是我们在日常的开发中用的少,但是一些开源框架中还是能见到它的影子,例如:spring的spEL表达式在解析时就用到了解释器模式,以 ...
- Java设计模式之九 ----- 解释器模式和迭代器模式
前言 在上一篇中我们学习了行为型模式的责任链模式(Chain of Responsibility Pattern)和命令模式(Command Pattern).本篇则来学习下行为型模式的两个模式, 解 ...
- java设计模式-----16、解释器模式
概念: Interpreter模式也叫解释器模式,是行为模式之一,它是一种特殊的设计模式,它建立一个解释器,对于特定的计算机程序设计语言,用来解释预先定义的文法.简单地说,Interpreter模式是 ...
- 行为型模式(十一) 解释器模式(Interpreter)
一.动机(Motivate) 在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化.在这种情况下,将特定领域的问题表达为某种语法规 ...
- 《Java设计模式》之解释器模式
解释器模式是类的行为模式.给定一个语言之后,解释器模式能够定义出其文法的一种表示,并同一时候提供一个解释器. client能够使用这个解释器来解释这个语言中的句子. 解释器模式的结构 以下就以一个示意 ...
- 《JAVA设计模式》之解释器模式(Interpreter)
在阎宏博士的<JAVA与模式>一书中开头是这样描述解释器(Interpreter)模式的: 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个 ...
- [19/04/30-星期二] GOF23_行为型模式(中介者模式、命令模式、解释器模式、访问者模式)
一.中介者模式(meditor) [中介] /*** * 抽象中介者接口和其具体实现类"经理"类 */ package cn.sxt.meditor; import java.ut ...
- 设计模式22---设计模式之解释器模式(Interpreter)(行为型)
1.讲解解释器模式 1.1解释器模式定义 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 1.2解释器模式要点 解析器:把描述客户端调用要求的表达式, ...
- Java设计模式(24)——行为模式之解释器模式(Interpreter)
一.概述 概念 自己定义文法,实际中还是很少出现的,作了解 给出一篇网友的参考博文:http://blog.csdn.net/ylchou/article/details/7594135
- 十一个行为模式之解释器模式(Interpreter Pattern)
定义: 定义一个语言的文法,可以使用一个解释器来解释其文法.定义终结符和非终结符的统一接口,并使用抽象对象建立非终结符与其它元素的关联. 结构图: AbstractExpression:抽象表达式类, ...
随机推荐
- ASP入门(八)-Request对象
Request是ASP的耳朵,它可以获得客户端传递来的消息. BinaryRead方法和TotalBytes属性 用于以二进制方式读取客户端的请求,比如处理通过客户端上传文件时,通常使用这个方法来读取 ...
- 百科知识 英特尔处理器I5 4460和4590有哪些区别
4460是855元 4590是880元 i5 4460与4590CPU主要区别在:1.主频差0.3GHz;;2.最大睿频相差0.5GHz;:3.核显(HD4600)最大动态频率相差0.1GHz ...
- 【Ubuntu】Ubuntu bug “Reached target Shutdown”
在使用 Ubuntu-16.04.3-server-amd64.iso 版本(参考官方,iso下载地址)的时候,遇到一个Ubuntu的Bug,后来查了一下,在官方看到,目前还是Unassigned: ...
- 微软BI 之SSRS 系列 - 在 Cube 中通过 MDX 查询实现基于父子递归关系的汇总报表
之前我写了一篇在 SSRS 开发中处理这种父子关系的汇总与聚合的文章 (SSRS 系列 - 使用分组 Group 属性实现基于父子递归关系的汇总报表),示例中的查询是基于 SQL Server 关系型 ...
- HTTP长连接与短链接
想要充分了解HTTP长连接,需要首先知道一些基本概念: TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这 ...
- SpringBoot2中配置文件的调整,升级SpringBoot2时候注意的坑
原来使用SpringBoot1.5最近写个demo后发现原来的配置文件不能用了. 最后上网查询了一下资料,springboot2.0和spring1.x还是存在不少问题的. 1.问题一:Java版本要 ...
- Java多线程之创建线程的三种方式比较
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6560057.html 一:继承Thread类创建线程 1:继承Thread类定义线程子类: 2:重写run( ...
- ACE .i .inl文件(转)
在ACE的源代码目录里,有源文件.cpp.头文件.h,我们还发现有以.i和.inl为扩展名的文件.其实,以.i和.inl为扩展名的文件是ACE源码中inline函数的存放形式. 在说明ACE中为什么采 ...
- Laravel中pluck的使用——返回指定的字段值信息列表
$model = self::where(['is_delete' => 0, 'is_on_sale' => 1]) ->whereIn('goods.cat_id', Goods ...
- MyBatis Spring SqlSessionFactoryBean 配置
在基本的 MyBatis 中,session 工厂可以使用 SqlSessionFactoryBuilder 来创建.而在 MyBatis-Spring 中,则使用 SqlSessionFactory ...