javascript设计模式之解释器模式详解

前端技术 2023/09/02 JavaScript


神马是“解释器模式”?

先翻开《GOF》看看Definition:
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

在开篇之前还是要科普几个概念:

抽象语法树:

解释器模式并未解释如何创建一个抽象语法树。它不涉及语法分析。抽象语法树可用一个表驱动的语法分析程序来完成,也可用手写的(通常为递归下降法)语法分析程序创建,或直接client提供。

解析器:

指的是把描述客户端调用要求的表达式,经过解析,形成一个抽象语法树的程序。

解释器:

指的是解释抽象语法树,并执行每个节点对应的功能的程序。

要使用解释器模式,一个重要的前提就是要定义一套语法规则,也称为文法。不管这套文法的规则是简单还是复杂,必须要有这些规则,因为解释器模式就是按照这些规则来进行解析并执行相应的功能的。

先来看看解释器模式的结构图和说明:



AbstractExpression:定义解释器的接口,约定解释器的解释操作。
TerminalExpression:终结符解释器,用来实现语法规则中和终结符相关的操作,不再包含其他的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的叶子对象,可以有多种终结符解释器。
NonterminalExpression:非终结符解释器,用来实现语法规则中非终结符相关的操作,通常一个解释器对应一个语法规则,可以包含其他的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的组合对象。可以有多种非终结符解释器。
Context:上下文,通常包含各个解释器需要的数据或是公共的功能。
Client:客户端,指的是使用解释器的客户端,通常在这里将按照语言的语法做的表达式转换成为使用解释器对象描述的抽象语法树,然后调用解释操作。

下面我们通过一个xml示例来理解解释器模式:

首先要为表达式设计简单的文法,为了通用,用root表示根元素,abc等来代表元素,一个简单的xml如下:

复制代码 代码如下:

<?xml version=\"1.0\" encoding=\"UTF-8\">
  <root id=\"rootId\">
      <a>
          <b>
              <c name=\"testC\">12345</c>
              <d id=\"1\">d1</d>
              <d id=\"2\">d2</d>
              <d id=\"3\">d3</d>
              <d id=\"4\">d4</d>
          </b>
      </a>
  </root>

约定表达式的文法如下:

1.获取单个元素的值:从根元素开始,一直到想要获取取值的元素,元素中间用“/”分隔,根元素前不加“/”。比如,表达式“root/a/b/c”就表示获取根元素下,a元素下,b元素下,c元素的值。
2.获取单个元素的属性的值:当然是多个,要获取值的属性一定是表达式的最后一个元素的属性,在最后一个元素后面添加“.”然后再加上属性的名称。比如,表达式“root/a/b/c.name”就表示获取根元素下,a元素下,b元素下,c元素的name属性的值。
3.获取相同元素名称的值,当然是多个,要获取值的元素一定是表达式的最后一个元素,在最后一个元素后面添加“$”。比如,表达式“root/a/b/d$”就表示获取根元素下,a元素下,b元素下的多个d元素的值的集合。
4.获取相同元素名称的属性的值,当然也是多个:要获取属性值的元素一定是表达式的最后一个元素,在最后一个元素后面添加\"$\"。比如,表达式“root/a/b/d$.id$”就表示获取根元素下,a元素下,b元素下的多个d元素的id属性的值的集合。

上面的xml,对应的抽象语法树,可能的结构如图:



下面我们来看看具体的代码:

1.定义上下文:

复制代码 代码如下:

/**
 * 上下文,用来包含解释器需要的一些全局信息
 * @param {String} filePathName [需要读取的xml的路径和名字]
 */
function Context(filePathName) {
    // 上一个被处理元素
    this.preEle = null;
    // xml的Document对象
    this.document = XmlUtil.getRoot(filePathName);
}

Context.prototype = {
    // 重新初始化上下文
    reInit: function () {
        this.preEle = null;
    },
    /**
     *  各个Expression公共使用的方法
     * 根据父元素和当前元素的名称来获取当前元素
     * @param  {Element} pEle    [父元素]
     * @param  {String} eleName [当前元素名称]
     * @return {Element|null}         [找到的当前元素]
     */
    getNowEle: function (pEle, eleName) {
        var tempNodeList = pEle.childNodes;
        var nowEle;

        for (var i = 0, len = tempNodeList.length; i < len; i++) {
            if ((nowEle = tempNodeList[i]).nodeType === 1)
                if (nowEle.nodeName === eleName)
                    return nowEle;
        }

        return null;
    },
    getPreEle: function () {
        return this.preEle;
    },
    setPreEle: function (preEle) {
        this.preEle = preEle;
    },
    getDocument: function () {
        return this.document;
    }
};

本文地址:https://www.stayed.cn/item/3336

转载请注明出处。

本站部分内容来源于网络,如侵犯到您的权益,请 联系我

我的博客

人生若只如初见,何事秋风悲画扇。