什么是解释器模式
解释器模式(Interpreter Pattern)是一种行为型设计模式,给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例,其核心原理是将一个语言表达式表示为一个抽象语法树,然后定义解释器来遍历这棵语法树并执行相应的操作。
是的,没有看错,这就是解释器模式的标准定义,是不是和我一样,看完后完全处于懵逼状态,这里语言是什么?文法又是什么?语法树是什么?解释器是怎么解释句子的?...讲真的,解释器模式确实不太好理解。
复杂的事情,慢慢来理解,先来弄懂标题解释器是什么意思?
解释器(Interpreter)是一种电脑程序,能够把高级编程语言一行一行直接转译运行。实际它是一种翻译程序,执行方式是一边翻译一边执行。什么意思呢?再具体来说,比如JavaScript、Python就属于解释型语言,如下面的python代码,打印输出"hello world",在计算机里是怎么运行呢?首先,计算机能够理解并执行的指令是二进制的,这些高级字符肯定是不直接执行的,因此在执行前,这句代码会通过python环境中的解释器解释成计算机可以理解的二进制指令,如果语法有问题,解释的过程就会出现异常,程序执行会中断。而Java是属于编译型的,什么意思呢?就是先把.java文件编译成.class文件然后再加载到虚拟机执行。
print("Hello, world!")
找到标题的出处后,后面的定义内容就好理解多了。
-
语言:python就是一门编程语言,汉语就是一门中国人之间直接交流的语言;
-
文法:就是语法,python有自己语法,遵循语法规范,程序才能正确解释并执行。汉语也有,一般就是主谓宾那一套;
-
句子:对于python来说,print("Hello, world!"),就一句话。对于汉语来说,"我爱你,中国!"也是一个句子;
-
语法树:语法树是句子结构的一种树型表示,比如:我爱你,中国!这句话照汉语的语法表示为一个树型结果就是:
解释器模式的核心原理
解释器模式是一种行为型设计模式,其核心原理是定义一个语言表达式的文法,并且定义该文法的解释器,用于解释语言中的表达式,将其转换为程序可执行的语句。简单来说,它可以让我们通过编写代码来模拟自然语言中的表达方式,并且能够将其转换为程序可以执行的指令。
解释器模式的核心角色主要包括以下四个:
- 抽象表达式(IExpression):这是解释器模式中的核心接口,负责定义解释方法interpret,交由具体子类进行具体解释。
java
/**
* 抽象表达工
*/
public abstract class AbstractExpression {
public abstract boolean interpret(String info);
}
- 终结符表达式(TerminalExpression):这是抽象表达式的子类,实现了与文法中终结符相关的解释操作。通常一个解释器中只有一个终结符表达式,但有多个实例,对应不同的终结符。
java
/**
* 终结符表达式
*/
public class TerminalExpression extends AbstractExpression{
private String data;
public TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String info) {
return info.contains(data);
}
}
- 非终结符表达式(NonterminalExpression):这也是抽象表达式的子类,实现了文法中与非终结符相关的解释操作。
java
/**
* 非终结符表达式
*/
public class NonterminalExpression extends AbstractExpression {
private AbstractExpression expr1;
private AbstractExpression expr2;
public NonterminalExpression(AbstractExpression expr1, AbstractExpression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String info) {
return expr1.interpret(info) || expr2.interpret(info);
}
}
- 上下文环境类(Context):这个类主要用来存放解释器之外的全局信息,一般用来存放文法中各个终结符所对应的具体值。
java
/**
* 上下文环境类
*/
public class Context {
private AbstractExpression expression;
public Context(AbstractExpression expression) {
this.expression = expression;
}
public void interpret(String info){
System.out.println(info);
boolean interpret = this.expression.interpret(info);
if (interpret) {
System.out.println("结束");
}
}
}
java
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
AbstractExpression expre1=new TerminalExpression("\n");
AbstractExpression expre2=new TerminalExpression("\r");
AbstractExpression expre=new NonterminalExpression(expre1,expre2);
Context context=new Context(expre);
context.interpret("hello ,world!\n");
}
}
解释器模式如何实现
需求描述
这篇文章是设计模式系列的最后一篇,之所以放在最后一篇是因为我觉得理解起来比较困难和抽象。当然,如果你能够跟上我的思路,到这里也许已经明白其中的一二,再举一个实际的例子,来模拟解释器模式的实现,看完之后,也许就全明白了。
现在直播带货是真的火,有的主播,随随便便一场直播,就够普通打工人干一辈子的了,也正因此,无数人开始蜂拥而入,更有很多人为了圈钱,更是没有底线的蹭热点、制造热点。人多了,那么问题就出现了:在直播间里,有的人比较喜欢这个主播,,则是"我爱你xxx"、"xxx,我支持你"之类的各种弹幕满屏飞,有的人不喜欢这个主播,则是各种诋毁和谩骂,那么如果需要对所有要输出打印到公屏上的弹幕做一个敏感词的过滤,应该怎么做呢?如果使用解释器模式,则可以这样实现:
实现方法
1、AbstractExpression.java:声明一个抽象表达式(IExpression):在抽象类里,定义解释方法interpret,由具体敏感词表达式、非敏感词表达工子类进行具体实现。
java
/**
* 抽象表达式
*/
public abstract class AbstractExpression {
public abstract boolean interpret(String info);
}
2、SensitiveExpression.java、NonsensitiveExpresson.java:声明具体的敏感词表达式类、非敏感词表达式类,实现具体的解释逻辑;
java
/**
* 敏感词表达式
*/
public class SensitiveExpression extends AbstractExpression {
private String data;
public SensitiveExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String info) {
return info.contains(data);
}
}
java
/**
* 非敏感词表达式
*/
public class NonsensitiveExpression extends AbstractExpression {
private AbstractExpression expr1;
private AbstractExpression expr2;
public NonsensitiveExpression(AbstractExpression expr1, AbstractExpression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String info) {
return expr1.interpret(info) || expr2.interpret(info);
}
}
3、Context.java:上下文环境类(Context),持有具体的表达式实现。
java
/**
* 上下文环境类
*/
public class Context {
private AbstractExpression expression;
public Context(AbstractExpression expression) {
this.expression = expression;
}
public void print(String info) {
boolean interpret = this.expression.interpret(info);
if (interpret) {
System.out.println("忽略敏感词:*****");
} else {
System.out.println("XXXX说:"+info);
}
}
}
4、编写业务客户端进行测试验证
java
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
AbstractExpression expre1=new SensitiveExpression("滚");
AbstractExpression expre2=new SensitiveExpression("垃圾");
AbstractExpression expre=new NonsensitiveExpression(expre1,expre2);
Context context=new Context(expre);
context.print("支持主播");
context.print("主播垃圾");
context.print("主播真漂亮");
context.print("主播滚蛋");
}
}
解释器模式适用哪些场景
解释器模式的应用场景包括但不限于以下几种情况:
- 表达式求值:例如计算器应用、公式计算、数据分析等,解释器可以将表达式解析成计算机可以执行的指令。
- 自然语言处理:例如语言翻译、语音识别、虚拟助手等,解释器可以将人类语言翻译成计算机可以识别的语言。
- 日志处理:使用脚本语言或编程语言处理日志时,有很多服务会产生大量的日志,需要对日志进行解析,生成报表。各个服务的日志格式不同,数据中的要素相同,这种情况下,通过程序解决上述问题,主要的解决方案就是使用解释器模式。
解释器模式的优点和缺点
优点
- 易于改变和扩展文法:由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
- 每一条文法规则都可以表示为一个类:因此可以方便地实现一个简单的语言。
- 增加新的解释表达式较为方便:如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合"开闭原则"。
缺点
- 对于复杂文法难以维护:在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护。
- 执行效率较低:由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
总结
总之,在表达式求值、自然语言处理和日志处理等业务场景中,解释器模式可以将人类语言或非编程语言转换为计算机可执行的语言,从而实现对数据的解析、处理和计算等功能,是一种行之有效的设计模式,但是其本身复杂性和核心原理需要特别注意。