看看用TypeScript怎样实现常见的设计模式,顺便复习一下。

学模式最重要的不是记UML,而是知道什么模式可以解决什么样的问题,在做项目时碰到问题可以想到用哪个模式可以解决,UML忘了可以查,思想记住就好。

这里尽量用原创的,实际中能碰到的例子来说明模式的特点和用处。

解释器模式 Interpreter

特点:使用给定语法来解释一段内容。

用处:管理类系统经常会定义一些搜索语句格式来使用户方便搜索库里的内容,这时就可以考虑用解释器来翻译执行这些语句。

注意:适合相对简单的语法。

解释器模式通过把一段表达式拆开成很多个,分为不同的解析类,一个一个的去解析并执行,这过程中经常会用Context来保存解析过程的信息。

这种解释器的优点在于各种表达式的解析相对独立,要加入新的规则也不会影响现有的解析。缺点也很明显,一个表达式一个类,复杂语法或复合语法的话表达式数量就非常多,并且表达式之间也很难真正独立。

下面用TypeScript写一个简单正则表达式的解释器:

要解释的表达式有:{}, [], \d, ^, $这几种。

先建立一个Expression接口,所有解释器都实现这个接口:

interface Expression{
interpret(context: Context);
}

可以看到接口里用到了一个Context,这个用来保存解析时的一些数据和进度,包含:

pattern: 整个表达式

currentPatternIndex: 当前正在验证的表达式的位置

lastExpression: 上一个表达式,用于{}解析

text: 需要验证的文本

currentTextIndex: 当前验证到text里的哪个字符的位置

isMatch: 是否匹配成功

class Context{
constructor(public pattern: string, public text: string){ } currentTextIndex: number = 0;
get currentText(): string{
return this.text[this.currentTextIndex];
} currentPatternIndex: number = 0;
lastExpression: string;
get currentPatternExpression(): string{
return this.pattern[this.currentPatternIndex];
} isMatch: boolean;
}

现在分别给那些符号写解析类:

// 开始符号:^
class BeginExpression implements Expression{
interpret(context: Context){
context.isMatch = context.currentPatternIndex === 0;
context.currentPatternIndex++;
}
} // 结束符号:$
class EndExpression implements Expression{
interpret(context: Context){
context.isMatch = context.currentTextIndex === context.text.length;
context.currentPatternIndex++;
}
} // 反斜杠:\d,只支持\d
class BslashExpression implements Expression{
interpret(context: Context){
if(context.pattern[context.currentPatternIndex + 1] !== 'd'){
throw new Error('only support \\d');
} let target = context.currentText;
context.lastExpression = '\\d';
context.isMatch = Number.isInteger(Number.parseInt(target));
context.currentPatternIndex+=2;
context.currentTextIndex++;
}
} // 中括号:[]
class BracketExpression implements Expression{
interpret(context: Context){
let prevIndex = context.currentPatternIndex;
while(context.pattern[++context.currentPatternIndex] !== ']'){
if(context.currentPatternIndex+1 === context.pattern.length){
throw new Error(`miss symbol ]`);
}
}
let expression = context.pattern.substr(prevIndex+1, context.currentPatternIndex - prevIndex - 1);
let target = context.currentText; context.lastExpression = `[${expression}]`;
context.isMatch = [...expression].indexOf(target) > -1;
context.currentPatternIndex++;
context.currentTextIndex++;
}
} // 大括号:{}
class BraceExpression implements Expression{
interpret(context: Context){
let endIndex = context.currentPatternIndex;
while(context.pattern[++endIndex] !== '}'){
if(i+1 === context.pattern.length){
throw new Error(`miss symbol }`);
}
}
let expression = context.pattern.substr(context.currentPatternIndex + 1, endIndex - context.currentPatternIndex - 1);
let num = Number.parseInt(expression);
if(!Number.isInteger(num)){
throw new Error('{} only support number');
}
let newExpression = '';
for(let i=1;i<num;i++){
newExpression += context.lastExpression;
}
context.pattern = context.pattern.substr(0, context.currentPatternIndex) +
newExpression +
context.pattern.substr(endIndex+1);
}
} // 普通字符
class StringExpression implements Expression{
interpret(context: Context){
context.lastExpression = context.currentPatternExpression;
context.isMatch = context.currentPatternExpression === context.currentText;
context.currentPatternIndex++;
context.currentTextIndex++;
}
}

有了这些解释器,现在解析表达式就很轻松了:

class Regex{
mapping: {[key:string]: Expression} = {
'^': new BeginExpression(),
'$': new EndExpression(),
'{': new BraceExpression(),
'[': new BracketExpression(),
'\\':new BslashExpression(),
}; // 这是一个表达式-解释器的映射表
stringExp: Expression = new StringExpression(); constructor(private pattern: string){ } IsMatch(text: string): boolean{
let context = new Context(this.pattern, text); for(context.currentPatternIndex=0;context.currentPatternIndex<context.pattern.length;){
let symbol = this.mapping[context.currentPatternExpression];
symbol ? symbol.interpret(context) : this.stringExp.interpret(context); //通过找到对应的解释器来解释匹配文本
if(!context.isMatch){
break;
}
}
return context.isMatch;
}
}

写个手机号码验证的正则表达式测试一下:

let pattern = '/^1[34578]\d{9}$/';
let regex = new Regex(pattern); let text = '13712345678';
console.log(`match ${text}: ${regex.IsMatch(text)}`); // 正常手机号:成功 text = '1371234567p';
console.log(`match ${text}: ${regex.IsMatch(text)}`); // 手机号里有字母:失败 text = '137123456789';
console.log(`match ${text}: ${regex.IsMatch(text)}`); // 多了一位:失败 text = '1371234567';
console.log(`match ${text}: ${regex.IsMatch(text)}`); // 少了一位:失败

