解释器模式的一个应用

来源:互联网 发布:农村淘宝政府如何补贴 编辑:程序博客网 时间:2024/05/22 04:49

需求

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

基础数据结构

配置的数据结构如下

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个类了,而且每个类都非常简单
- 过多的对象,每次运行都会产生大量中间变量,固然是很不好的。可以使用对象池+工厂解决。

0 0
原创粉丝点击