Unity【话大】设计模式之解释器模式
前言:笔者在最开始写程序的时候经常会遇到一种情况,例如更改一个字段、或者添加一种小功能,就要把原来写过的东西几乎废弃掉,或者更改大量以前写过的代码。又或者自己写的东西时间久了再去回顾,完全找不到到时为什么这么写的头绪,如果遇到了Bug更是无法快速定位在哪里小范围出现的问题。如果你也经常遇到这种问题,就说明你现阶段非常需要学习下设计模式了。
在网上经常说的设计模式有23种,也有一些更多的设计模式,无非也是从这些设计模式中变种而来。如果让笔者来形容什么是设计模式,我认为设计模式是:一种思想,一种模式,一种套路,一种解决问题的高效策略。
有说的不正确或者不准确的地方欢迎留言指正
有什么有趣的写作技巧或者想法欢迎大家给我留言,大家的帮助是我写下去最有效的动力
今天笔者给大家介绍的事解释器模式,在说解释器模式之前,先介绍几个概念
- 文法:我们要描述一种语言时,需要给出这种语言的所有句子,当句子的数目是有限可数时,就要都列出来;当句子是一个无穷集,也就是无限不可数时,就要给出可以表示它们的结构的描述方法或者说,句子的组成规则。这种规则就是文法。
-
抽象表达式(AbstractExpression):定义解释器的接口,约定解释器的解释操作。其中的Interpret接口,正如其名字那样,它是专门用来解释该解释器所要实现的功能。
-
终结符表达式(Terminal Expression):实现了抽象表达式角色所要求的接口,主要是一个interpret()方法;文法中的每一个终结符都有一个具体终结表达式与之相对应。比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
-
非终结符表达式(Nonterminal Expression):文法中的每一条规则都需要一个具体的非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,“+”就是非终结符,解析“+”的解释器就是一个非终结符表达式。
-
环境角色(Context):这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。
-
客户端(Client):指的是使用解释器的客户端,通常在这里将按照语言的语法做的表达式转换成使用解释器对象描述的抽象语法树,然后调用解释操作。
解释器模式(interpreter) 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
示例代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Custom.Log;
using System.Linq;
namespace InterpreterPattern
{
public class Context
{
private string text;
public string Input
{
set { text= value; }
}
public string Output
{
get { return text; }
}
}
public abstract class AbstractExpression
{
public abstract void Interpret(Context context);
}
public class NumberToChineseExpression : AbstractExpression
{
private static Dictionary<string, string> _Dictionary = new Dictionary<string, string>();
static NumberToChineseExpression()
{
_Dictionary.Add("0", "零");
_Dictionary.Add("1", "一");
_Dictionary.Add("2", "二");
_Dictionary.Add("3", "三");
_Dictionary.Add("4", "四");
_Dictionary.Add("5", "五");
_Dictionary.Add("6", "六");
_Dictionary.Add("7", "七");
_Dictionary.Add("8", "八");
_Dictionary.Add("9", "九");
}
public override void Interpret(Context context)
{
string text = context.Output;
if (string.IsNullOrEmpty(text))
return;
List<string> numberList = new List<string>();
foreach (var item in text)
{
if (_Dictionary.ContainsKey(item.ToString()))
{
numberList.Add(_Dictionary[item.ToString()]);
}
else
{
numberList.Add(item.ToString());
}
}
this.Log($"转换后:{string.Concat(numberList)}");
context.Input=string.Concat(numberList);
}
}
public class ChineseToEnglishExpression : AbstractExpression
{
private static Dictionary<string, string> _Dictionary = new Dictionary<string, string>();
static ChineseToEnglishExpression()
{
_Dictionary.Add( "零","Zero");
_Dictionary.Add( "一","One");
_Dictionary.Add( "二","Two");
_Dictionary.Add( "三","Three");
_Dictionary.Add( "四","Four");
_Dictionary.Add( "五","Five");
_Dictionary.Add( "六","Six");
_Dictionary.Add( "七","Seven");
_Dictionary.Add( "八", "Eight");
_Dictionary.Add( "九","Nine");
}
public override void Interpret(Context context)
{
string text = context.Output;
if (string.IsNullOrEmpty(text))
return;
List<string> numberList = new List<string>();
foreach (var item in text)
{
if (_Dictionary.ContainsKey(item.ToString()))
{
numberList.Add(_Dictionary[item.ToString()]);
}
else
{
numberList.Add(item.ToString());
}
}
this.Log($"转换后:{string.Concat(numberList)}");
context.Input = string.Concat(numberList);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Custom.Log;
namespace InterpreterPattern
{
public class InterpreterComponent : MonoBehaviour
{
void Start()
{
Context context = new Context();
context.Input = "123456789";
IList<AbstractExpression> list = new List<AbstractExpression>();
list.Add(new NumberToChineseExpression());
list.Add(new ChineseToEnglishExpression());
foreach (AbstractExpression exp in list)
{
exp.Interpret(context);
}
this.Log($"最终输出:{context.Output}");
}
}
}
优点:
- 可扩展性比较好,灵活。
- 增加了新的解释表达式的方式。
- 易于实现简单文法。
缺点:
- 可利用场景比较少。
- 对于复杂的文法比较难维护。
- 解释器模式会引起类膨胀。
- 解释器模式采用递归调用方法。