22、设计模式之解释器模式

解释器模式是一种行为设计模式,用于评估语言的语法或表达式。它通过构建语法树来解释特定上下文中的句子,常用于SQL解析和符号处理引擎。模式包括解释器接口和环境类,支持文法扩展和新表达式方式的添加,但复杂的文法维护困难,可能导致类膨胀。在Java中,expression4J可作为替代方案。本文将详细介绍解释器模式的实现,包括算数、逻辑表达式接口,规则接口,执行上下文以及后缀表达式的应用。
摘要由CSDN通过智能技术生成

解释器模式

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。
这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

介绍

意图: 给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。

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

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

如何解决: 构建语法树,定义终结符与非终结符。

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

应用实例: 编译器、运算表达式计算。

优点:

  1. 可扩展性比较好,灵活。
  2. 增加了新的解释表达式的方式。
  3. 易于实现简单文法。

缺点:

  1. 可利用场景比较少。
  2. 对于复杂的文法比较难维护。
  3. 解释器模式会引起类膨胀。
  4. 解释器模式采用递归调用方法。

使用场景:

  1. 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  2. 一些重复出现的问题可以用一种简单的语言来进行表达。
  3. 一个简单语法需要解释的场景。

注意事项:
可利用场景比较少,JAVA 中如果碰到可以用Drools或aviator代替。

代码实现

这里实现的解释器

  • 既可以直接计算(1+2)*(3+4)这样的表达式;
  • 也可以计算(a+b)*(c+d) 然后通过执行上下文对abcd进行赋值,例如:a=3,b=5,c=9,d=2,然后计算;
  • 还可以执行(a&b)|(c&d),然后通过执行上下文对abcd进行赋值执行的规则和规则匹配变量值。
    例如:对a赋值的规则 x > 5?true:false 这样的规则,同时对a的规则x的变量赋予6,也可以赋予4。这样a在表达式中(a&b)|(c&d)中由于不同的变量,可能表现的值不一样。其他同样。最后求值。

算数表达式接口

public interface MathExpr {

    int exec(ExprContext exprContext);
}

实现加、减、乘、除、乘方

/**
 * 加法表达式
 */
public class AddExpr implements MathExpr {
    private MathExpr left;
    private MathExpr right;

    public AddExpr(MathExpr left, MathExpr right)
    {
        this.left = left;
        this.right = right;
    }

    public int exec(ExprContext exprContext)
    {
        return left.exec(exprContext) + right.exec(exprContext);
    }
}

/**
 * 除法表达式
 */
public class DivExpr implements MathExpr{
    private MathExpr left;
    private MathExpr right;

    public DivExpr(MathExpr left, MathExpr right)
    {
        this.left = left;
        this.right = right;
    }

    public int exec(ExprContext exprContext)
    {
        return left.exec(exprContext) / right.exec(exprContext);
    }
}

/**
 * 乘法表达式
 */
public class MulExpr implements MathExpr{
    private MathExpr left;
    private MathExpr right;

    public MulExpr(MathExpr left, MathExpr right)
    {
        this.left = left;
        this.right = right;
    }

    public int exec(ExprContext exprContext)
    {
        return left.exec(exprContext) * right.exec(exprContext);
    }
}

/**
 * 减法表达式
 */
public class SubExpr implements MathExpr{
    private MathExpr left;
    private MathExpr right;

    public SubExpr(MathExpr left, MathExpr right)
    {
        this.left = left;
        this.right = right;
    }

    public int exec(ExprContext exprContext)
    {
        return left.exec(exprContext) - right.exec(exprContext);
    }
}

/**
 * 乘方表达式
 */
public class PowerExpr implements MathExpr{
    private MathExpr left;
    private MathExpr right;

    public PowerExpr(MathExpr left, MathExpr right)
    {
        this.left = left;
        this.right = right;
    }

    public int exec(ExprContext exprContext)
    {
        return (int)Math.pow(left.exec(exprContext),right.exec(exprContext));
    }
}

//算数表达式原子,如 a+b 中a和b
public class ValExpr implements MathExpr {

