【C#设计模式】解释器模式(Interpreter Pattern)【行为型】
- 特别说明:【源代码有问题,实际运行结果和期望结果不同!】
- 本人对代码进行了缩减,可作为理解的实例!
- 此模式只大致了解
前言
今天我们开始讲“行为型”设计模式的第十一个模式,也是面向对象设计模式的最后一个模式,先要说明一下,其实这个模式不是最后一个模式(按Gof的排序来讲),为什么把它放在最后呢?因为我们在业务系统中写一个解释器的机会并不是很多,实践比较少,理解和应用该模式就有些困难,所以就放在最后来说。该模式就是【解释器模式】,英文名称是:Interpreter Pattern。按老规矩,先从名称上来看看这个模式,个人的最初理解“解释器”和Google的中英翻译功能类似。如果有一天你去国外旅游去了,比如去美国吧,美国人是讲英语的,我们是讲汉语的,如果英语听不懂,讲不好,估计沟通就完蛋了,不能沟通,估计玩的就很难尽兴了,因为有很多景点的解说你可能不明白(没有中文翻译的情况下,一般情况会有的)。所以我们需要一个软件,可以把中英文互译,那彼此就可以更好的理解对方的意思,我感觉翻译软件也可以称得上是解释器,把你不懂的解释成你能理解的。我们写代码,需要编译器把我们写的代码编译成机器可以理解的机器语言,从这方面来讲,C#的编译器也是一种解释器。
一、模式的详细介绍
1.1 动机(Motivate)
在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。
1.2 意图(Intent)
给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。 ——《设计模式》GoF
1.3 结构图(Structure)
1.4 模式的组成
可以看出,在解释器模式的结构图有以下角色:
(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):指的是使用解释器的客户端,通常在这里将按照语言的语法做的表达式转换成使用解释器对象描述的抽象语法树,然后调用解释操作。
1.5 模式的具体实现
在很多场合都需要把数字转换成中文,我们就可以使用解释器来实现该功能,把给定的数字解释成符合语法规范的汉字表示法。实现代码如下:【源代码有问题,实际运行结果和期望结果不同!请看下一个实例代码】
using System;
using System.Collections;
using System.Collections.Generic;
namespace InterpreterPattern
{
// 抽象表达式
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; }
}
}
class Program
{
static void Main(string[] args)
{
string roman = "五亿七千三百零二万六千四百五十二"; //【01】运行结果有误:73006452
//分解:((五)亿)((七千)(三百)(零)(二)万)
//((六千)(四百)(五十)(二))
//string roman = "七千三百零二万六千四百五十二"; //【02】运行结果有误: 73006452
//string roman = "七千三百万六千四百五十二";//【03】运行结果有误: 70036452
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);
Console.Read();
}
}
}
缩减后可正确运行的代码【注意注释中“核心代码”的理解】:
using System;
using System.Collections;
using System.Collections.Generic;
namespace InterpreterPattern
{
// 抽象表达式
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()))//【重点理解】如果以 五(十)/六(百)结尾 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 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; }
}
}
class Program
{
static void Main(string[] args)
{
string roman = "六百五十二"; //【01】运行结果有误:652
Context context = new Context(roman);
ArrayList tree = new ArrayList();
tree.Add(new GeExpression());
tree.Add(new ShiExpression());
tree.Add(new BaiExpression());
foreach (Expression exp in tree)
{
exp.Interpreter(context);
}
Console.Write(context.Data);
Console.Read();
}
}
}
二、模式的实现要点
使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧方便地“扩展”文法。
Interpreter模式比较适合简单的文法表示,对于复杂的文法表示,Interpreter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具。
2.1 模式的优点
(1)易于改变和扩展文法。
(2)每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
(3)实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
(4)增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”
2.2 模式的缺点
(1)对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。
(2)执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
2.3 模式使用的场景
Interpreter模式的应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的模式不断重复出现,并且容易抽象为语法规则的问题”才适合使用Interpreter模式。
(1)当一个语言需要解释执行,并可以将该语言中的句子表示为一个抽象语法树的时候,可以考虑使用解释器模式(如XML文档解释、正则表达式等领域)。
(2)一些重复出现的问题可以用一种简单的语言来进行表达。
(3)一个语言的文法较为简单。
(4)当执行效率不是关键和主要关心的问题时可考虑解释器模式(注:高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。)
三、.NET 中模式的实现
-
正则表达式就是一个典型的解释器。
-
ASP.NET中,把aspx文件转化为dll时,会对html语言进行处理,这个处理过程也包含了解释器的模式在里面。
-
Interpreter模式其实有Composite模式的影子,但它们解决的问题是不一样的。
总结
今天就写到这里了,23种设计模式都写完了,有时间了,再写一篇文章,为这个系列做一个总结。
解释器模式可以和其他模式混合使用==,具体的使用方法和解决的问题我列出了一个表,大家好好了解一下,或许对大家有些帮助。列表如下: