用心理解设计模式——解释器模式 (Interpreter Pattern)
前置文章:设计模式的原则其他设计模式:用心理解设计模式设计模式相关代码已统一放至我的 Github一、定义行为型模式之一。Given a language, define a representation for its grammar along with an interpreter that uses the representation to int...
前置文章: 设计模式的原则
其他设计模式:用心理解设计模式
设计模式相关代码已统一放至 我的 Github
一、定义
行为型模式之一。
Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
(给定一种语言,为其语法定义一种表示,和使用该表示解释该语言中的句子的解释器。)
二、结构解析
解释器模式的一般结构有四种角色: 环境/上下文、抽象表达式、终结表达式、非终结表达式。
环境/上下文(Context):定义一个环境,各表达式在解释时,可以利用这个环境对某些语义进行查找解释。通常它是一个(或包含一个)支持查找的数据库 或 表格 或 字典 或 哈希表。
抽象表达式(Expression):定义一个对表达式进行解释的解释接口。
终结表达式(TerminalExpression):简单、独立的表达式,可以利用 “环境/上下文” 对自身语义给出直接解释的表达式。
非终结表达式(NonterminalExpression):复杂、聚合的表达式,无法对自身语义进行全部解释,只能解释出一部分语义,其余部分通过其他子表达式进行解释。子表达式可能是 “终结表达式”,也可能是 “非终结表达式”。子表达式可能是与自身类型相同的非终结表达式,构成递归式的解释。
三、评价
解释器模式,是用于对 特定语法语句 进行解释的设计模式。语法约定俗成,语句都由表达式构成。
表达式分为终结表达式和非终结表达式。其中,“终结” 的含义就是:到此结束,能利用当前环境或自然语义给出直接的、全部的解释,不需继续细分(不依赖其他表达式)。
“非终结表达式” 聚合/依赖了其他表达式(重点!请参考解释器模式类图),因此,对它进行解释时,主要的工作就是对这种聚合关系进行解释。
解释复杂语句的过程是自顶而下的, “非终结表达式” 会被逐渐分解,直到所有表达式都变成 “终结表达式” (可直接解释)或自然语义(可直接解释),此时就可完成对语句的全部解释。
解释器模式为语法解释提供了明确灵活的模板,也是很常见,比如: Json解释器、XML解释器、Plist解释器。总之,只要是一种约定俗成的语法语句,基本都可以套用解释器模式进行解释。
四、实现
本来打算不用实例表述任何设计模式(避免不准确举例的误导,事实上网上确有很多跑偏的举例),但解释器模式确实比较抽象,不用实例真的难以理解清楚。
这里用游戏开发中常见的 奖励字符串 (以分号分割奖励,以逗号分割奖励类型和数量。如,"1,123;3,1;4,999;2,1")进行举例。
结果预览:
using System.Collections.Generic;
using UnityEngine;
namespace Interpreter
{
// 环境/上下文。存储可直接进行翻译的内容
public class Context
{
private Dictionary<int, string> typeDict;
public Context() {
typeDict = new Dictionary<int, string>();
typeDict.Add(0, "钻石");
typeDict.Add(1, "金币");
typeDict.Add(2, "月卡");
typeDict.Add(3, "体验VIP");
typeDict.Add(4, "优惠券");
typeDict.Add(5, "门票");
}
public string GetRewardTypeName(int type)
{
return typeDict[type];
}
}
//抽象表达式,提供通用的解释接口
public abstract class Expression
{
public abstract string Interpret(Context context);
}
//“奖励列表字符串”表达式。(非终结表达式)
public class RewardsStrExp : Expression
{
string rewardsStr;
public RewardsStrExp(string rewardsStr)
{
this.rewardsStr = rewardsStr.Trim();
}
public override string Interpret(Context context)
{
string result = "奖励列表: {\n";
string[] rewardStrs = this.rewardsStr.Split(';');
foreach (var rewardStr in rewardStrs)
{
Debug.Log("rewardStr" + rewardStr);
//非终结表达式聚合依赖了另外的表达式(可能是多个、可能递归依赖自身)
result += new RewardStrExp(rewardStr).Interpret(context) + ";\n";
}
result += "}";
return result;
}
}
//“单个奖励字符串”表达式(非终结表达式)
public class RewardStrExp : Expression
{
string rewardStr;
public RewardStrExp(string rewardStr)
{
this.rewardStr = rewardStr.Trim();
}
public override string Interpret(Context context)
{
string[] typeAndNum = this.rewardStr.Split(',');
int type = int.Parse(typeAndNum[0]);
int num = int.Parse(typeAndNum[1]);
//非终结表达式无法直接解释,因此聚合依赖了另外的表达式进行解释。
//非终结表达式,也不完全依赖其他表达式进行解释,比如此处的“Num”就可直接利用自然语义给出解释。
return " 名称: " + new RewardTypeExp(type).Interpret(context) + ", 数量: " + num;
}
}
//“奖励类型”表达式(终结表达式)
public class RewardTypeExp : Expression
{
int type;
public RewardTypeExp(int type)
{
this.type = type;
}
public override string Interpret(Context context)
{
//重点: 终结表达式直接利用 context 或 自然语义给出直接解释。
return context.GetRewardTypeName(this.type);
}
}
//客户
public class Client
{
static public void Main()
{
Context context = new Context();
//解释“奖励字符串”表达式
string rewardsStr = "1,123;3,1;4,999;2,1";
string result = new RewardsStrExp(rewardsStr).Interpret(context);
Debug.Log(result);
}
}
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)