结果符合预期,可以看到用解释器把表达分开解释的好处很明显,各个解释器互不干扰,主体部分调用这些解释器分别进行解释就可以了,非常方便。

当然这也只是处理简单的语法,如果语法很复杂就需要考虑引入分析引擎或编译器了。

TypeScript设计模式之解释器的更多相关文章

  1. 乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern)

    原文:乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern) 作 ...

  2. 设计模式:解释器(Interpreter)模式

    设计模式:解释器(Interpreter)模式 一.前言 这是我们23个设计模式中最后一个设计模式了,大家或许也没想到吧,竟然是编译原理上的编译器,这样说可能不对,因为编译器分为几个部分组成呢,比如词 ...

  3. C#设计模式:解释器模式(Interpreter Pattern)

    一,C#设计模式:解释器模式(Interpreter Pattern) 1,解释器模式的应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的模式不断重复出现,并且容易 ...

  4. 北风设计模式课程---解释器模式(Interpreter Pattern)

    北风设计模式课程---解释器模式(Interpreter Pattern) 一.总结 一句话总结: 不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例 设计模式都是对生活的抽象,比如用 ...

  5. python设计模式之解释器模式

    python设计模式之解释器模式 对每个应用来说,至少有以下两种不同的用户分类. [ ] 基本用户:这类用户只希望能够凭直觉使用应用.他们不喜欢花太多时间配置或学习应用的内部.对他们来说,基本的用法就 ...

  6. 【GOF23设计模式】解释器模式 & 访问者模式

    来源:http://www.bjsxt.com/ 一.[GOF23设计模式]_解释器模式.访问者模式.数学表达式动态解析库式 1.解释器模式Interpreter  2.访问者模式Visitor 

  7. [设计模式] 15 解释器模式 Interpreter

    在GOF的<设计模式:可复用面向对象软件的基础>一书中对解释器模式是这样说的:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子.如果一种特定类 ...

  8. TypeScript设计模式之单例、建造者、原型

    看看用TypeScript怎样实现常见的设计模式,顺便复习一下. 单例模式 Singleton 特点:在程序的生命周期内只有一个全局的实例,并且不能再new出新的实例. 用处:在一些只需要一个对象存在 ...

  9. TypeScript设计模式之工厂

    看看用TypeScript怎样实现常见的设计模式,顺便复习一下. 学模式最重要的不是记UML,而是知道什么模式可以解决什么样的问题,在做项目时碰到问题可以想到用哪个模式可以解决,UML忘了可以查,思想 ...

随机推荐

  1. 如何用CSS快速布局(一)—— 布局元素详细

    要快速进行网页排版布局,则必须对布局的元素有清晰的了解,才不会总是在细节处出错.这一篇先详解有关布局的因素作为布局基础:块级元素and内联元素.盒模型.准确定位.元素对齐.样式继承.下一篇则重点描述快 ...

  2. [html5] 学习笔记-html5音频视频

    HTML5 最大的新特色之一就是支持音频和视频.在 HTML5 之前,我们必须使用插件如 Silverlight  或 Flash 来实现这些功能.在 HTML5 中,可以直接使用新标签< au ...

  3. [TPYBoard-Micropython之会python就能做硬件 4] 学习使用电位器和1602显示屏

    一.实验器材 1.TPYboard V102板  一块 2.电位器   一个 3.1602 屏 一块 4.杜邦线:若干 二.电位器的使用 电位器 (英文:Potentiometer)是可变电阻器的一种 ...

  4. Oracle DataGuard 升级 [11.2.0.1 -> 11.2.0.4]

    Oracle DataGuard 升级 [11.2.0.1 -> 11.2.0.4] Primary: 11.2.0.1 单机,Site A. Standby: 11.2.0.1 单机,Site ...

  5. 二叉搜索树Java实现(查找、插入、删除、遍历)

    由于最近想要阅读下 JDK1.8 中 HashMap 的具体实现,但是由于 HashMap 的实现中用到了红黑树,所以我觉得有必要先复习下红黑树的相关知识,所以写下这篇随笔备忘,有不对的地方请指出- ...

  6. UI进阶 即时通讯之XMPP好友列表、添加好友、获取会话内容、简单聊天

    这篇博客的代码是直接在上篇博客的基础上增加的,先给出部分代码,最后会给出能实现简单功能的完整代码. UI进阶 即时通讯之XMPP登录.注册 1.好友列表 初始化好友花名册 #pragma mark - ...

  7. javascript学习-对象与原型

    javascript学习-对象与原型 Javascript语言是符合面向对象思想的.一般来说,面向对象思想需要满足以下三个基本要求: 封装,Javascript的对象可以自由的扩充成员变量和方法,自然 ...

  8. Xcode插件失效解决办法

    升级完Xcode突然间发现之前安装的所有插件都不生效了,费了九牛二虎之力找到了解决办法...      1.打开终端,输入以下代码获取到DVTPlugInCompatibilityUUID       ...

  9. selenium框架与chrome浏览器的不兼容问题

    在一次偶然的情况下,在chrome上用selenium框架去抓取某个id为XX的页面元素,使用WebDriver的findElement().click()方法进行点击,原来在firefox浏览器运行 ...

  10. devexpress显示缓冲滚动条与实现类似QQ消息推送效果

    1.一般在项目中处理大数据,或者查询大量数据时,耗时会很长,这个时候缓冲条是必不可少的.这里展示一个devexpress不错的缓冲条,如图所示: 使用到了控件splashScreenManager,运 ...