设计模式-解析器模式

定义:

定义特定的文法,对特定的某一类语言进行解析处理,如,运算表达式、SQL以及Android中的xml文件解析等有固定语法的语言。

实现方式

这种模式在实际开发中开发人员较少用到,也可以说是较少需要自己自定义解析器的,但他在开发中的应用场景却不少,了解解析器模式可以更方便地去了解架构源码,了解及解析过程更方便与对已有功能的扩展,比如说在加减运算法则中添加乘除运算。

其通用实现模式如下:


1.首先创建特定文法(抽象表达式)接口:
public abstract class Expression { 
    public boolean interpret(String context); 
}

2.创建表达式接口的实现类(终结符表达式):
public class TerminalExpression extend Expression {
    private String data;
    public TerminalExpression(String data){
        this.data = data;
    }
    @Override 
    public Object interpret(String context) {
        //逻辑处理
        return null;
    }
}

//非终结符表达式
public class NonterminalExpression extends Expression { 
    public NonterminalExpression(Expression ...expressions) { } 
    @Override Object interpreter(Context ctx) { 
        return null; 
    }
}

//可以创建更多的的实现类用于处理特定语法中不同关键字
复制代码

举例说明

这里以运算表达式为例:

public abstract class Expression {
    int interpret(String info);
}

//变量解析器
public class VariableExpression extend Expression {
    private String key;
    VariableExpression(String key) {
        this.key = key;
    }
    @Override
    public int interpreter(HashMap<String, Integer> var) { 
        return var.get(key); 
    }
}

//运算符解析器
public abstract class OperatorExpression extend Expression {
    private Expression varLeft;//运算符左变量
    private Expression varRight;//运算符右变量
    OperatorExpression(Expression varLeft, Expression varRight) {
        this.varLeft = varLeft;
        this.varRight = varRight;
    }
}

//加法运算符解析器
public class AndExpression extend OperatorExpression {
    AndExpression(Expression varLeft, Expression varRight) {
        super(varLeft, varRight)
    }
    
    @Override
    public int interpreter(HashMap<String, Integer> var) { 
        return super.varLeft.interpreter(var) + super.varRight.interpreter(var); 
    }
    
}

//减法法运算符解析器
//加法运算符解析器
public class SubExpression extend OperatorExpression {
    SubExpression(Expression varLeft, Expression varRight) {
        super(varLeft, varRight)
    }
    @Override
    public int interpreter(HashMap<String, Integer> var) { 
        return super.varLeft.interpreter(var) - super.varRight.interpreter(var); 
    }
}


public class Client {

    public static void main(String[] args) throws IOException {
        //表达式
        String expStr = "a+b-c";
        
        //a、b、c对应的值放到operatorMap中
        HashMap<String, Integer> varMap;//变量数据
        
        //对表达式进行解析
        Expression expression = parse(expStr);
        
        //运算结果
        int result = expression.interpreter(varMap);
    }
    
    
    //定义对表达式进行解析的方法(解析器的核心方法,也可以说是真正的解析器)
    private static Expression parser(String expStr) {
        
        Stack<Expression> stack = new Stack<>(); 
        char[] charArray = expStr.toCharArray(); 
        Expression left = null; 
        Expression right = null; 
        for (int i = 0; i < charArray.length; i++) { 
        switch (charArray[i]) { 
            case '+': left = stack.pop(); 
                      right = new VariableExpression(String.valueOf(charArray[++i])); 
                      stack.push(new AddExpression(left, right)); 
                      break; 
            case '-': 
                      left = stack.pop(); 
                      right = new VariableExpression(String.valueOf(charArray[++i])); 
                      stack.push(new SubExpression(left, right)); 
                      break; 
            default: 
                      stack.push(new VariableExpression(String.valueOf(charArray[i]))); 
                      break; 
            } 
        } 
        return expression = stack.pop(); 
    }
}
复制代码

在这里定义的特定文法就是一个运算法则(a+b-c),用户根据这个文法输入对应的a、b、c的值,当用户输入了运算法则,接着需要对法则进行解析,其中会把运算符号解析,并标记其左右运算值,以栈的数据结构对其存储,然后进行类似于递归的调用interpreter()方法进行一步步解析,最终运算出结果。

总结:

将想要解析的文本表达式做出一个分析,通常可分为终结符和非终结符,就类似于这个例子中的操作符(终结符)、运算值(非终结符),而他们都是实现了符号表达式接口,并通过重写接口的逻辑处理方法,并传入符号表达式对象的集合,在方法中从集合中取出表达式对象,对象又继续调用了这个逻辑处理方法,实现了递归调用,直至符号表达式对象结束。其核心的思想就是递归调用,一步一步递归将每一个符号表达式对象解析,执行对应的逻辑处理。

应用场景:

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

优缺点:

可以看到,这样的方式对特定文本的解析可以对解析方式较容易地进行扩展,如:修改运算法则,则只需要对相应的操作符类进行改变逻辑处理;添加操作符,则只需要创建一个操作符类,在解析器中对这个操作符进行解析判断就行了。其缺点就是容易造成代码复杂度增加,且效率在递归调用过程中明显不高。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享