本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8242238.html,记录一下学习过程以备后续查用。

一、引言

今天我们要讲行为型设计模式的第十一个模式--解释器模式,也是面向对象设计模式的最后一个模式。先要说明一下,其实这个模式不是最后一个模

式(按Gof的排序来讲),为什么把它放在最后呢?因为我们在业务系统中写一个解释器的机会并不是很多,实践比较少,理解和应用该模式就有些困

难,所以就放在最后来说,先从名称上来看这个模式。“解释器”和Google的中英翻译功能类似,假设有一天你去国外比如美国旅游,如果不会讲英语也

听不懂,沟通就是一个大障碍,估计也会玩得不尽兴了,因为有很多景点的解说你可能不明白(没有中文翻译的情况下,一般情况会有的)。所以我们

需要一个翻译软件(如科大讯飞),可以把中英文互译,那彼此就可以更好的理解对方的意思。个人觉得这个翻译软件也可以称得上是解释器,把你不

懂的解释成你能理解的。我们写代码,需要编译器把我们写的代码编译成机器可以理解的机器语言,从这方面来讲,C#的编译器也是一种解释器。

    二、解释器模式介绍

解释器模式:英文名称--Interpreter Pattern;分类--行为型。

2.1、动机(Motivate)

在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。在这种

情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。

2.2、意图(Intent)

给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。——《设计模式》GoF

2.3、结构图(Structure)

2.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):指的是使用解释器的客户端,通常在这里将按照语言的语法做的表达式转换成使用解释器对象描述的抽象语法树,然后调用解

释操作。

2.5、解释器模式的具体实现

在很多场合都需要把数字转换成中文,我们就可以使用解释器来实现该功能,把给定的数字解释成符合语法规范的汉字表示法,实现代码如下:

    class Program
{
/// <summary>
/// 环境上下文
/// </summary>
public sealed class Context
{
public string Statement { get; set; } public int Data { get; set; } public Context(string statement)
{
Statement = statement;
}
} /// <summary>
/// 抽象表达式
/// </summary>
public abstract class Expression
{
protected Dictionary<string, int> table = new Dictionary<string, int>(); protected Expression()
{
table.Add("一", );
table.Add("二", );
table.Add("三", );
table.Add("四", );
table.Add("五", );
table.Add("六", );
table.Add("七", );
table.Add("八", );
table.Add("九", );
} public virtual void Interpreter(Context context)
{
if (context.Statement.Length == )
{
return;
} foreach (string key in table.Keys)
{
int value = table[key]; if (context.Statement.EndsWith(key + GetPostFix()))
{
context.Data += value * Multiplier();
context.Statement = context.Statement.Substring(, context.Statement.Length - GetLength());
}
if (context.Statement.EndsWith("零"))
{
context.Statement = context.Statement.Substring(, context.Statement.Length - );
}
}
} public abstract string GetPostFix(); public abstract int Multiplier(); //十、百、千位数使用,所以用虚方法。
public virtual int GetLength()
{
return GetPostFix().Length + ;
}
} /// <summary>
/// 个位表达式
/// </summary>
public sealed class GeExpression : Expression
{
public override string GetPostFix()
{
return "";
} public override int Multiplier()
{
return ;
} public override int GetLength()
{
return ;
}
} /// <summary>
/// 十位表达式
/// </summary>
public sealed class ShiExpression : Expression
{
public override string GetPostFix()
{
return "十";
} public override int Multiplier()
{
return ;
}
} /// <summary>
/// 百位表达式
/// </summary>
public sealed class BaiExpression : Expression
{
public override string GetPostFix()
{
return "百";
} public override int Multiplier()
{
return ;
}
} /// <summary>
/// 千位表达式
/// </summary>
public sealed class QianExpression : Expression
{
public override string GetPostFix()
{
return "千";
} public override int Multiplier()
{
return ;
}
} /// <summary>
/// 万位表达式
/// </summary>
public sealed class WanExpression : Expression
{
public override string GetPostFix()
{
return "万";
} public override int Multiplier()
{
return ;
} public override int GetLength()
{
return ;
} public override void Interpreter(Context context)
{
if (context.Statement.Length == )
{
return;
} ArrayList tree = new ArrayList
{
new GeExpression(),
new ShiExpression(),
new BaiExpression(),
new QianExpression()
}; foreach (string key in table.Keys)
{
if (context.Statement.EndsWith(GetPostFix()))
{
int temp = context.Data;
context.Data = ; context.Statement = context.Statement.Substring(, context.Statement.Length - GetLength()); foreach (Expression exp in tree)
{
exp.Interpreter(context);
}
context.Data = temp + context.Data * Multiplier();
}
}
}
} /// <summary>
/// 亿位表达式
/// </summary>
public sealed class YiExpression : Expression
{
public override string GetPostFix()
{
return "亿";
} public override int Multiplier()
{
return ;
} public override int GetLength()
{
return ;
} public override void Interpreter(Context context)
{
ArrayList tree = new ArrayList
{
new GeExpression(),
new ShiExpression(),
new BaiExpression(),
new QianExpression()
}; foreach (string key in table.Keys)
{
if (context.Statement.EndsWith(GetPostFix()))
{
int temp = context.Data;
context.Data = ;
context.Statement = context.Statement.Substring(, context.Statement.Length - GetLength()); foreach (Expression exp in tree)
{
exp.Interpreter(context);
}
context.Data = temp + context.Data * Multiplier();
}
}
}
} static void Main(string[] args)
{
#region 解释器模式
//分解:((五)亿)((七千)(三百)(零)(二)万) ((六千)(四百)(五十)(二))
string roman = "五亿七千三百零二万六千四百五十二"; Context context = new Context(roman);
ArrayList tree = new ArrayList
{
new GeExpression(),
new ShiExpression(),
new BaiExpression(),
new QianExpression(),
new WanExpression(),
new YiExpression()
}; foreach (Expression exp in tree)
{
exp.Interpreter(context);
} Console.Write(context.Data); Console.Read();
#endregion
}
}

