本文节选自《设计模式就该这样学》

1 应用解释器模式解析数学表达式

上面用解释器模式来实现一个数学表达式计算器,蕴含加、减、乘、除运算。
首先定义形象表达式角色IArithmeticInterpreter接口。

public interface IArithmeticInterpreter {    int interpret();}

创立终结表达式角色Interpreter抽象类。

public abstract class Interpreter implements IArithmeticInterpreter {    protected IArithmeticInterpreter left;    protected IArithmeticInterpreter right;    public Interpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {        this.left = left;        this.right = right;    }}

而后别离创立非终结符表达式角色加、减、乘、除解释器,加法运算表达式AddInterpreter类的代码如下。

public class AddInterpreter extends Interpreter {    public AddInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {        super(left, right);    }    public int interpret() {        return this.left.interpret() + this.right.interpret();    }}

减法运算表达式SubInterpreter类的代码如下。

public class SubInterpreter extends Interpreter {    public SubInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right) {        super(left, right);    }    public int interpret() {        return this.left.interpret() - this.right.interpret();    }}

乘法运算表达式MultiInterpreter类的代码如下。

public class MultiInterpreter extends Interpreter {    public MultiInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right){        super(left,right);    }    public int interpret() {        return this.left.interpret() * this.right.interpret();    }}

除法运算表达式DivInterpreter类的代码如下。

public class DivInterpreter extends Interpreter {    public DivInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right){        super(left,right);    }    public int interpret() {        return this.left.interpret() / this.right.interpret();    }}

数字表达式NumInterpreter类的代码如下。

public class NumInterpreter implements IArithmeticInterpreter {    private int value;    public NumInterpreter(int value) {        this.value = value;    }    public int interpret() {        return this.value;    }}

接着创立计算器GPCalculator类。

public class GPCalculator {    private Stack<IArithmeticInterpreter> stack = new Stack<IArithmeticInterpreter>();    public GPCalculator(String expression) {        this.parse(expression);    }    private void parse(String expression) {        String [] elements = expression.split(" ");        IArithmeticInterpreter left,right;        for (int i = 0; i < elements.length ; i++) {            String operator = elements[i];            if(OperatorUtil.ifOperator(operator)){                left = this.stack.pop();                right = new NumInterpreter(Integer.valueOf(elements[++i]));                System.out.println("出栈" + left.interpret() + "和" + right.interpret());                this.stack.push(OperatorUtil.getInterpreter(left,right,operator));                System.out.println("利用运算符:" + operator);            }else {                NumInterpreter numInterpreter = new NumInterpreter(Integer.valueOf(elements[i]));                this.stack.push(numInterpreter);                System.out.println("入栈:" + numInterpreter.interpret());            }        }    }    public int calculate() {        return this.stack.pop().interpret();    }}

工具类OperatorUtil的代码如下。

public class OperatorUtil {    public static boolean isOperator(String symbol) {        return (symbol.equals("+") || symbol.equals("-") || symbol.equals("*"));    }    public static Interpreter getInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter     right, String symbol) {        if (symbol.equals("+")) {            return new AddInterpreter(left, right);        } else if (symbol.equals("-")) {            return new SubInterpreter(left, right);        } else if (symbol.equals("*")) {            return new MultiInterpreter(left, right);        } else if (symbol.equals("/")) {            return new DivInterpreter(left, right);        }        return null;    }}

最初编写客户端测试代码。

public static void main(String[] args) {        System.out.println("result: " + new GPCalculator("10 + 30").calculate());        System.out.println("result: " + new GPCalculator("10 + 30 - 20").calculate());        System.out.println("result: " + new GPCalculator("100 * 2 + 400 * 1 + 66").calculate());}

运行后果如下图所示。

当然,下面的繁难计算器还没有思考优先级,就是从左至右顺次运算的。在理论运算中,乘法和除法属于一级运算,加法和减法属于二级运算。一级运算须要优先计算。另外,咱们能够通过应用括号手动调整运算的优先级。咱们再优化一下代码,首先新建一个枚举类。

public enum OperatorEnum {    LEFT_BRACKET("("),    RIGHT_BRACKET(")"),    SUB("-"),    ADD("+"),    MULTI("*"),    DIV("/"),    ;    private String operator;    public String getOperator() {        return operator;    }    OperatorEnum(String operator) {        this.operator = operator;    }}

而后批改OperatorUtil的解决逻辑,设置两个栈。

public class OperatorUtil {    public static Interpreter getInterpreter(Stack<IArithmeticInterpreter> numStack, Stack<String> operatorStack) {        IArithmeticInterpreter right = numStack.pop();        IArithmeticInterpreter left = numStack.pop();        String symbol = operatorStack.pop();        System.out.println("数字出栈:" + right.interpret() + "," + left.interpret() + ",操作符出栈:" + symbol);        if (symbol.equals("+")) {            return new AddInterpreter(left, right);        } else if (symbol.equals("-")) {            return new SubInterpreter(left, right);        } else if (symbol.equals("*")) {            return new MultiInterpreter(left, right);        } else if (symbol.equals("/")) {            return new DivInterpreter(left, right);        }        return null;    }}

批改GPCalculator的代码。