    private final String val;
    public ValExpr(String val)
    {
        this.val = val;
    }
    @Override
    public int exec(ExprContext exprContext) {
        Integer content = exprContext.getVal(val);
        if (content == null){
            throw new IllegalArgumentException("不支持的规则类型");
        }
        return content;
    }
}

定义逻辑表达式接口

public interface LogicExpr {

    boolean exec(ExprContext exprContext);
}

定义与、或、非逻辑运算

//逻辑与
public class AndExpr implements LogicExpr {
    private final LogicExpr left,right;

    public AndExpr(LogicExpr left, LogicExpr right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public boolean exec(ExprContext exprContext) {
        return left.exec(exprContext) && right.exec(exprContext);
    }
}
//逻辑非
public class NotExpr implements LogicExpr {
    private final LogicExpr expr;
    public NotExpr(LogicExpr expr)
    {
        this.expr = expr;
    }
    @Override
    public boolean exec(ExprContext exprContext) {
        return !expr.exec(exprContext);
    }
}

//逻辑或
public class OrExpr implements LogicExpr {
    private final LogicExpr left,right;

    public OrExpr(LogicExpr left, LogicExpr right) {
        this.left = left;
        this.right = right;
    }
    @Override
    public boolean exec(ExprContext exprContext) {
        return left.exec(exprContext) || right.exec(exprContext);
    }
}

//逻辑表达式原子,如 a & b 中a和b
public class RuleExpr implements LogicExpr {

    private final String val;
    public RuleExpr(String val)
    {
        this.val = val;
    }
    @Override
    public boolean exec(ExprContext exprContext) {
        Rule rule = exprContext.getRule(val);
        if (rule instanceof NumbleRule){
            Integer content = exprContext.getVal(val);
            return rule.match(content);
        }

        if (rule instanceof StringRule){
            String content = exprContext.getVal(val);
            return rule.match(content);
        }

        throw new IllegalArgumentException("不支持的规则类型");
    }
}

定义规则接口

public interface Rule<T> {
    boolean match(T content);
}

定义字符串规则:支持正则、包含、以…开头、以…接口

public abstract class StringRule implements Rule<String>{

    protected final String regex;

    protected StringRule(String regex){
        this.regex = regex;
    }

    @Override
    public abstract boolean match(String content);

    public static StringRule regex(String regex){
        return new RegexRule(regex);
    }

    public static StringRule contains(String contains){
        return new ContainsRule(contains);
    }

    public static StringRule startWith(String startWith){
        return new StartWithRule(startWith);
    }

    public static StringRule endWith(String endWith){
        return new EndWithRule(endWith);
    }

    private static class EndWithRule extends StringRule{
        public EndWithRule(String endWith)
        {
            super(endWith);
        }

        public boolean match(String content)
        {
            return content.endsWith(this.regex);
        }
    }

    private static class StartWithRule extends StringRule{

        public StartWithRule(String startWith)
        {
            super(startWith);
        }

        public boolean match(String content)
        {
            return content.startsWith(this.regex);
        }
    }

    private static class ContainsRule extends StringRule{


        public ContainsRule(String contains)
        {
            super(contains);
        }

        public boolean match(String content)
        {
            return content.contains(this.regex);
        }
    }

    private static class RegexRule extends StringRule{

        private final Pattern pattern;

        public RegexRule(String regex)
        {
            super(regex);
            this.pattern = Pattern.compile(this.regex);
        }

        public boolean match(String content)
        {
            return pattern.matcher(content).matches();
        }
    }

}

定义数据规则:>、<、>=、<=、=、between

public abstract class NumbleRule implements Rule<Integer> {

    protected int low;

    protected int high;

    private NumbleRule(){}

    protected NumbleRule(int low, int high) {
        this.low = low;
        this.high = high;
    }

    public static NumbleRule lt(int numble,boolean eq)
    {
        return new LT(numble,eq);
    }

    public static NumbleRule gt(int numble,boolean eq)
    {
        return new GT(numble,eq);
    }

    public static NumbleRule eq(int numble)
    {
        return new EQ(numble);
    }

