1. 程式人生 > >二十三種設計模式[15] - 解釋器模式(Interpreter)

二十三種設計模式[15] - 解釋器模式(Interpreter)

pub move 以及 exce aps ret 行為型 根節點 key

前言

解釋器模式,類行為型模式。一種用來解釋特定文法(語言的語法和表達式)規則的方式。這種行為模式使用了類似組合的結構來構建一個抽象語法樹(Abstract Syntax Tree,AST),用來描述該解釋器所解釋的語法。如果你想要了解組合模式,可跳轉至二十三種設計模式[8] - 組合模式(Composite Pattern)。

“ 給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。 ” ——《設計模式 - 可復用的面向對象軟件》

抽象語法樹(Abstract Syntax Tree,AST)

抽象語法樹是一種語法或表達式結構的樹狀表現形式。比如“ 1+2+3+4+5 ”的樹狀表現形式如下。

技術分享圖片

結構

技術分享圖片

  • Client(調用者):解釋器的調用者;
  • Context(上下文):用來存儲解釋器的全局信息;
  • AbstractExpression(抽象表達式):語法樹中所有節點的共有父類,用來定義解釋操作和保持所有節點的一致性;
  • TerminalExpression(終結符表達式):語法樹中的葉節點,實現文法中的終結符的解釋操作。文法中每一個終結符對應一個終結符表達式類;
  • NontermainalExpression(非終結符表達式):語法樹中的根節點,實現文法中的非終結符的解釋操作。該解釋操作一般要遞歸調用其子節點的解釋操作;

註:在“ 1+2+3+4+5 ”中,1、2、3、4、5為終結符,符號+為非終結符。

示例

還是以加、減、乘、除運算為例,下面將實現一個根據字符串進行求和運算的Demo。

技術分享圖片

/// <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();
}

技術分享圖片

示例中,我們為加減乘除每個運算各定義了一個非終結符表達式類,以及代表結果的終結符表達類。為了方便客戶的使用,我們創建了一個OperationInterpreter類來處理求和運算的處理邏輯。當我們需要為這個Demo擴展新的運算時(比如求余),只需要增加新的非終結符表達式類並將其增加到邏輯類中即可。表達式“ 6/3+2*5-1+3 ”的抽象語法樹如下。

技術分享圖片

總結

解釋器模式為我們增加了一種新的解釋表達式的方式,它易於實現一些簡單的文法並且也比較容易擴展新的文法。但由於非終結符表達式需要遞歸的調用其終結符表達式,使得代碼維護難度提高。同時該模式也存在類爆炸的風險。由於解釋器模式主要是用來解釋一些特有的自定義文法,所以在我們日常開發中可使用的場景較少。


以上,就是我對解釋器模式的理解,希望對你有所幫助。

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

系列匯總:https://www.cnblogs.com/wxingchen/p/9833744.html

本文著作權歸本人所有,如需轉載請標明本文鏈接(https://www.cnblogs.com/wxingchen/p/10055295.html)

二十三種設計模式[15] - 解釋器模式(Interpreter)