行为型模式(十一) 解释器模式(Interpreter)
一、动机(Motivate)
在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。
二、意图(Intent)
给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。 ——《设计模式》GoF
三、结构图(Structure)

四、模式的组成
可以看出,在解释器模式的结构图有以下角色:
(1)、抽象表达式(AbstractExpression):定义解释器的接口,约定解释器的解释操作。其中的Interpret接口,正如其名字那样,它是专门用来解释该解释器所要实现的功能。
(2)、终结符表达式(Terminal Expression):实现了抽象表达式角色所要求的接口,主要是一个interpret()方法;文法中的每一个终结符都有一个具体终结表达式与之相对应。比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
(3)、非终结符表达式(Nonterminal Expression):文法中的每一条规则都需要一个具体的非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,“+”就是非终结符,解析“+”的解释器就是一个非终结符表达式。
(4)、环境角色(Context):这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。
(5)、客户端(Client):指的是使用解释器的客户端,通常在这里将按照语言的语法做的表达式转换成使用解释器对象描述的抽象语法树,然后调用解释操作。
五、解释器模式的代码实现
在很多场合都需要把数字转换成中文,我们就可以使用解释器来实现该功能,把给定的数字解释成符合语法规范的汉字表示法。实现代码如下:
static void Main(string[] args)
{
string roman = "五亿七千三百零二万六千四百五十二";
//分解:((五)亿)((七千)(三百)(零)(二)万)
//((六千)(四百)(五十)(二)) Context context = new Context(roman);
ArrayList tree = new ArrayList(); tree.Add(new GeExpression());
tree.Add(new ShiExpression());
tree.Add(new BaiExpression());
tree.Add(new QianExpression());
tree.Add(new WanExpression());
tree.Add(new YiExpression()); foreach (Expression exp in tree)
{
exp.Interpreter(context);
} Console.Write(context.Data);
}
// 抽象表达式
public abstract class Expression
{
protected Dictionary<string, int> table = new Dictionary<string, int>(9); protected Expression()
{
table.Add("一", 1);
table.Add("二", 2);
table.Add("三", 3);
table.Add("四", 4);
table.Add("五", 5);
table.Add("六", 6);
table.Add("七", 7);
table.Add("八", 8);
table.Add("九", 9);
} public virtual void Interpreter(Context context)
{
if (context.Statement.Length == 0)
{
return;
} foreach (string key in table.Keys)
{
int value = table[key]; if (context.Statement.EndsWith(key + GetPostFix()))
{
context.Data += value * this.Multiplier();
context.Statement = context.Statement.Substring(0, context.Statement.Length - this.GetLength());
}
if (context.Statement.EndsWith("零"))
{
context.Statement = context.Statement.Substring(0, context.Statement.Length - 1);
}
}
} public abstract string GetPostFix(); public abstract int Multiplier(); //这个可以通用,但是对于个位数字例外,所以用虚方法
public virtual int GetLength()
{
return this.GetPostFix().Length + 1;
}
} //个位表达式
public sealed class GeExpression : Expression
{
public override string GetPostFix()
{
return "";
} public override int Multiplier()
{
return 1;
} public override int GetLength()
{
return 1;
}
} //十位表达式
public sealed class ShiExpression : Expression
{
public override string GetPostFix()
{
return "十";
} public override int Multiplier()
{
return 10;
}
} //百位表达式
public sealed class BaiExpression : Expression
{
public override string GetPostFix()
{
return "百";
} public override int Multiplier()
{
return 100;
}
} //千位表达式
public sealed class QianExpression : Expression
{
public override string GetPostFix()
{
return "千";
} public override int Multiplier()
{
return 1000;
}
} //万位表达式
public sealed class WanExpression : Expression
{
public override string GetPostFix()
{
return "万";
} public override int Multiplier()
{
return 10000;
} public override void Interpreter(Context context)
{
if (context.Statement.Length == 0)
{
return;
} ArrayList tree = new ArrayList(); tree.Add(new GeExpression());
tree.Add(new ShiExpression());
tree.Add(new BaiExpression());
tree.Add(new QianExpression()); foreach (string key in table.Keys)
{
if (context.Statement.EndsWith(GetPostFix()))
{
int temp = context.Data;
context.Data = 0; context.Statement = context.Statement.Substring(0, context.Statement.Length - this.GetLength()); foreach (Expression exp in tree)
{
exp.Interpreter(context);
}
context.Data = temp + context.Data * this.Multiplier();
}
}
}
} //亿位表达式
public sealed class YiExpression : Expression
{
public override string GetPostFix()
{
return "亿";
} public override int Multiplier()
{
return 100000000;
} public override void Interpreter(Context context)
{
ArrayList tree = new ArrayList(); tree.Add(new GeExpression());
tree.Add(new ShiExpression());
tree.Add(new BaiExpression());
tree.Add(new QianExpression()); foreach (string key in table.Keys)
{
if (context.Statement.EndsWith(GetPostFix()))
{
int temp = context.Data;
context.Data = 0;
context.Statement = context.Statement.Substring(0, context.Statement.Length - this.GetLength()); foreach (Expression exp in tree)
{
exp.Interpreter(context);
}
context.Data = temp + context.Data * this.Multiplier();
}
}
}
} //环境上下文
public sealed class Context
{
private string _statement;
private int _data; public Context(string statement)
{
this._statement = statement;
} public string Statement
{
get { return this._statement; }
set { this._statement = value; }
} public int Data
{
get { return this._data; }
set { this._data = value; }
}
}
六、解释器模式的实现要点:
使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧方便地“扩展”文法。
Interpreter模式比较适合简单的文法表示,对于复杂的文法表示,Interpreter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具。
1、解释器模式的主要优点有:
1】、易于改变和扩展文法。
2】、每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
3】、实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
4】、增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”
2、解释器模式的主要缺点有:
1】、对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。
2】、执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
3、在下面的情况下可以考虑使用解释器模式:
Interpreter模式的应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的模式不断重复出现,并且容易抽象为语法规则的问题”才适合使用Interpreter模式。
1】、当一个语言需要解释执行,并可以将该语言中的句子表示为一个抽象语法树的时候,可以考虑使用解释器模式(如XML文档解释、正则表达式等领域)
2】、一些重复出现的问题可以用一种简单的语言来进行表达。
3】、一个语言的文法较为简单.
4】、当执行效率不是关键和主要关心的问题时可考虑解释器模式(注:高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。)
七、.NET 解释器模式的实现
正则表达式就是一个典型的解释器。ASP.NET中,把aspx文件转化为dll时,会对html语言进行处理,这个处理过程也包含了解释器的模式在里面。Interpreter模式其实有Composite模式的影子,但它们解决的问题是不一样的。
八、总结
(1)解释器和组合模式
这两种可以组合使用,一般非终结符解释器相当于组合模式中的组合对象,终结符解释器相当于叶子对象。
(2)解释器模式和迭代器模式
由于解释器模式通常使用组合模式来实现,因此在遍历整个对象结构时,可以使用迭代器模式。
(3)解释器模式和享元模式
在使用解释器模式的时候,可能会造成多个细粒度对象,如各式各样的终结符解释器,而这些终结符解释器对不同的表达式来说是一样的,是可以共用的,因此可以引入享元模式来共享这些对象。
(4)解释器模式和访问者模式
在解释器模式中,语法规则和解释器对象是有对应关系的。语法规则的变动意味着功能的变化。自然会导致使用不同的解释器对象;而且一个语法规由可以被不同的解释器解释执行。因此在构建抽象语法树的时候,如果每个节点所对应的解释器对象是固定的,这意味着该节点对应的功能是固定的,那么就不得不根据需要来构建不同的抽象语法树。为了让构建的抽象语法树较为通用,那就要求解释器的功能不要那么固定,要能很方便地改变解释器的功能,这个时候就变成了如何能够很方便地更改树形结构中节点对象的功能了,访问者模式可以很好的实现这个功能。
行为型模式(十一) 解释器模式(Interpreter)的更多相关文章
- 设计模式22---设计模式之解释器模式(Interpreter)(行为型)
1.讲解解释器模式 1.1解释器模式定义 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 1.2解释器模式要点 解析器:把描述客户端调用要求的表达式, ...
- 行为型设计模式之解释器模式(Interpreter)
结构 意图 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 适用性 当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用 ...
- [19/04/30-星期二] GOF23_行为型模式(中介者模式、命令模式、解释器模式、访问者模式)
一.中介者模式(meditor) [中介] /*** * 抽象中介者接口和其具体实现类"经理"类 */ package cn.sxt.meditor; import java.ut ...
- 十一个行为模式之解释器模式(Interpreter Pattern)
定义: 定义一个语言的文法,可以使用一个解释器来解释其文法.定义终结符和非终结符的统一接口,并使用抽象对象建立非终结符与其它元素的关联. 结构图: AbstractExpression:抽象表达式类, ...
- GoF23种设计模式之行为型模式之解释器模式
一.概述 给定一种语言和其文法的一种表示,再定义一个解释器,该解释器使用表示来解释语言中的句子. 二.适用性 当需要解释一种语言,并且可以将该语言中的句子表示 ...
- Java设计模式(24)——行为模式之解释器模式(Interpreter)
一.概述 概念 自己定义文法,实际中还是很少出现的,作了解 给出一篇网友的参考博文:http://blog.csdn.net/ylchou/article/details/7594135
- C#设计模式之二十三解释器模式(Interpreter Pattern)【行为型】
一.引言 今天我们开始讲"行为型"设计模式的第十一个模式,也是面向对象设计模式的最后一个模式,先要说明一下,其实这个模式不是最后一个模式(按Gof的排序来讲),为什么把它放在最 ...
- 深入浅出设计模式——解释器模式(Interpreter Pattern)
模式动机 如果在系统中某一特定类型的问题发生的频率很高,此时可以考虑将这些问题的实例表述为一个语言中的句子,因此可以构建一个解释器,该解释器通过解释这些句子来解决这些问题.解释器模式描述了如何构成一个 ...
- 北风设计模式课程---解释器模式(Interpreter Pattern)
北风设计模式课程---解释器模式(Interpreter Pattern) 一.总结 一句话总结: 不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例 设计模式都是对生活的抽象,比如用 ...
随机推荐
- pdflatex 插入eps图片
1. 将eps图片转成pdf或者将pdf图片转成eps,也就是说一张图片有pdf.eps两种格式.方法一: \includegraphics{pic} %不要扩展名.这样pdflatex自动调相应的p ...
- Prometheus入门到放弃(5)之AlertManager部署
alertmanager与exporters.cadvisor一样,都是独立于prometheus项目,这里我们也使用docker方式部署alertmanager. 1.下载镜像 镜像地址:https ...
- tomcat-修改端口--号
1.背景 在默认情况下,tomcat的端口是8080,使用了两个tomcat,那么就需要修改其中的一个的端口号才能使得两个同时工作. 2.方法 2.1改动一 那么,如何修改tomcat的端口号呢?首先 ...
- Delphi百度语音【支持语音识别和语音合成】
作者QQ:(648437169) 点击下载➨百度语音 语音识别api文档 语音合成api文档 [Delphi 百度语音]支持获取 Access Token.语音识别.语 ...
- python函数知识六 内置函数二、匿名函数与内置函数三(重要)
19.内置函数二 abs():绝对值 lst = [1,2,-3,1,2,-5] print([abs(i) for i in lst]) enumerate("可迭代对象",&q ...
- Django模型层之更多操作
Django模型层之更多操作 一 .ORM字段 1.1 常用字段 AutoField int自增列,必须填入参数 primary_key=True.当model中如果没有自增列,则自动会创建一个列名为 ...
- Unity的学习笔记(UGUI文本逐个字输出)
之前在网上找过各种的逐个输出字,我可能理解能力不好,照着代码复制没有能使用成功,于是自己研究了很多网上说的思路,各种改良出了一个能用的,写完自己测试,觉得还真好用,于是记录下来 用法:将用代码组件挂上 ...
- 14-2 SQL语言简介
1.结构化查询语言(Structured Query Language,SQL),常被读作sequel,最初是由Microsoft.Sybase和Ashton-Tate这3家公司共同开发的. 2.Wi ...
- NetCore踩坑记1、 一块网卡引发的血案
公司的项目架构演进,我们也趁机尝试迁移到netcore,系列随笔讲记录我们的踩坑和填坑记录. HttpClient不行? 这是我们第一次尝试netcore 简要介绍环境 netcore2.2+aspn ...
- python day10: 反射补充,面向对象
目录 pythdon day 10 1. 反射补充 16. 面向对象 16.1 面向对象初步介绍 16.2 面向对象和面向过程区别 16.3 对象的进化 17. 类class 17.1 类的定义 17 ...