    public static NumbleRule between(int low,int high)
    {
        return new Between(low,high);
    }

    public static NumbleRule between(int low,int high,boolean include_low,boolean include_high)
    {
        return new Between(low,high,include_low,include_high);
    }


    @Override
    public abstract boolean match(Integer content);

    private static class LT extends NumbleRule{
        private final boolean eq;
        public LT(int numble,boolean eq)
        {
            super(numble,numble);
            this.eq = eq;
        }
        @Override
        public boolean match(Integer content) {
            if (eq) {
                return content <= low;
            }else {
                return content < low;
            }
        }
    }

    private static class GT extends NumbleRule{
        private final boolean eq;
        public GT(int numble,boolean eq)
        {
            super(numble,numble);
            this.eq = eq;
        }
        @Override
        public boolean match(Integer content) {
            if (eq) {
                return content >= low;
            }else {
                return content > low;
            }
        }
    }

    private static class EQ extends NumbleRule{
        public EQ(int numble)
        {
            super(numble,numble);
        }
        @Override
        public boolean match(Integer content) {
            return content == super.low;
        }
    }

    private static class Between extends NumbleRule{
        private final boolean include_low;

        private final boolean include_high;

        public Between(int low,int high,boolean include_low,boolean include_high)
        {
            super(low,high);
            this.include_low = include_low;
            this.include_high = include_high;
        }
        public Between(int low,int high)
        {
           this(low,high,true,true);
        }
        @Override
        public boolean match(Integer content) {
            if (include_low){
                return content >= low && (include_high?content <= high:content < high);
            }else {
                return content > low && (include_high?content <= high:content < high);
            }
        }
    }
}

定义执行上下文,负责传递变量、规则

public class ExprContext {
    private final Map<String,Object> map;

    public ExprContext()
    {
        this.map = new HashMap<String,Object>();
    }

    public <T> void putVal(String key,T value){
        this.map.put(makeValKey(key),value);
    }

    private String makeValKey(String key){
        return "val_"+key;
    }

    private String makeRuleKey(String key){
        return "rule_"+key;
    }

    public void putRule(String key, Rule rule){
        this.map.put(makeRuleKey(key),rule);
    }

    public <T> T getVal(String key){
        return (T)this.map.get(makeValKey(key));
    }

    public Rule getRule(String key){
        return (Rule) this.map.get(makeRuleKey(key));
    }

}

通过后缀表达式实现的算数表达式、逻辑表达式解释器。

public class Interpreter {

    private static final char[] opChars= {'+','-','*','/','^'
                                            ,'&','|','!','(',')'};

    private final String input;

    public Interpreter(String input){
        this.input = input;
    }

    public LogicExpr parseLogicExpr() {
        String expr = input;
        List<Object> exprStack = new ArrayList<>();
        Stack<Character> opStack = new Stack<>();
        int i = 0;
        //按照后缀表达式解析
        while (i<expr.length()) {
            char c = expr.charAt(i);
            if (isValChar(c)){
                String val = valToken(expr,i);
                exprStack.add(new RuleExpr(val));
                i = i+ val.length();
                continue;
            }
            if (isOp(c)){
                if (c == '('){
                    opStack.push(c);
                }else if (c == ')'){
                    while (!opStack.isEmpty()){
                        char op = opStack.pop();
                        if (op == '('){
                            break;
                        }
                        exprStack.add(op);
                    }
                }else {
                    //'&','|','!'
                    while (!opStack.isEmpty()){
                        char op = opStack.peek();
                        if (op == '(' || priority(op) < priority(c)){
                            break;
                        }else {
                            exprStack.add(opStack.pop());
                        }
                    }
                    opStack.push(c);
                }
            }
            i++;
        }

        while (!opStack.isEmpty()){
            exprStack.add(opStack.pop());
        }

        Stack<LogicExpr> ruleExprStack = new Stack<>();
        for (int j = 0; j < exprStack.size(); j++) {
            Object o = exprStack.get(j);
            if (o instanceof Character){
                char c = (char)o;
                if (c == '!'){
                    LogicExpr ruleExpr = ruleExprStack.pop();
                    ruleExprStack.push(new NotExpr(ruleExpr));
                    continue;
                }
                LogicExpr ruleExpr1 = ruleExprStack.pop();
                LogicExpr ruleExpr2 = ruleExprStack.pop();
                if (c == '&'){
                    ruleExprStack.push(new AndExpr(ruleExpr2,ruleExpr1));
                }else if (c == '|'){
                    ruleExprStack.push(new OrExpr(ruleExpr2,ruleExpr1));
                }
            }else {
                ruleExprStack.push((LogicExpr) o);
            }
        }
        return ruleExprStack.pop();
    }