运行结果如下:

    三、解释器模式的实现要点

    使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧方便地“扩展”文法。

    Interpreter模式比较适合简单的文法表示,对于复杂的文法表示,Interpreter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准

工具。

    3.1、解释器模式的主要优点

    1)易于改变和扩展文法。

    2)每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。

    3)实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生

成节点类代码。

    4)增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码

无须修改,符合“开闭原则”。

 3.2、解释器模式的主要缺点

    1)对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致

系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。

    2)执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

    3.3、在下面的情况下可以考虑使用解释器模式

    Interpreter模式的应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化且类似的模式不断重复出现,同时容易抽象为语法规则的问题”

才适合使用Interpreter模式。

    1)当一个语言需要解释执行,并可以将该语言中的句子表示为一个抽象语法树的时候,可以考虑使用解释器模式(如XML文档解释、正则表达式等领

域)

    2)一些重复出现的问题可以用一种简单的语言来进行表达。

    3)一个语言的文法较为简单。

    4)当执行效率不是关键和主要关心的问题时可考虑解释器模式(注:高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换

成其他形式,使用解释器模式的执行效率并不高。)

    四、.NET中解释器模式的实现

    正则表达式就是一个典型的解释器。ASP.NET中,把aspx文件转化为dll时,会对html语言进行处理,这个处理过程也包含了解释器的模式在里面。

Interpreter模式其实有Composite模式的影子,但它们解决的问题是不一样的。

    五、总结

    至此,23种设计模式都写完了。解释器模式可以和其他模式混合使用,具体的使用方法和解决的问题我列出了一个表,大家好好了解一下,或许对大

家有些帮助,列表如下:

    (1)解释器和组合模式

这两种可以组合使用,一般非终结符解释器相当于组合模式中的组合对象,终结符解释器相当于叶子对象。

    (2)解释器模式和迭代器模式

由于解释器模式通常使用组合模式来实现,因此在遍历整个对象结构时,可以使用迭代器模式。

    (3)解释器模式和享元模式

在使用解释器模式的时候,可能会造成多个细粒度对象,如各式各样的终结符解释器,而这些终结符解释器对不同的表达式来说是一样的,是可以共用

的,因此可以引入享元模式来共享这些对象。

    (4)解释器模式和访问者模式

    在解释器模式中,语法规则和解释器对象是有对应关系的。语法规则的变动意味着功能的变化,自然会导致使用不同的解释器对象,而且一个语法规可

以被不同的解释器解释执行。因此在构建抽象语法树的时候,如果每个节点所对应的解释器对象是固定的,这意味着该节点对应的功能是固定的,那么就

不得不根据需要来构建不同的抽象语法树。为了让构建的抽象语法树较为通用,那就要求解释器的功能不要那么固定,要能很方便地改变解释器的功能,

这个时候就变成了如何能够很方便地更改树形结构中节点对象的功能了,访问者模式可以很好的实现这个功能。

