(24)解释器设计模式

一、概述

        解释器模式(Interpreter Pattern)实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

        给定一个语言(汉语),定义它的文法(主谓宾)表示,并定义一个解释器,使用该解释器来解释语言中的句子。

主要解决:

        对于一些固定文法构建一个解释句子的解释器。

何时使用:

        如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

如何解决:

        构建语法树,定义终结符与非终结符。

关键代码:

        构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。

应用实例:

        编译器、运算表达式计算。

优点: 

1、可扩展性比较好,灵活。

2、增加了新的解释表达式的方式。

3、易于实现简单文法。

缺点

 1、可利用场景比较少。

2、对于复杂的文法比较难维护。

3、解释器模式会引起类膨胀。

4、解释器模式采用递归调用方法。

使用场景: 

1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。

2、一些重复出现的问题可以用一种简单的语言来进行表达。

3、一个简单语法需要解释的场景。

注意事项:

可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。

Context:环境角色(上下文),包含解释器之外的一些全局信息。

AbstractExpression:抽象表达式类,声明了抽象的解释操作,所有解释器类都继承或实现该类。这个接口为抽象语法树中所有的节点所共享

TerminalExpression:终结符表达式类,是AbstractExpression的子类,实现了文法中有关终结符相关的解释操作。 一个句子中的每个终结符需要该类的一个实例

NonTerminalExpression:非终结符表达式类,AbstractExpression的子类,该类的功能与终结表达式类相反,为文法中的非终结符实现解释操作

Client:客户端测试类。

二、代码测试

/**
 * 抽象解释器
 */
public abstract class AbstractExpression {
    /**
     * 解释表达式的抽象方法
     *
     * @param map 比如现有的表达式a+b, 那么map中应该存放的是{a=10,b=20}
     * @return
     */
    public abstract int interpret(Map<String, Integer> map);
}

/**
 * 运算符-解析器类,非终结
 */
public class SymbolExpression extends AbstractExpression {
    /**
     * 假如现有表达式 : a + b - c 需要解析
     * 分析:
     * 1、一个运算符连接的是它左右两个数字
     * 2、如上表达式 + 号连接的是 a 和 b,- 号连接的是 a + b 和 c
     * 3、所以我们将运算符的左右都看成是一个表达式也就是Expression
     */
    protected AbstractExpression leftExpression;
    protected AbstractExpression rightExpression;

    public SymbolExpression(AbstractExpression leftExpression, AbstractExpression rightExpression) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
    }

    /**
     * 不同种类的运算符由不同的运算符子类进行解析,所以该类不实现interpreter方法
     *
     * @param map 比如现有的表达式a+b, 那么map中应该存放的是{a=10,b=20}
     * @return
     */
    @Override
    public int interpret(Map<String, Integer> map) {
        return 0;
    }
}

/**
 * 非终结符解释器-add
 */
public class AddExpression extends SymbolExpression {
    public AddExpression(AbstractExpression leftExpression, AbstractExpression rightExpression) {
        super(leftExpression, rightExpression);
    }

    @Override
    public int interpret(Map<String, Integer> map) {
        return leftExpression.interpret(map) + rightExpression.interpret(map);
    }
}

/**
 * 非终结符解释器-sub
 */
public class SubExpression extends SymbolExpression {
    public SubExpression(AbstractExpression leftExpression, AbstractExpression rightExpression) {
        super(leftExpression, rightExpression);
    }

    @Override
    public int interpret(Map<String, Integer> map) {
        return leftExpression.interpret(map) - rightExpression.interpret(map);
    }
}

/**
 * 终结符解释器
 */
public class VarExpression extends AbstractExpression {
    // 公式中的变量
    private String key;

    public VarExpression(String key) {
        this.key = key;
    }

    // 通过key获取对应的值
    @Override
    public int interpret(Map<String, Integer> map) {
        return map.get(key);
    }
}

/**
 * 计算器
 */
public class CalculatorContext {
    private AbstractExpression expression;

    public CalculatorContext(String expressionString) {
        init(expressionString);
    }

    /**
     * 表达式解析
     *
     * @param expressionString 语句
     */
    private void init(String expressionString) {
        List<Character> charArray = infix2suffix(expressionString);
        // [a,d,+,c,-]
        System.out.println(charArray);
        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数值-AddExpression
                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数值-SubExpression
                stack.push(new SubExpression(left, right));
            } else if (item <= 'z' && item >= 'a') {
                // stack push数值-VarExpression
                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 = new HashSet<>();
        operatorSet.add('+');
        operatorSet.add('-');

        for (char item : infix.toCharArray()) {
            //是字母,直接添加到结果列表
            if (item <= 'z' && item >= 'a') {
                res.add(item);
                continue;
            }
            // 操作符
            if (operatorSet.contains(item)) {
                if (!stack.isEmpty()) {
                    // 弹出操作符,加入到res
                    res.add(stack.pop());
                }
                // stack压入操作符
                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.interpret(var);
    }
}

/**
 * 客户端
 */
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);
        // 上下文,并解析
        CalculatorContext calculator = new CalculatorContext(expStr);
        System.out.println("运算结果:" + expStr + "=" + calculator.run(context));
    }
}

三、解释器模式在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对象。

参考:设计模式之解释器模式 - strongmore - 博客园

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值