步步为营 .NET 设计模式学习笔记 二十三、Interpreter(解释器模式)
概述
在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。
意图
给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。
结构图
角色说明:
AbstractExpression:
- 声明一个抽象的Interpret方法,抽象语法树中所有的节点都必须实现该抽象方法。
TerminalExpression:
- 实现和语法中末端符号相关的Interpret方法。
- 在每个句子的末端符号中均需要一个TerminalExpression实例。
NonterminalExpression:
另外一个实现了AbstractExpression 接口的类,用来处理语法树中非末端节点的语法。它含有下一个AbstractExpression(s)的引用,调用它每个子节点的Interpret方法。
Context:
Interpreter方法所需要的信息的容器,该信息对Interpreter而言全局可见。充当几个AbstractExpresssion 实例之间的通讯频道。
PatternClient:
构建或者接收一个抽象语法书的实例。对于一个特定的句子而言,语法树往往由若干个TerminalExpressions 和 NonterminalExpression组成。PatterClient在合适的context下,调用Interpret方法。
生活中的例子
在日常生活中英汉辞典的作用就是把中文翻译成英文或者是把英文翻译成中文,它的实现原理就是词典库里先把对应的中文和英文存在数据库表里,然后根据你输入的内容来匹配出相应的结果出来.
示例用例图
在公司里,借款单申请需要高管来审批,而高管又不会上班时间都坐在电脑面前,公司就有个系统,员工的借款申请都会通过手机短信方式来提示高管,高管可以通过回复短信来审批单据,高管一般都很懒,不愿回复太多的字,只能回复Y或y来表示同意,N或n表示拒绝,用四位标识码代替借款申请单号,用例图如下:
代码设计
先创建ReplyContent.cs:
/// <summary> /// 回复内容 /// </summary> public class ReplyContent { private string _ReplyText; public string ReplyText { get { return _ReplyText; } set { _ReplyText = value; } } }
再创建InterPreter.cs:
public abstract class InterPreter { public string ConvertContent(ReplyContent content) { if (content.ReplyText.Length == 0) return "请按规则回复审批短信."; return Excute(content.ReplyText); } public abstract string Excute(string key); }
再创建Approve.cs:
public class Approve : InterPreter { public override string Excute(string key) { if (key == "Y" || key == "y") { return "同意"; } else if (key == "N" || key == "n") { return "拒绝"; } else { return "回复内容有误,请重新回复."; } } }
再创建DocumentNum.cs:
public class DocumentNum : InterPreter { public Dictionary<string, string> OddNum { get { Dictionary<string, string> OddID = new Dictionary<string, string>(); OddID.Add("0001", "123890890892345"); OddID.Add("0002", "123456717012345"); OddID.Add("0003", "123456669012345"); OddID.Add("0004", "123423444012345"); OddID.Add("0005", "123467845345345"); OddID.Add("0006", "123231234564345"); OddID.Add("0007", "128797897867745"); return OddID; } } public override string Excute(string key) { string value = null; if (OddNum.TryGetValue(key, out value)) { return value; } else { return "没找到对应的单号."; } } }
再创建ReplyClient.cs:
public class ReplyClient { public static string ApplyContent(string ReplayValue) { ReplyContent content = new ReplyContent(); string approvevalue = ReplayValue.Substring(0, 1); string OddIDvalue = ReplayValue.Substring(1, 4); string result = string.Empty; InterPreter expression = new Approve(); content.ReplyText = approvevalue; result = string.Format("你{0}", expression.ConvertContent(content)); expression = new DocumentNum(); content.ReplyText = OddIDvalue; result += string.Format("单号是{0}的申请.\n", expression.ConvertContent(content)); return result; } }
最后再调用:
public partial class Run : Form { public Run() { InitializeComponent(); } private void btnRun_Click(object sender, EventArgs e) { //------------------------------------- rtbResult.AppendText(ReplyClient.ApplyContent("Y0001")); rtbResult.AppendText(ReplyClient.ApplyContent("y0002")); rtbResult.AppendText(ReplyClient.ApplyContent("N0003")); rtbResult.AppendText(ReplyClient.ApplyContent("n0004")); } }
结果如下图:
实现要点
1.Interpreter模式的应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的模式不断重复出现,并且容易抽象为语法规则
的问题”才适合使用Interpreter模式。
2.使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧来方便地“扩展”文法。
3.Interpreter模式比较适合简单的文法表示,对于复杂的文法表示,Interperter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具。
适用性
1.Interpreter模式的应用场合是interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的模式不断重复出现,并且容易抽象为语法规则的问题”才适合使用Interpreter模式。
总结
效率不是一个Interpreter关心的关键问题。最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。例如:正则表达式通常被转换成状态机。但即使在这种情况下,如果效率不是一个关键问题,转换器仍可用Interpreter模式实现,该模式仍是有用的。