解释器模式的一个应用

需求

做产品的时候,有一个需求:对于一个字符串要在提交之前做校验,但是校验标准需要可配置。最合理的方案就是使用正则表达式+表达式组合。

基础数据结构

配置的数据结构如下

package com.example.ayizty.myapplication.reg;

import java.util.HashMap;

public class Configure {
    public HashMap<String, String> statements;//variant list
    public String expression;//separate all operand by space
    public HashMap<String, String> extras;
}

解释器分为:
- 取非:读下一个值,并取非
- 合:对前后两值取和
- 或:对前后两值取或
- 左括号:计算后面所有值,直到遇到右括号
- 右括号:返回前一个值
- 正则表达式:计算值
- 值:布尔值,表示中间结果
expression是由!、&、|、括号和正则表达式组成的逻辑表达式,所有单词用空格分隔开。正则表达式由变量表示,对应放到statements里。(主要是懒得做词法分析)

逻辑

这是一个标准的解释器模式的应用场景,但是,因为面试题的余孽(逆波兰式,表达式解析什么的),用解释器的模式做解释器不太容易,百度最高的就是用堆栈处理表达式,然后调解释器,这就成了strategy,而不是解释器。
解释器模式的精髓在于使用函数调用的堆栈来遍历表达式树,每个解释器仅对其前后的解释器有感知,对于所有解释器都是调用interpret(为了省事,我这里特殊处理了值解释器)。

解释器基类

package com.example.ayizty.myapplication.reg;

public abstract class AbsOperator {
    public static AbsOperator getOperand(String rawOperand, Processor processor) {
        switch (rawOperand.charAt(0)) {
            case '!':
                return new NegativeOperator();
            case '&':
                return new AndOperator();
            case '(':
                return new LeftParenthesisOperator();
            case '|':
                return new OrOperator();
            case ')':
                return new RightParenthesisOperator();
            default:
                return new RegOperand(rawOperand, processor);
        }
    }

    public abstract ValueOperand interpret(String input, Processor processor, int index);
}

解释器们

  • 取非
package com.example.ayizty.myapplication.reg;

public class NegativeOperand extends AbsOperand {
    @Override
    public ValueOperand interpret(String input, Processor processor, int index) {
        processor.removeOperand(index);
        AbsOperand next = processor.getOperand(index);
        ValueOperand result = next.interpret(input, processor, index);
        result.setValue(!result.getValue());
        return result;
    }

    @Override
    public String toString() {
        return "!";
    }
}
package com.example.ayizty.myapplication.reg;

public class AndOperator extends AbsOperator {
    @Override
    public ValueOperand interpret(String input, Processor processor, int index) {
        ValueOperand left = (ValueOperand) processor.removeOperand(index - 1);
        processor.removeOperand(index - 1);
        AbsOperator next = processor.getOperand(index - 1);
        return new ValueOperand(next.interpret(input, processor, index - 1).getValue() && left.getValue());
    }

    @Override
    public String toString() {
        return "&&";
    }
}
package com.example.ayizty.myapplication.reg;

public class OrOperator extends AbsOperator {
    @Override
    public ValueOperand interpret(String input, Processor processor, int index) {
        ValueOperand left = (ValueOperand) processor.removeOperand(index - 1);
        processor.removeOperand(index - 1);
        AbsOperator next = processor.getOperand(index - 1);
        return new ValueOperand(next.interpret(input, processor, index - 1).getValue() || left.getValue());
    }

    @Override
    public String toString() {
        return "||";
    }
}
  • 左括号
package com.example.ayizty.myapplication.reg;

public class LeftParenthesisOperator extends AbsOperator {
    @Override
    public ValueOperand interpret(String input, Processor processor, int index) {
        processor.removeOperand(index);
        AbsOperator expected = null;
        do {
            AbsOperator next = processor.getOperand(index);
            processor.putOperand(index, next.interpret(input, processor, index));
            expected = processor.getOperand(index + 1);
        } while (!(expected instanceof RightParenthesisOperator));
        return expected.interpret(input, processor, index + 1);
    }

    @Override
    public String toString() {
        return "(";
    }
}
  • 右括号
public class RightParenthesisOperator extends AbsOperator {
    @Override
    public ValueOperand interpret(String input, Processor processor, int index) {
        ValueOperand operand = (ValueOperand) processor.removeOperand(index - 1);
        processor.removeOperand(index - 1);
        return operand;
    }

    @Override
    public String toString() {
        return ")";
    }
}
  • 正则表达式
public class RegOperand extends AbsOperator {
    private Pattern mPattern;

    public RegOperand(String rawOperand, Processor processor) {
        mPattern = Pattern.compile(processor.getRawStatement(rawOperand));
    }

    @Override
    public ValueOperand interpret(String input, Processor processor, int index) {
        processor.removeOperand(index);
        Matcher matcher = mPattern.matcher(input);
        ValueOperand result = new ValueOperand(matcher.find());
        System.out.println(index + " " + this + " 3 " + result);
        return result;
    }

    @Override
    public String toString() {
        return mPattern.toString();
    }
}
public class ValueOperand extends AbsOperator {
    private boolean mValue;

    public ValueOperand(boolean value) {
        mValue = value;
    }

    @Override
    public ValueOperand interpret(String input, Processor processor, int index) {
        AbsOperator next = processor.getOperand(index + 1);
        return next.interpret(input, processor, index + 1);
    }

    public void setValue(boolean value) {
        mValue = value;
    }

    public boolean getValue() {
        return mValue;
    }

    @Override
    public String toString() {
        return String.valueOf(mValue);
    }
}

值之所以有这个intercept是因为整体流程的需要。主要就是Processor的结构:

package com.example.ayizty.myapplication.reg;

import java.util.ArrayList;

public class Processor {
    private Configure mConfigure;
    private ArrayList<AbsOperator> mOperands;

    public Processor(Configure configure) {
        mConfigure = configure;
        String expression = configure.expression;
        String[] rawOperands = expression.split(" ");
        final int length = rawOperands.length;
        mOperands = new ArrayList<>();
        for (int i = 0; i < length; ++i) {
            mOperands.add(AbsOperator.getOperand(rawOperands[i], this));
        }
        System.out.println("init " + this);
    }

    public AbsOperator removeOperand(int index) {
        return mOperands.remove(index);
    }

    public AbsOperator getOperand(int index) {
        return mOperands.get(index);
    }

    public void putOperand(int i, AbsOperator operand) {
        mOperands.add(i, operand);
    }

    public String getRawStatement(String name) {
        return mConfigure.statements.get(name);
    }

    public boolean process(String input) {
        while (1 != mOperands.size()) {
            ValueOperand operand = mOperands.get(0).interpret(input, this, 0);
            mOperands.add(0, operand);
            System.out.println("processor " + this);
        }
        AbsOperator operand = mOperands.get(0);
        if (operand instanceof ValueOperand) {
            return ((ValueOperand) operand).getValue();
        }
        return false;
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        final int length = mOperands.size();
        for (int i = 0; i < length; ++i) {
            stringBuilder.append(mOperands.get(i));
        }
        return stringBuilder.toString();
    }
}

这里就是解释器模式的整体调用逻辑,没有树、没有堆栈,简单到底的对第一个解释器进行解释。

PS

解释器模式确实让代码简单的令人发指,但是有一些问题:
- 类爆炸,简简单单的逻辑表达式已经10个类了,而且每个类都非常简单
- 过多的对象,每次运行都会产生大量中间变量,固然是很不好的。可以使用对象池+工厂解决。

你可能感兴趣的:(设计模式,解释器,布尔表达式)