简述-解释器模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ddxxii/article/details/84108385

介绍

一种用得比较少的行为型模式。其提供一种解释语言的语法或表达式的方式,化繁为简。比如给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器用来解释语言中的句子。Android中的AndroidManifest.xml的解析,PackageParser就用到了这种模式。

场景

  • 简单语言需要解释执行且可以将该语言的语句表示为抽象语法树(如±法运算)
  • 特定领域出现不断重复,可以将该问题转换为一种语法规则。

UML

  • AbstractExpression: 抽象表达式,生命一个抽象的解释操作父类,并定义一个抽象的解释方法,其具体的实现在子类解释器完成。
  • TerminalExpression:终结符表达式。实现文法终与终结符有关的解释操作。文法中每一个终结符都有一个具体的终结表达式与之对应,
  • NonterminalExpression: 非终结符表达式。实现文法中与非终结符有关的解释操作。
  • Context:上下文环境类。
  • Client:客户类。

事例

举一个简单的例子,比如"3 + 5 + 7",构建一个语法来进行解析和计算。

这里,咱们将3,5,7可看为终结符,+号可看为非终结符

  1. 先建立一个抽象的解析类
/**
 * 抽象解析类
 */
public abstract class AbsPrease {
    /**
     * 抽象解析方法
     * 具体解析由子类实现
     */
    public abstract int interpret();
}
  1. 创建数字的解析(终结符)
/**
 * 数字解析
 */
public class NumPrease extends AbsPrease {
    /**
     * 承载的数字
     */
    private int num;

    public NumPrease(int num) {
        this.num = num;
    }

    /**
     * 数字的解析类直接返回数字
     *
     * @return
     */
    @Override
    public int interpret() {
        System.out.println("数字解析:num="+num);
        return num;
    }
}

数字的解析类,就包含一个数字,计算的时候就返回数字即可。

  1. 创建一个抽象的操作符解析(非终结符),之所以是抽象,目的是后面还可以扩展成其他操作符
/**
 * 操作符解析类,还是用抽象类,后续可以再继承,实现不同操作
 */
public abstract class OperatPrease extends AbsPrease {
    /**
     * 操作符左侧的解析
     */
    protected AbsPrease numLeft;
    /**
     * 操作符右侧解析类
     */
    protected AbsPrease numRight;

    public OperatPrease(AbsPrease numLeft, AbsPrease numRight) {
        this.numLeft = numLeft;
        this.numRight = numRight;
    }
}

这里的持有运算符两边解析器

  1. 创建加法解析器继承自操作符解析器
/**
 * 加法操作符实现类,继承操作解析类
 */
public class AddOpeartionPrease extends OperatPrease {

    /**
     * 传入左右数字
     *
     * @param numLeft
     * @param numRight
     */
    public AddOpeartionPrease(AbsPrease numLeft, AbsPrease numRight) {
        super(numLeft, numRight);
    }

    @Override
    public int interpret() {
        //返回两个数字相加
        System.out.println("加法解析数字解析");
        return numLeft.interpret() + numRight.interpret();
    }
}

顾名思义,计算左右解析的相加

  1. 定义一个承载业务的对象

/**
 * 计算类,用于承载一些业务
 */
public class CalculatePrase {
//存储相关解析器,在这里至始至终里面都只存了一个解析器(存了取,取了存)
    private Stack<AbsPrease> stack = new Stack<>();

    public CalculatePrase() {
    }

    /**
     * 解析计算字符串,并返回计算后的结构
     *
     * @param expression 字符串 如 "1 + 2 + 3"
     * @return 计算后的结果
     */
    public int interpret(String expression) {
        /*定义两个临时变量*/
        AbsPrease absPrease1;
        AbsPrease absPrease2;
        //以空格将文字分开,得到数字和运算符
        String[] elements = expression.split(" ");
        for (int i = 0; i < elements.length; i++) {
            switch (elements[i]) {
                //加号
                case "+":
                    //将当前存储的解析给拿出来
                    absPrease1 = stack.pop();
                    //获取到加号之后的一个解析
                    absPrease2 = new NumPrease(Integer.valueOf(elements[++i]));
                    //将之前存储的数字与当前加号后的一个数字再放入栈中
                    stack.push(new AddOpeartionPrease(absPrease1, absPrease2));
                    break;
                //数字
                default:
                    //数字直接入栈
                    stack.push(new NumPrease(Integer.valueOf(elements[i])));
                    break;
            }
        }

        //前面解析完成之后,再进行一次性计算打印
        return stack.pop().interpret();
    }
}

其接收字符串,并通过解析来得到最终结果。

  1. 测试
System.out.println("计算结果:" + new CalculatePrase().interpret("1 + 2 + 5"));
  1. 输出:
加法解析数字解析
加法解析数字解析
数字解析:num=1
数字解析:num=2
数字解析:num=5
计算结果:8

为什么这么输出,最后栈中的唯一一个AbsPrase为如下,我想大家已经懂了,如果再多,就是不断嵌套:

在这里解释器的优势也出来了,好扩展,咱们可以再扩展减法的,只需要创建一个减法的解释,然后再改一下CalculatePrase中的case即可

优缺点

优点:

  • 灵活的扩展性,需要对文法进行扩展对时候,只需要加响应对非终结符解释器,在构件抽象语法树时候,使用新增对解释器对象进行具体对解释即可

缺点:

  • 每一条文法都对应一个解释器,会生成大量对类,后期维护困难。
  • 复杂文法就不适合了,构建很困难。

总结:其思想就是通过固定的解释语法来将一个约定好语法的整体(比如这里的运算字符串,其他如xml中的内容)解析出我们想要的理解的结果,构建一个这个确实是比较复杂,平时几乎也不会用到。

猜你喜欢

转载自blog.csdn.net/ddxxii/article/details/84108385
今日推荐