(15)行为型模式——解释器

行为型模式——解释器(Interpreter)

问题背景

当需要为某一类问题创建文法并解释执行时,考虑使用解释器。比如要实现一个计算器,根据用户输入的表达式字符串输出表达式的值。在第一个版本中,计算器只实现了四则运算的功能,并且不能使用“(”和“)”,但是在以后的版本中会不断支持新的运算符。

解决方案

根据以往的经验,如果在第一个版本把逻辑硬编码到计算器类里,就会造成扩展困难,当下一个版本支持更多运算符的时候,就需要修改计算器类。为了应对变化,我们把每个基本表达式抽象出来,单独实现解释逻辑。提取一个表达式接口IExpression,它包含了一个解释方法Interpret来解释当前表达式,所有具体表达式都实现这个接口。之后添加一个上下文类Context来记录当前表达式的解释情况,Interpret方法接收一个上下文对象参数来确定要解释的上下文。

一个有实际意义的解释器是非常复杂的,实现起来非常困难。本文旨在介绍解释器这种设计思想,而不是要教读者如何实现一个解释器,因此这里只用一个简单的示例。这个示例实现了十以内整数加减法表达式的解析,程序结构如下:
程序结构

效果

  1. 易于扩展文法。
  2. 可以执行复杂逻辑。

缺陷

解释器只有在遇到复杂逻辑时才会用到,但一个复杂的解释器又非常难以实现和维护。词法分析、语法分析这些过程非常复杂,造成了解释器实现逻辑也非常复杂;不配合反射的接口只能将对象的表示剥离,却不能将对象的创建剥离,因此无法做到表达式类和用户完全解耦。

相关模式

  1. 复合:复杂解释器的抽象语法树是复合结构的一个实例。
  2. 享元:可以把某些表达式设计成享元。
  3. 迭代器:可以用迭代器遍历抽象语法树。
  4. 访问器:可以将具体表达式的逻辑集中到一个类里。

实现

using System;
using System.Collections.Generic;
using System.IO;

namespace Interpreter
{
    class Client
    {
        public interface IExpression
        {
            int Interpret(Context context);
        }

        public class ValueExpression
        {
            private int exp;
            public ValueExpression(int exp)
            {
                this.exp = exp;
            }
            public int Interpret(Context context)
            {
                return Convert.ToInt32(((char) exp).ToString());
            }
        }

        public class AddExpression
        {
            public int Interpret(Context context)
            {
                return context.Pop() + new ValueExpression(context.Read()).Interpret(context);
            }
        }

        public class SubExpression
        {
            public int Interpret(Context context)
            {
                return context.Pop() - new ValueExpression(context.Read()).Interpret(context);
            }
        }

        public class TerminalExpression
        {
            public int Interpret(Context context)
            {
                return context.Pop();
            }
        }

        public class Calculator
        {
            private Context context = new Context();
            public int Calculate(string expression)
            {
                context.Expression = expression;
                int peek;
                while ((peek = context.Read()) != -1)
                {
                    switch (peek)
                    {
                        case int c when c >= '0' && c <= '9':
                            context.Push(new ValueExpression(c).Interpret(context));
                            break;
                        case '+':
                            context.Push(new AddExpression().Interpret(context));
                            break;
                        case '-':
                            context.Push(new SubExpression().Interpret(context));
                            break;
                    }
                }
                return new TerminalExpression().Interpret(context);
            }
        }

        public class Context
        {
            private Stack<int> stack = new Stack<int>();
            private StringReader expression;
            public void Push(int x)
            {
                stack.Push(x);
            }
            public int Pop()
            {
                return stack.Pop();
            }
            public int Read()
            {
                return expression.Read();
            }
            public string Expression
            {
                set
                {
                    stack.Clear();
                    expression = new StringReader(value);
                }
            }
        }

        static void Main(string[] args)
        {
            var calc = new Calculator();
            var expression = "1+2+3";
            var result = calc.Calculate(expression);
            Console.WriteLine($"{expression}={result}");

            expression = "1-5+2";
            result = calc.Calculate(expression);
            Console.WriteLine($"{expression}={result}");

            expression = "0-5+0";
            result = calc.Calculate(expression);
            Console.WriteLine($"{expression}={result}");

            expression = "7+8+9";
            result = calc.Calculate(expression);
            Console.WriteLine($"{expression}={result}");
        }
    }
}

运行结果

发布了27 篇原创文章 · 获赞 41 · 访问量 2075

猜你喜欢

转载自blog.csdn.net/DIAX_/article/details/104232661
今日推荐