    private static int priority(char op){
        switch (op){
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
                return 2;
            case '^':
                return 3;
            case '&':
            case '|':
                return 4;
            case '!':
                return 5;
            default:
                throw new IllegalArgumentException("非法字符:"+op);
        }
    }

    private boolean isValChar(char c){
        return( c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')|| (c >= 'A' && c <= 'Z');
    }

    private boolean isOp(char c){
        for (char opChar : opChars) {
            if (opChar == c) {
                return true;
            }
        }
        return false;
    }

    private boolean isOp(String c){
        if (c.length() == 1) {
            char ch = c.charAt(0);
            return isOp(ch);
        }
        return false;
    }

    private String valToken(String expr,int from){
        String token = "";
        for (int i = from; i < expr.length(); i++) {
            char c = expr.charAt(i);
            if (isValChar(c)){
                token += c;
            }else{
                break;
            }
        }
        return token;
    }

    public int eval(){
        Stack<Character> opStack = new Stack<>();
        List<String> result = new ArrayList<>();
        String expr = input;
        int i = 0;
        while (i<expr.length()){
            char c = expr.charAt(i);//从左到右扫描中缀表达式的每个元素
            if (isValChar(c)){
                String val = valToken(expr,i);
                result.add(val);
                i += val.length();
                continue;
            }
            if(isOp(c)){
                if (c == ')'){
                    while (!opStack.isEmpty()){
                        char o = opStack.pop();
                        if (o == '('){
                            break;
                        }
                        result.add(String.valueOf(o));
                    }
                }else if (c == '('){
                    opStack.push(c);
                }else{
                    //'+','-','*','/','^','&','|','!'
                    while (!opStack.isEmpty()){
                        char o = opStack.peek();
                        if (o == '('||priority(o) < priority(c)){
                            break;
                        }else{
                            //priority(o) >= priority(c)
                            result.add(String.valueOf(opStack.pop()));
                        }
                    }
                    opStack.push(c);
                }
            }
            i++;
        }

        while (!opStack.isEmpty()){
            result.add(String.valueOf(opStack.pop()));
        }
        Stack<Integer> numStack = new Stack<>();

        for (int j = 0; j < result.size(); j++) {
            String op = result.get(j);
            if (isOp(op)){
                int num1 = numStack.pop();//栈顶元素
                int num2 = numStack.pop();//次栈顶元素
                switch (op){
                    /**
                     * 对于后缀表达式,进行计算时,需要栈顶元素与次栈顶元素进行计算
                     * 如
                     * 次栈顶元素 + 栈顶元素
                     * 次栈顶元素 - 栈顶元素
                     * 次栈顶元素 * 栈顶元素
                     * 次栈顶元素 / 栈顶元素
                     * 次栈顶元素 ^ 栈顶元素
                     */

                    case "+": {
                        numStack.push(num2+num1);
                        break;
                    }
                    case "-": {
                        numStack.push(num2-num1);
                        break;
                    }
                    case "*": {
                        numStack.push(num2*num1);
                        break;
                    }
                    case "/": {
                        numStack.push(num2 / num1);
                        break;
                    }
                    case "^": {
                        numStack.push((int) Math.pow(num2,num1));
                        break;
                    }
                    default:{
                        throw new IllegalArgumentException("非法字符:"+op);
                    }
                }
            }else {
                numStack.push(Integer.parseInt(op));
            }
        }
        return numStack.pop();
    }

    public MathExpr parseMathExpr() {
        String expr = input;
        List<String> exprStack = new ArrayList<>();
        Stack<Character> opStack = new Stack<>();
        int i = 0;
        //按照后缀表达式解析
        while (i<expr.length()) {
            char c = expr.charAt(i);
            if (isValChar(c)){
                String val = valToken(expr,i);
                exprStack.add(val);
                i=i+ val.length();
                continue;
            }
            if (isOp(c)){
                if (c == '('){
                    opStack.push(c);
                }else if (c == ')'){
                    while (!opStack.isEmpty()){
                        char op = opStack.pop();
                        if (op == '('){
                            break;
                        }
                        exprStack.add(String.valueOf(op));
                    }
                }else {
                    //'+','-','*','/','^'
                    while (!opStack.isEmpty()){
                        char o = opStack.peek();
                        if (o == '(' || priority(o) < priority(c)){
                            break;
                        }
                        exprStack.add(String.valueOf(opStack.pop()));
                    }
                    opStack.push(c);
                }
            }
            i++;
        }
        while (!opStack.isEmpty()){
            exprStack.add(String.valueOf(opStack.pop()));
        }
        Stack<MathExpr> mathExprStack = new Stack<>();
        for (int j = 0; j < exprStack.size(); j++) {
            String t = exprStack.get(j);
            if (isOp(t)){
                MathExpr num1 = mathExprStack.pop();//栈顶元素
                MathExpr num2 = mathExprStack.pop();//次栈顶元素
                switch (t){
                    case "+":
                        mathExprStack.push(new AddExpr(num2,num1));
                        break;
                    case "-":
                        mathExprStack.push(new SubExpr(num2,num1));
                        break;
                    case "*":
                        mathExprStack.push(new MulExpr(num2,num1));
                        break;
                    case "/":
                        mathExprStack.push(new DivExpr(num2,num1));
                        break;
                    case "^":
                        mathExprStack.push(new PowerExpr(num2,num1));
                        break;
                    default:
                        throw new IllegalArgumentException("非法字符:"+t);
                }
            }else {
                mathExprStack.push(new ValExpr(t));
            }
        }
        return mathExprStack.pop();
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        testLogicInterpreter();
        testMathInterpreter();
    }

    private static void testMathInterpreter() {
        System.out.println("testMathInterpreter");
        MathExpr mathExpr = new Interpreter("(a + b)*(c+d)").parseMathExpr();

        //执行上下文,相当于计算(1+2)*(3+4) = 21
        ExprContext ruleExprContext = new ExprContext();
        ruleExprContext.putVal("a", 1);
        ruleExprContext.putVal("b", 2);
        ruleExprContext.putVal("c", 3);
        ruleExprContext.putVal("d", 4);

        int result = mathExpr.exec(ruleExprContext);
        System.out.println(result);
        
        //直接执行表达式(1+2)*(3+4),不需要执行上下文
        //这里也可以把1、2、3、4当作变量,
        //然后按照上面的通过执行上下文传递变量,计算结果
        int abc = new Interpreter("(1+2)*(3+4)").eval();
        System.out.println(abc);

    }


    private static void testLogicInterpreter() {
        System.out.println("testLogicInterpreter");
        LogicExpr logicExpr = new Interpreter("(a & b)|!(c&d)").parseLogicExpr();

        /**
        * 执行上下文
        * (a & b)|!(c&d)
        * 相当于
        * (1<5 & 2<5)|!(6<5&7<5)
        */
        Rule<Integer> rule = NumbleRule.lt(5, false);
        ExprContext ruleExprContext = new ExprContext();
        ruleExprContext.putRule("a", rule);
        ruleExprContext.putRule("b", rule);
        ruleExprContext.putRule("c", rule);
        ruleExprContext.putRule("d", rule);

        ruleExprContext.putVal("a", 1);
        ruleExprContext.putVal("b", 2);
        ruleExprContext.putVal("c", 6);
        ruleExprContext.putVal("d", 7);

        boolean result = logicExpr.exec(ruleExprContext);
        System.out.println(result);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风吹千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值