public class GPCalculator {    //数字stack    private Stack<IArithmeticInterpreter> numStack = new Stack<IArithmeticInterpreter>();    //操作符stack    private Stack<String> operatorStack = new Stack<String>();    /**     * 解析表达式     * @param expression     */    public GPCalculator(String expression) {        this.parse(expression);    }    private void parse(String input) {        //对表达式去除空字符操作        String expression = this.fromat(input);        System.out.println("规范表达式:" + expression);        for (String s : expression.split(" ")) {            if (s.length() == 0){                //如果是空格,则持续循环,什么也不操作                continue;            }            //如果是加减,因为加减的优先级最低,所以这里只有遇到加减号,无论操作符栈中是什么运算符都要运算            else if (s.equals(OperatorEnum.ADD.getOperator())                    || s.equals(OperatorEnum.SUB.getOperator())) {                //当栈不是空的,并且栈中最下面的一个元素是加减乘除的任意一个                while (!operatorStack.isEmpty()                        &&(operatorStack.peek().equals(OperatorEnum.SUB.getOperator())                        || operatorStack.peek().equals(OperatorEnum.ADD.getOperator())                        || operatorStack.peek().equals(OperatorEnum.MULTI.getOperator())                        || operatorStack.peek().equals(OperatorEnum.DIV.getOperator()))) {                    //后果存入栈中                    numStack.push(OperatorUtil.getInterpreter(numStack,operatorStack));                }                //运算完后将以后的运算符入栈                System.out.println("操作符入栈:"+s);                operatorStack.push(s);            }            //以后运算符是乘除的时候,因为优先级高于加减           //所以要判断最下面的是否是乘除,如果是乘除,则运算,否则间接入栈            else if (s.equals(OperatorEnum.MULTI.getOperator())                    || s.equals(OperatorEnum.DIV.getOperator())) {                while (!operatorStack.isEmpty()&&(                        operatorStack.peek().equals(OperatorEnum.MULTI.getOperator())                        || operatorStack.peek().equals(OperatorEnum.DIV.getOperator()))) {                    numStack.push(OperatorUtil.getInterpreter(numStack,operatorStack));                }                //将以后操作符入栈                System.out.println("操作符入栈:"+s);                operatorStack.push(s);            }            //如果是左括号,则间接入栈,什么也不必操作,trim()函数是用来去除空格的,因为下面的宰割                 操作,可能会令操作符带有空格            else if (s.equals(OperatorEnum.LEFT_BRACKET.getOperator())) {                System.out.println("操作符入栈:"+s);                operatorStack.push(OperatorEnum.LEFT_BRACKET.getOperator());            }            //如果是右括号,则革除栈中的运算符直至左括号            else if (s.equals(OperatorEnum.RIGHT_BRACKET.getOperator())) {                while (!OperatorEnum.LEFT_BRACKET.getOperator().equals(operatorStack.peek())) {                    //开始运算                    numStack.push(OperatorUtil.getInterpreter(numStack,operatorStack));                }                //运算完之后革除左括号                String pop = operatorStack.pop();                System.out.println("括号运算操作实现,革除栈中右括号:"+pop);            }            //如果是数字,则间接入数据的栈            else {                //将数字字符串转换成数字,而后存入栈中                NumInterpreter numInterpreter = new NumInterpreter(Integer.valueOf(s));                System.out.println("数字入栈:"+s);                numStack.push(numInterpreter);            }        }        //最初当栈中不是空的时候持续运算,直到栈为空即可        while (!operatorStack.isEmpty()) {            numStack.push(OperatorUtil.getInterpreter(numStack,operatorStack));        }    }    /**     * 计算结果出栈     * @return     */    public int calculate() {        return this.numStack.pop().interpret();    }    /**     * 换成规范模式,便于宰割     * @param expression     * @return     */    private String fromat(String expression) {        String result = "";        for (int i = 0; i < expression.length(); i++) {            if (expression.charAt(i) == '(' || expression.charAt(i) == ')' ||                expression.charAt(i) == '+' || expression.charAt(i) == '-' ||                expression.charAt(i) == '*' || expression.charAt(i) == '/')                //在操作符与数字之间减少一个空格                result += (" " + expression.charAt(i) + " ");            else                result += expression.charAt(i);        }        return result;    }}

此时,再来看客户端测试代码。

public static void main(String[] args) {        System.out.println("result: " + new GPCalculator("10+30/((6-4)*2-2)").calculate());}

运行失去预期的后果,如下图所示。

2 解释器模式在JDK源码中的利用

先来看JDK源码中的Pattern对正则表达式的编译和解析。

public final class Pattern implements java.io.Serializable {    ...    private Pattern(String p, int f) {        pattern = p;        flags = f;        if ((flags & UNICODE_CHARACTER_CLASS) != 0)            flags |= UNICODE_CASE;             capturingGroupCount = 1;        localCount = 0;        if (pattern.length() > 0) {            compile();        } else {            root = new Start(lastAccept);            matchRoot = lastAccept;        }    }    ...    public static Pattern compile(String regex) {        return new Pattern(regex, 0);    }    public static Pattern compile(String regex, int flags) {        return new Pattern(regex, flags);    }    ...}

3 解释器模式在Spring源码中的利用

再来看Spring中的ExpressionParser接口。

public interface ExpressionParser {    Expression parseExpression(String expressionString) throws ParseException;    Expression parseExpression(String expressionString, ParserContext context) throws ParseException;}

这里咱们不深刻解说源码,通过咱们后面编写的案例大抵可能分明其原理。无妨编写一段客户端代码验证一下。客户端测试代码如下。

    public static void main(String[] args) {        ExpressionParser parser = new SpelExpressionParser();        Expression expression = parser.parseExpression("100 * 2 + 400 * 1 + 66");        int result = (Integer) expression.getValue();        System.out.println("计算结果是:" + result);    }        

运行后果如下图所示。

由上图可知,运行后果与预期的后果是统一的。

关注微信公众号『 Tom弹架构 』回复“设计模式”可获取残缺源码。

【举荐】Tom弹架构:30个设计模式实在案例(附源码),挑战年薪60W不是梦
本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我高兴!
如果本文对您有帮忙,欢送关注和点赞;如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源。关注微信公众号『 Tom弹架构 』可获取更多技术干货!