设计模式-行为型-解释器模式

解释器模式是一种设计模式,用于将特定语言的语法结构转化为可执行代码。它涉及抽象表达式、终结符表达式和非终结符表达式等角色,常用于处理简单语法的解释任务。该模式允许在运行时构建和修改解析表达式,适用于解决重复出现的简单语言表达。文章通过示例代码展示了如何创建和使用解释器模式,包括上下文环境的构建和解释步骤,同时也指出了其在语法复杂时可能导致类膨胀和效率降低的缺点。
摘要由CSDN通过智能技术生成

解释器模式



前言

当我们用不同的编程语言完成代码开发后,代码是不会直接被计算机运行的,因为计算机不认识代码,这时候我们需要一个编译器,将代码编译成计算机可运行的代码,而编译后的代码对开发人员来说,没有可读性,编译器就是中间的桥梁,预编译型语言都会有自己的编译器,而编译器中也存在不同语言的定义及解释,通过将可读代码解释为计算机代码集,实现项目的编译,这就是编译器的工作,然而不同的语言拥有不同的风格,不同的语法,不同的解释器,确立了语言有各自的文法来解释不同语言的代码


一、解释器模式

指定一门语言,定义它的文法的一种表示,并定义一个解释器,用该解释器来解释语言中的句子,是一种按照文法进行解析,来获取我们需要的指定的语法的方式。

二、应用场景

  • 当我们不能直接将语法公开时,可以对语法中的应用部分进行包装公开,将包装后的语法转换为正式语法的过程叫解释,包装后的语法称之为文法,处理这一过程的上下文叫解释器。
  • 一些重复出翔的问题可以用一种简单的语言进行表达。
  • 一个简单语法需要解释的场景。

三、角色分析

  1. 抽象表达式(IExpression):定义一个解释方法,交由具体子类进行具体解释,声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
  2. 终结符表达式(TerminalExpression):实现与文法中的终结符相关联的解释操作。
  3. 非终结符表达式(NonterminalExpression):为文法中的非终结符实现解释操作,对文法中每一条规则R1、R2……Rn都需要一个具体的非终结符表达式类。
  4. 上下文环境类(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

总结

解释器模式的本质是通过一定的方式,将代码按照文法解析获得相应的指定语法的方式,无所谓是终结符表达式还是非终结符表达式,更甚者由人会将终结符表达式作为抽象方法,使非终结符表达式对其进行重写,虽然不理解为什么会这样做,但是本着存在就是合理的理念,也就欣然接受了,毕竟名字都是开发的人定义的,思想都是终结出来的,甚至可以通过解释器的文法维度将解释器划分为一元,二元,多元无所谓,识别度高就行。

优点:

  • 语法是由很多类表示的,当语法规则修改时,只需要修改相应的非终结符表达式即可,当扩展语法时,则需要添加相应的非终结符表达式
  • 增加了新的解释表达式的方式
  • 解释器模式对用的文法应当是比较简单且易于实现的,过于复杂的语法不适合使用解释器模式。

缺点:

  • 解释器的每个语法都要产生一个非终结符表达式,当语法规则比较复杂时,就会产生大量解释类,引起类膨胀,增加系统维护的维度。
  • 计时器模式采用递归调用方法,每个非终结符表达式都只关心与自己有关的表达式,每个表达式都需要知道最终的结果,因此完整表达式的最终结果是通过从后往前递归调用的方式获取的,当完整表达式层级较深时,解释效率下降,且出错时调试困难,因为递归迭代的层级太深。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kenny@chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值