C#设计模式学习笔记:(23)解释器模式的更多相关文章

  1. 设计模式学习笔记--备忘录(Mamento)模式

    写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...

  2. 设计模式学习笔记-Adapter模式

    Adapter模式,就是适配器模式,使两个原本没有关联的类结合一起使用. 平时我们会经常碰到这样的情况,有了两个现成的类,它们之间没有什么联系,但是我们现在既想用其中一个类的方法,同时也想用另外一个类 ...

  3. Java-马士兵设计模式学习笔记-装饰者模式

    Java装饰者模式简介 一.假设有一个Worker接口,它有一个doSomething方法,Plumber和Carpenter都实现了Worker接口,代码及关系如下: 1.Worker.java p ...

  4. 研磨设计模式学习笔记2--外观模式Facade

    需求:客户端需要按照需求,执行一个操作,操作包括一个系统中的3个模块(根据配置选择是否全部执行). 外观模式优点: 客户端无需知道系统内部实现,,只需要写好配置文件,控制那些模块执行,简单易用. 外观 ...

  5. 设计模式学习笔记 1.factory 模式

    Factory 模式 用户不关心工厂的具体类型,只知道这是一个工厂就行. 通过工厂的实现推迟到子类里面去来确定工厂的具体类型. 工厂的具体类型来确定生产的具体产品. 同时用户不关心这是一个什么样子的产 ...

  6. 设计模式学习笔记——Composite 组合模式

    用于描述无限层级的复杂对象,类似于描述资源管理器,抽象出每一个层级的共同特点(文件夹和文件,展开事件) 以前描述一个对象,是将整个对象的全部数据都描述清楚,而组合模式通过在对象中定义自己,描述自己的下 ...

  7. 设计模式学习笔记——Bridge 桥接模式

    先说一下我以前对桥接模式的理解:当每个类中都使用到了同样的属性或方法时,应该将他们单独抽象出来,变成这些类的属性和方法(避免重复造轮子),当时的感觉是和三层模型中的model有点单相似,也就是让mod ...

  8. 设计模式学习笔记——Visitor 访问者模式

    1.定义IVisitor接口,确定变化所涉及的方法 2.封装变化类.实现IVisitor接口 3.在实体类的变化方法中传入IVisitor接口,由接口确定使用哪一种变化来实现(封装变化) 4.在使用时 ...

  9. Java-马士兵设计模式学习笔记-责任链模式-FilterChain功能

    一.目标 增加filterchain功能 二.代码 1.Filter.java public interface Filter { public String doFilter(String str) ...

  10. Java-马士兵设计模式学习笔记-责任链模式-处理数据

    一.目标 数据提交前做各种处理 二.代码 1.MsgProcessor.java public class MsgProcessor { private List<Filter> filt ...

随机推荐

  1. 异数OS 星星之火(三)--异数OS-织梦师云 微服务编写入门

    . 异数OS 星星之火(三)–异数OS-织梦师云 微服务编写入门 本文来自异数OS社区 github: https://github.com/yds086/HereticOS 异数OS社区QQ群: 6 ...

  2. 【5min+】帮我排个队,谢谢。await Task.Yield()

    系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...

  3. JVM第一弹

    JVM第一弹 基本概念 JVM是可运行java代码的假想计算机,包括一套字节码指令集,一组寄存器,一个栈,一个垃圾回收.堆和一个存储方法域.JVM是运行在操作系统之上的,它与硬件没有直接的交互. 运行 ...

  4. Python3基础之内置模块

    模块和包 一.定义: 模块:用来从逻辑上组织Python代码(变量,函数,类,逻辑:实现一个功能),本质就是.py结尾的Python文件包:用来从逻辑上组织模块,本质就是一个目录(必须带有一个__in ...

  5. 基于spring cloud OAuth2的微服务授权验证服务搭建的一些坑, 包括401,client_secret,invalid_scope等问题

    一 先贴成功图,用的是springcloud Finchley.SR1版本,springboot版本2.0.6 问题一: 返回401, Unauthorized 出现这个问题原因很多:首先确保方法开启 ...

  6. Leetcode 题目整理 climbing stairs

    You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb ...

  7. k8s注册节点提示Docker SystemdCheck]: detected cgroupfs" as the Docker cgroup dr iver. The r ecommended dr fiver is" systemd"

    本篇就不长篇大论了,直接附上解决办法 提示如上,此提示非必要要求,只是建议,但是看着不爽,那就解决它 这是未解决之前的docker信息,现在输入下面的指令 sudo echo -e "{\n ...

  8. 使用vscode进行远程开发

    1.前置条件,安装SSH客户端.OpenSSH或者Git两者任选其一即可,本文使用的是Git 2.安装 Remote Development 扩展包 官方地址为:https://marketplace ...

  9. c和c++中读取数据的方式总结

    目录 c 输出 printf() 输入 scanf getchar(), putchar() gets(), puts() c++ 输入 cin() getline() get() 输出 cout 最 ...

  10. C语言学习笔记--关于指针的一些小认知

    int main() { int i; char *str = "hu mian yuan"; int length = strlen(str); printf("str ...