定义:
定义特定的文法,对特定的某一类语言进行解析处理,如,运算表达式、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()方法进行一步步解析,最终运算出结果。
总结:
将想要解析的文本表达式做出一个分析,通常可分为终结符和非终结符,就类似于这个例子中的操作符(终结符)、运算值(非终结符),而他们都是实现了符号表达式接口,并通过重写接口的逻辑处理方法,并传入符号表达式对象的集合,在方法中从集合中取出表达式对象,对象又继续调用了这个逻辑处理方法,实现了递归调用,直至符号表达式对象结束。其核心的思想就是递归调用,一步一步递归将每一个符号表达式对象解析,执行对应的逻辑处理。
应用场景:
一些重复出现的问题可以用一种简单的语言来表达,可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
优缺点:
可以看到,这样的方式对特定文本的解析可以对解析方式较容易地进行扩展,如:修改运算法则,则只需要对相应的操作符类进行改变逻辑处理;添加操作符,则只需要创建一个操作符类,在解析器中对这个操作符进行解析判断就行了。其缺点就是容易造成代码复杂度增加,且效率在递归调用过程中明显不高。