前置文章: 设计模式的原则 

其他设计模式:用心理解设计模式

设计模式相关代码已统一放至 我的 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);
        }
    }
}
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