• 二十三种设计模式[15]


    前言

           解释器模式,类行为型模式。一种用来解释特定文法(语言的语法和表达式)规则的方式。这种行为模式使用了类似组合的结构来构建一个抽象语法树(Abstract Syntax Tree,AST),用来描述该解释器所解释的语法。如果你想要了解组合模式,可跳转至二十三种设计模式[8] - 组合模式(Composite Pattern)

           “ 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 ”   ——《设计模式 - 可复用的面向对象软件》

    抽象语法树(Abstract Syntax Tree,AST)

           抽象语法树是一种语法或表达式结构的树状表现形式。比如“ 1+2+3+4+5 ”的树状表现形式如下。

    image

    结构

    Interpreter_1

    • Client(调用者):解释器的调用者;
    • Context(上下文):用来存储解释器的全局信息;
    • AbstractExpression(抽象表达式):语法树中所有节点的共有父类,用来定义解释操作和保持所有节点的一致性;
    • TerminalExpression(终结符表达式):语法树中的叶节点,实现文法中的终结符的解释操作。文法中每一个终结符对应一个终结符表达式类;
    • NontermainalExpression(非终结符表达式):语法树中的根节点,实现文法中的非终结符的解释操作。该解释操作一般要递归调用其子节点的解释操作;

    注:在“ 1+2+3+4+5 ”中,1、2、3、4、5为终结符,符号+为非终结符。

    示例

           还是以加、减、乘、除运算为例,下面将实现一个根据字符串进行求和运算的Demo。

    Interpreter_2

    /// <summary>
    /// 抽象表达式接口
    /// </summary>
    public interface IExpression
    {
        double Interpret();
    }
    
    /// <summary>
    /// 终结符表达式
    /// </summary>
    public class ValueExpression : IExpression
    {
        private string Value { set; get; } = string.Empty;
    
        public ValueExpression(string value)
        {
            this.Value = value;
        }
    
        public double Interpret()
        {
            return Convert.ToDouble(this.Value);
        }
    }
    
    /// <summary>
    /// 非终结符表达式,加法
    /// </summary>
    public class PlusExpression : IExpression
    {
        private List<IExpression> ExpressionList { set; get; } = new List<IExpression>();
    
        public PlusExpression(List<IExpression> expressions)
        {
            this.ExpressionList = expressions;
        }
    
        public double Interpret()
        {
            double sum = this.ExpressionList.FirstOrDefault().Interpret();
    
            for (int i = 1, len = this.ExpressionList.Count; i < len; i++)
            {
                sum += this.ExpressionList[i].Interpret();
            }
    
            return sum;
        }
    }
    
    /// <summary>
    /// 非终结符表达式,减法
    /// </summary>
    public class MinusExpression : IExpression
    {
        private List<IExpression> ExpressionList { set; get; } = new List<IExpression>();
    
        public MinusExpression(List<IExpression> expressions)
        {
            this.ExpressionList = expressions;
        }
    
        public double Interpret()
        {
            double sum = this.ExpressionList.FirstOrDefault().Interpret();
    
            for (int i = 1, len = this.ExpressionList.Count; i < len; i++)
            {
                sum -= this.ExpressionList[i].Interpret();
            }
    
            return sum;
        }
    }
    
    /// <summary>
    /// 非终结符表达式,乘法
    /// </summary>
    public class MultiplicationExpression : IExpression
    {
        private List<IExpression> ExpressionList { set; get; } = new List<IExpression>();
    
        public MultiplicationExpression(List<IExpression> expressions)
        {
            this.ExpressionList = expressions;
        }
    
        public double Interpret()
        {
    
            double sum = this.ExpressionList.FirstOrDefault().Interpret();
    
            for (int i = 1, len = this.ExpressionList.Count; i < len; i++)
            {
                sum *= this.ExpressionList[i].Interpret();
            }
    
            return sum;
        }
    }
    
    /// <summary>
    /// 非终结符表达式,除法
    /// </summary>
    public class DivisionExpression : IExpression
    {
        private List<IExpression> ExpressionList { set; get; } = new List<IExpression>();
    
        public DivisionExpression(List<IExpression> expressions)
        {
            this.ExpressionList = expressions;
        }
    
        public double Interpret()
        {
    
            double sum = this.ExpressionList.FirstOrDefault().Interpret();
    
            for (int i = 1, len = this.ExpressionList.Count; i < len; i++)
            {
                sum /= this.ExpressionList[i].Interpret();
            }
    
            return sum;
        }
    }
    
    /// <summary>
    /// 运算解释器
    /// </summary>
    public class OperationInterpreter
    {
        public double Interpret(string str)
        {
            if (string.IsNullOrWhiteSpace(str))
            {
                throw new Exception("字符串为空");
            }
    
            double d;
            if (!double.TryParse(str.Replace("+", string.Empty)
                                    .Replace("-", string.Empty)
                                    .Replace("*", string.Empty)
                                    .Replace("/", string.Empty)
                                    .Replace(".", string.Empty), out d))
            {
                throw new Exception("当前字符串不可进行求和运算");
            }
    
            str = str.Replace(" ", string.Empty);
            var plusList = str.Split(new string[] { "+" }, StringSplitOptions.RemoveEmptyEntries);
            ValueExpression operationValue = null;
            foreach (var plusItem in plusList)
            {
                ValueExpression minuValue = null;
                var minusList = plusItem.Split(new string[] { "-" }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var minuItem in minusList)
                {
                    ValueExpression mulValue = null;
                    var mulList = minuItem.Split(new string[] { "*" }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (var mulItem in mulList)
                    {
                        var divList = mulItem.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
                        ValueExpression divValue = null;
                        foreach (var divItem in divList)
                        {
                            if (divValue == null)
                            {
                                divValue = new ValueExpression(divItem);
                            }
                            else
                            {
                                var divExp = new DivisionExpression(new List<IExpression> { divValue, new ValueExpression(divItem) });
                                divValue = new ValueExpression(divExp.Interpret().ToString());
                            }
                        }
    
                        if (mulValue == null)
                        {
                            mulValue = divValue;
                        }
                        else
                        {
                            var mulExp = new MultiplicationExpression(new List<IExpression> { mulValue, divValue });
                            mulValue = new ValueExpression(mulExp.Interpret().ToString());
                        }
                    }
    
                    if (minuValue == null)
                    {
                        minuValue = mulValue;
                    }
                    else
                    {
                        var minExp = new MinusExpression(new List<IExpression> { minuValue, mulValue });
                        minuValue = new ValueExpression(minExp.Interpret().ToString());
                    }
                }
    
                if (operationValue == null)
                {
                    operationValue = minuValue;
                }
                else
                {
                    var operationExp = new PlusExpression(new List<IExpression> { operationValue, minuValue });
                    operationValue = new ValueExpression(operationExp.Interpret().ToString());
                }
            }
    
            return operationValue.Interpret();
        }
    }
    
    static void Main(string[] args)
    {
        OperationInterpreter interpreter = new OperationInterpreter();
        Console.WriteLine($"6/3+2*5-1+3 = {interpreter.Interpret("6/3+2*5-1+3")}");
        Console.ReadKey();
    }

    image

           示例中,我们为加减乘除每个运算各定义了一个非终结符表达式类,以及代表结果的终结符表达类。为了方便客户的使用,我们创建了一个OperationInterpreter类来处理求和运算的处理逻辑。当我们需要为这个Demo扩展新的运算时(比如求余),只需要增加新的非终结符表达式类并将其增加到逻辑类中即可。表达式“ 6/3+2*5-1+3 ”的抽象语法树如下。

    image

    总结

           解释器模式为我们增加了一种新的解释表达式的方式,它易于实现一些简单的文法并且也比较容易扩展新的文法。但由于非终结符表达式需要递归的调用其终结符表达式,使得代码维护难度提高。同时该模式也存在类爆炸的风险。由于解释器模式主要是用来解释一些特有的自定义文法,所以在我们日常开发中可使用的场景较少。

           以上,就是我对解释器模式的理解,希望对你有所帮助。

           示例源码:https://gitee.com/wxingChen/DesignPatternsPractice

           系列汇总:https://www.cnblogs.com/wxingchen/p/10031592.html

           本文著作权归本人所有,如需转载请标明本文链接(https://www.cnblogs.com/wxingchen/p/10055295.html)

  • 相关阅读:
    多行文本溢出显示省略号(…)
    CSS3 grayscale滤镜图片变黑白实例页面
    腾讯TGideas语义化标签(转)
    jQuery Ajax通用js封装
    动态加载 js
    Javascript的四种继承方式
    Android四大组件之Activity
    Android四大组件通信的Intent介绍和详解
    oracle学习笔记(十四) 数据库对象 索引 视图 序列 同义词
    oracle学习笔记(十三) 查询练习(三) 子查询查询
  • 原文地址:https://www.cnblogs.com/wxingchen/p/10055295.html
Copyright © 2020-2023  润新知