解释器模式
文章目录
前言
当我们用不同的编程语言完成代码开发后,代码是不会直接被计算机运行的,因为计算机不认识代码,这时候我们需要一个编译器,将代码编译成计算机可运行的代码,而编译后的代码对开发人员来说,没有可读性,编译器就是中间的桥梁,预编译型语言都会有自己的编译器,而编译器中也存在不同语言的定义及解释,通过将可读代码解释为计算机代码集,实现项目的编译,这就是编译器的工作,然而不同的语言拥有不同的风格,不同的语法,不同的解释器,确立了语言有各自的文法来解释不同语言的代码。
一、解释器模式
指定一门语言,定义它的文法的一种表示,并定义一个解释器,用该解释器来解释语言中的句子,是一种按照文法进行解析,来获取我们需要的指定的语法的方式。
二、应用场景
- 当我们不能直接将语法公开时,可以对语法中的应用部分进行包装公开,将包装后的语法转换为正式语法的过程叫解释,包装后的语法称之为文法,处理这一过程的上下文叫解释器。
- 一些重复出翔的问题可以用一种简单的语言进行表达。
- 一个简单语法需要解释的场景。
三、角色分析
- 抽象表达式(IExpression):定义一个解释方法,交由具体子类进行具体解释,声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
- 终结符表达式(TerminalExpression):实现与文法中的终结符相关联的解释操作。
- 非终结符表达式(NonterminalExpression):为文法中的非终结符实现解释操作,对文法中每一条规则R1、R2……Rn都需要一个具体的非终结符表达式类。
- 上下文环境类(Context):包含解释器之外的全局信息。一般用来存放文法中各个终结符所对应的具体值。
四、终结符表达式和非终结符表达式
五、使用步骤
1.定义抽象表达式
代码如下(示例):
/// <summary>
/// 抽象表达式
/// </summary>
public interface IExpression
{
public string interptret();
}
2.创建终结符表达式
代码如下(示例):终结符表达式:可以直接按照文法解释的符号。
/// <summary>
/// 距离表达式
/// </summary>
public class DistanceExpression : IExpression
{
private string distance;
public DistanceExpression(string distance)
{
this.distance = distance;
}
public string interptret()
{
try
{
Convert.ToInt32(this.distance);
return distance + "米";
}
catch (Exception error)
{
return "0米";
}
}
}
/// <summary>
/// 方向表达式
/// </summary>
public class DirectionExpression : IExpression
{
private string direction;
public DirectionExpression(string direction)
{
this.direction = direction;
}
public string interptret() => direction switch
{
"up" => "向上",
"down" => "向下",
"left" => "向左",
"right" => "向右",
_ => "_",
};
}
/// <summary>
/// 动作表达式
/// </summary>
public class ActionExpressioon : IExpression
{
private string action;
public ActionExpressioon(string action)
{
this.action = action;
}
public string interptret() => action switch
{
"walk" => "走",
"run" => "跑",
"jump" => "跳",
"climb" => "爬",
_ => "-"
};
}
3.定义非终结符表达式
代码如下(示例):非终结符表达式,需要通过关系表达式,关联终结符表达式,以下示例实际为终结符表达式,修改构造函数为IExpression,并在上下文中传入当前字符的前后连个字符,解析为表达式,这才是非终结符表达的定义。是关联前后元素的,而非直接返回元素。
/// <summary>
/// 并且关系节点
/// </summary>
public class andNodeExpression : IExpression
{
private string right;
public andNodeExpression(string right)
{
this.right = right;
}
public string interptret()
{
return "并且";
}
}
/// <summary>
/// 或者关系节点
/// </summary>
public class ORNodeExpression : IExpression
{
private string right;
public ORNodeExpression(string right)
{
this.right = right;
}
public string interptret()
{
return "或者";
}
}
4.上下文
代码如下(示例):当前案例从多个维度进行字符串解析,为多元解释器,一元解释器:从a-----1,b-----2这种单象限的解释器。
/// <summary>
/// 多元解释器上下文
/// </summary>
public class Context
{
public string resultStr;
private string[] wordsText;
public Context(string TextFun)
{
this.wordsText = TextFun.Split(' ');
Console.WriteLine($"当前wordsTexe存在{wordsText.Length}个对象数组");
foreach (var item in wordsText)
{
Console.Write($" {item} ");
}
Console.WriteLine();
}
public void Handle()
{
for (int i = 0, j = 0; i < wordsText.Length; i++)
{
var iex = FindIexpression(wordsText[i]);
resultStr += iex.interptret();
}
}
/// <summary>
/// 内容解析器
/// </summary>
/// <param name=""></param>
/// <returns></returns>
private IExpression FindIexpression(string textBody)
{
switch (textBody)
{
//动作解析器
case "run":
goto case "climb";
case "walk":
goto case "climb";
case "jump":
goto case "climb";
case "climb":
return new ActionExpressioon(textBody);
//方向解析器
case "up":
goto case "right";
case "down":
goto case "right";
case "left":
goto case "right";
case "right":
return new DirectionExpression(textBody);
case "and":
goto andNodeExpression;
case "or":
goto ORNodeExpression;
default:
goto canConvertToInt32;
}
//距离解析器对象
canConvertToInt32:
return new DistanceExpression(textBody);
//关系解析器对象
ORNodeExpression:
return new ORNodeExpression(textBody);
andNodeExpression:
return new andNodeExpression(textBody);
}
}
5.调用客户端
代码如下(示例):
/// <summary>
/// 调用客户端
/// </summary>
public class Clienter
{
public void mainFun()
{
string expressionText = "run up 10 and walk down 20 and run left 30 and jump right 40 and climb left 10";
var interpreter = new Context(expressionText);
interpreter.Handle();
Console.WriteLine(interpreter.resultStr);
}
}
6.输出
代码如下(示例):
run up 10 and walk down 20 and run left 30 and jump right 40 and climb left 10
跑向上10米并且走向下20米并且跑向左30米并且跳向右40米并且爬向左10米
总结
解释器模式的本质是通过一定的方式,将代码按照文法解析获得相应的指定语法的方式,无所谓是终结符表达式还是非终结符表达式,更甚者由人会将终结符表达式作为抽象方法,使非终结符表达式对其进行重写,虽然不理解为什么会这样做,但是本着存在就是合理的理念,也就欣然接受了,毕竟名字都是开发的人定义的,思想都是终结出来的,甚至可以通过解释器的文法维度将解释器划分为一元,二元,多元无所谓,识别度高就行。
优点:
- 语法是由很多类表示的,当语法规则修改时,只需要修改相应的非终结符表达式即可,当扩展语法时,则需要添加相应的非终结符表达式
- 增加了新的解释表达式的方式
- 解释器模式对用的文法应当是比较简单且易于实现的,过于复杂的语法不适合使用解释器模式。
缺点:
- 解释器的每个语法都要产生一个非终结符表达式,当语法规则比较复杂时,就会产生大量解释类,引起类膨胀,增加系统维护的维度。
- 计时器模式采用递归调用方法,每个非终结符表达式都只关心与自己有关的表达式,每个表达式都需要知道最终的结果,因此完整表达式的最终结果是通过从后往前递归调用的方式获取的,当完整表达式层级较深时,解释效率下降,且出错时调试困难,因为递归迭代的层级太深。