• 设计模式之解释器模式


    定义

    给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。这里的文法就是语言的语法规则。

    结构

    • AbstractExpression,抽象解释器角色,定义解释器的接口,约定解释器的解释操作。
    • TerminalExpression,终结符解释器,用来实现语法规则中和终结符相关的操作,不再包含其他的解释器,类似组合模式中的叶子对象。
    • UonterminalExpression,非终结符解释器,用来实现语法规则中和非终结符相关的操作,可以包含其他的解释器,类似组合模式中的组合对象。
    • Context,上下文,通常包含各个解释器所需要的数据或是公共的功能。

    简单实现

    抽象解释器

    public abstract class AbstractExpression {
    
      abstract void interpret(Context context);
    }
    

    终结符解释器

    public class TerminalExpression extends AbstractExpression {
    
      @Override
      void interpret(Context context) {
    
      }
    }
    

    非终结符解释器

    public class NonterminalExpression extends AbstractExpression {
    
      @Override
      void interpret(Context context) {
    
      }
    }
    

    上下文对象

    public class Context {
    
    }
    

    实现一个简单的计算器

    支持加减法的简单计算

    抽象解释器

    import java.util.Map;
    
    public abstract class AbstractExpression {
    
      abstract int interpreter(Map<String, Integer> context);
    
    }
    

    终结符解释器

    import java.util.Map;
    
    public class VarExpression extends AbstractExpression {
    
      private String key;
    
      public VarExpression(String key) {
        this.key = key;
      }
    
      @Override
      public int interpreter(Map<String, Integer> context) {
        if (context.containsKey(key)) {
          return context.get(key);
        }
        throw new IllegalArgumentException("not found field " + key);
      }
    }
    

    变量解释器,根据变量名称获取变量值,map作为上下文。

    非终结符解释器

    public abstract class SymbolExpression extends AbstractExpression {
    
      protected AbstractExpression left;
      protected AbstractExpression right;
    
      public SymbolExpression(AbstractExpression left, AbstractExpression right) {
    
        this.left = left;
        this.right = right;
      }
    
    }
    

    定义一个公共的操作符解释器,每个操作符都要有两个操作数,也就是变量

    import java.util.Map;
    
    public class AddExpression extends SymbolExpression {
    
      public AddExpression(AbstractExpression left, AbstractExpression right) {
        super(left, right);
      }
    
      //求出 left 和 right  表达式相加后的结果
      public int interpreter(Map<String, Integer> context) {
        return super.left.interpreter(context) + super.right.interpreter(context);
      }
    }
    
    import java.util.Map;
    
    public class SubExpression extends SymbolExpression {
    
      public SubExpression(AbstractExpression left, AbstractExpression right) {
        super(left, right);
      }
    
      //求出 left 和 right  表达式相减后的结果
      public int interpreter(Map<String, Integer> context) {
        return super.left.interpreter(context) - super.right.interpreter(context);
      }
    }
    

    定义加法解释器和减法解释器

    计算器

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.Stack;
    
    public class Calculator {
    
      private AbstractExpression expression;
    
      public Calculator(String expressionString) {
        init(expressionString);
      }
    
      /**
       * 表达式解析
       */
      private void init(String expressionString) {
        List<Character> charArray = infix2suffix(expressionString);
        Stack<AbstractExpression> stack = new Stack<>();
        for (char item : charArray) {
          if (item == '+') {
            if (stack.size() < 2) {
              throw iae();
            }
            AbstractExpression right = stack.pop();
            AbstractExpression left = stack.pop();
            stack.push(new AddExpression(left, right));
          } else if (item == '-') {
            if (stack.size() < 2) {
              throw iae();
            }
            AbstractExpression right = stack.pop();
            AbstractExpression left = stack.pop();
            stack.push(new SubExpression(left, right));
          } else if (item <= 'z' && item >= 'a') {
            stack.push(new VarExpression(String.valueOf(item)));
          } else {
            throw iae();
          }
        }
        if (stack.size() != 1) {
          throw iae();
        }
        this.expression = stack.pop();
      }
    
      /**
       * 中缀表达式转后缀表达式
       */
      private List<Character> infix2suffix(String infix) {
        List<Character> res = new ArrayList<>();
        Stack<Character> stack = new Stack<>();
        Set<Character> operatorSet = Set.of('+', '-');
        for (char item : infix.toCharArray()) {
          //是字母,直接添加到结果列表
          if (item <= 'z' && item >= 'a') {
            res.add(item);
            continue;
          }
          if (operatorSet.contains(item)) {
            if (!stack.isEmpty()) {
              res.add(stack.pop());
            }
            stack.push(item);
          } else {
            throw iae();
          }
        }
        while (!stack.isEmpty()) {
          res.add(stack.pop());
        }
        return res;
      }
    
      private IllegalArgumentException iae() {
        return new IllegalArgumentException("illegal expression");
      }
    
      public int run(Map<String, Integer> var) {
        return this.expression.interpreter(var);
      }
    }
    

    先将 中缀表达式 转成 后缀表达式,如

    a+b-c
    

    转成

    ab+c-
    

    然后将表达式解析成一个树形结构的解释器

    客户端

    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    public class Client {
    
      public static void main(String[] args) throws IOException {
        String expStr = "a+b-c";
    
        Map<String, Integer> context = new HashMap<>();
        context.put("a", 12);
        context.put("b", 34);
        context.put("c", 56);
    
        Calculator calculator = new Calculator(expStr);
        System.out.println("运算结果:" + expStr + "=" + calculator.run(context));
      }
    
    }
    

    输出为

    运算结果:a+b-c=-10
    

    解释器模式在Spring中的实现

    Spring中的SpelExpressionParser

    import org.springframework.expression.EvaluationContext;
    import org.springframework.expression.Expression;
    import org.springframework.expression.spel.standard.SpelExpressionParser;
    import org.springframework.expression.spel.support.StandardEvaluationContext;
    
    public class Client {
    
      public static void main(String[] args) {
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression("#a+#b-#c");
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("a",12);
        context.setVariable("b",34);
        context.setVariable("c",56);
        System.out.println(expression.getValue(context));//-10
      }
    
    }
    

    表达式解析器SpelExpressionParser根据字符串解析成Expression对象,根据EvaluationContext上下文对象获取执行结果。

    各种操作符如加号减号都是SpelNodeImpl对象。

    总结

    优点

    1. 一个解释器对应一条语法规则的方式,扩展新的语法非常容易。

    缺点

    1. 不适合复杂的语法,复杂语法需要构建复杂的抽象语法树。

    本质

    解释器模式的本质是分离实现,解释执行。将语法规则分离成多个,一个解释器处理一个语法规则。

    使用场景

    1. 当一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树的时候。
    2. 语法规则比较简单

    解释器模式在实际开发中应用很少,我们一般不需要自己去构建一种新的语法规则去解释。

    参考

    大战设计模式【20】—— 解释器模式
    设计模式(二十一)——解释器模式(Spring 框架中SpelExpressionParser源码分析)
    设计模式的征途—23.解释器(Interpreter)模式
    解释器模式(详解版)
    研磨设计模式-书籍

  • 相关阅读:
    15个新鲜出炉的 Photoshop 文本效果教程
    10个美丽的例子,插图在网页设计中的应用
    分享8个非常时髦的翻页特效(附代码片段)
    【Vue课堂】Vue.js 父子组件之间通信的十种方式
    Tippy.js – 轻量的 Javascript Tooltip 工具库
    12个美丽的网站与受到日出启发的配色方案
    精选:3个可以下载免费的高质量照片的网站
    nativefier
    Mark Text
    Puppeteer
  • 原文地址:https://www.cnblogs.com/strongmore/p/15255481.html
Copyright © 2020-2023  润新知