软件设计模式学习(十九)解释器模式

注释器是一种不常使用的设计模式,它用于形貌若何组成一个简朴的语言注释器,主要应用于使用面向工具语言开发的编译器和注释器设计。当我们需要开发一个新的语言时,可以思量使用注释器模式

模式念头

若是在系统中某一特定类型的问题发生的频率很高,此时可以思量将这些问题的实例表述为一个语言中的句子。再构建一个注释器,注释器通过注释这些句子来解决对应的问题。

举个例子,我们希望系统提供一个功效来支持一种新的加减法表达式语言,当输入表达式为 “1 + 2 + 3 – 4 + 1” 时,输出盘算效果为 3。为了实现上述功效,需要对输入表达式举行注释,若是不作注释,直接把 “1 + 2 + 3 – 4 + 1” 丢已往,现有的如 Java、C 之类的编程语言只会把它看成通俗的字符串,不可能实现我们想要的盘算效果。我们必须自己界说一套规则来实现该语句的注释,即实现一个简朴语言来注释这些句子,这就是注释器模式的模式念头。

模式界说

界说语言的文法,而且确立一个注释器来注释该语言中的句子,这里的 “语言” 意思是使用划定花样和语法的代码,它是一种类行为型模式。

模式结构

软件设计模式学习(十九)解释器模式

  1. AbstractExpression(抽象表达式)

    声明晰抽象的注释操作,它是所有终结符表达式和非终结符表达式的公共父类

  2. TerminalExpression(终结符表达式)

    抽象表达式的子类,实现了文法中的终结符相关联的注释操作,在句子中每一个终结符都是该类的一个实例。

  3. NonterminalExpression(非终结符表达式)

    也是抽象表达式的子类,实现了文法中的非终结符相关联的注释操作,非终结符表达式中可以包罗终结符表达式,也可以继续包罗非终结符表达式,因此其注释操作一样平常通过递归方式来完成。

  4. Context(环境类)

    环境类又称上下文类,它用于存储注释器之外的一些全局信息,通常它暂且存储了需要注释的语句。

  5. Client(客户类)

    客户类中组织了示意以划定文法界说的一个特定句子的抽象语法树,该抽象语法树由非终结符表达式和终结符表达式实例组合而成。在客户类中还将挪用注释操作,实现对句子的注释,有时候为了简化客户类代码,也可以将抽象语法树的组织事情封装到专门的类中完成,客户端只需提供待注释的句子并挪用该类的注释操作即可,该类可以称为注释器封装类

模式剖析

还是以之前提到的加减法表达式语言来举例,我们要为这门语言界说语法规则,可以使用如下文法来界说

expression ::= value | symbol
symbol ::= expression '+' expression | expression '-' expression
value ::= an integer	// 一个整数值

该文法规则包罗三条界说语句,第一句是表达式的组成方式,expression 是我们最终要获得的句子,假设是 “1 + 2 + 3 – 4 + 1″,那么该句的组成元素无非就是两种,数字(value)和运算符号(symbol),若是用专业术语来形貌的话,symbol 和 value 称为语法组织身分或语法单元。凭据句子界说,expression 要么是一个 value,要么是一个 symbol。

value 是一个终结符表达式,由于它的组成元素就是一个整数值,不能再举行剖析。与之对应的 symbol 则是非终结符表达式,它的组成元素依旧可以是表达式 expression,expression 又可以是 value 或者 symbol,即可以进一步剖析。

根据上述的文法规则,我们可以通过一种称之为抽象语法树(Abstract Syntax Tree)的图形方式来直观地示意语言的组成,每一颗抽象语法树对应一个语言实例,如 “1 + 2 + 3 – 4 + 1” 可以通过如图的抽象语法树来示意。

软件设计模式学习(十九)解释器模式

【XSS-labs】Level 1-5

每一个详细的语句都可以用类似的抽象语法树来示意,终结符表达式类的实例作为树的叶子节点,而非终结符表达式类的实例作为非叶子节点。抽象语法树形貌了若何组成一个庞大的句子,通过对抽象语法树的剖析,可以识别出语言中的终结符和非终结符类。

在注释器模式中,每一个终结符和非终结符都有一个详细类与之对应,正由于使用类来示意每一个语法规则,使得系统具有较好的扩展性和灵活性。对于所有的终结符和非终结符,首先要抽象出一个公共父类

public abstract class AbstractExpression {
    public abstract void interpret(Context ctx);
}

对于终结符表达式,其代码主要是对终结符元素的处置

public class TerminalExpression extends AbstractExpression {
    public void interpret(Context ctx) {
        // 对于终结符表达式的注释操作
    }
}

对于终结符表达式,其代码比较庞大,由于通过非终结符表达式可以将表达式组合成更庞大的结构。表达式可以通过非终结符毗邻在一起,对于两个操作元素的非终结符表达式,其典型代码如下

public class NonterminalExpression extends AbstractExpression {
    
    private AbstractExpression left;
    private AbstractExpression right;
    
    public NonterminalExpression(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }
    
    public void interpret(Context ctx) {
        // 递归挪用每一个组成部分的 interpret() 方式
        // 在递归挪用时指定组成部分的毗邻方式,即非终结符的功效
    }
}

通常在注释器模式中还提供了一个环境类 Context,用于存储一些全局信息,用于在举行详细的注释操作时从中获取相关信息。当系统无须提供全局公共信息时,可以省略环境类

public class Context {
    
    private HashMap map = new HashMap();
    
    public void assign(String key, String value) {
        // 往环境类中设值
    }
    public void lookup(String key) {
        // 获取存储在环境类中的值
    }
}

模式实例

现需组织一个语言注释器,使系统可以执行整数间的乘、除和求模运算。当用户输入表达式 “3 * 4 / 2 % 4″,输出效果为 2

  1. 抽象表达式类 Node(抽象节点)

    public interface Node {
        public int interpret();
    }
    
  2. 终结符表达式类 ValueNode(值节点类)

    public class ValueNode implements Node {
    
        private int value;
    
        public ValueNode(int value) {
            this.value = value;
        }
    
        @Override
        public int interpret() {
            return this.value;
        }
    }
    
  3. 抽象非终结符表达式类 SymbolNode(符号节点类)

    public abstract class SymbolNode implements Node {
    
        protected Node left;
        protected Node right;
    
        public SymbolNode(Node left, Node right) {
            this.left = left;
            this.right = right;
        }
    }
    
  4. 非终结符表达式类 MulNode(乘法节点类)

    public class MulNode extends SymbolNode {
    
        public MulNode(Node left, Node right) {
            super(left, right);
        }
    
        @Override
        public int interpret() {
            return super.left.interpret() * super.right.interpret();
        }
    }
    
  5. 非终结符表达式类 DivNode(除法节点类)

    public class DivNode extends SymbolNode {
    
        public DivNode(Node left, Node right) {
            super(left, right);
        }
    
        @Override
        public int interpret() {
            return super.left.interpret() / super.right.interpret();
        }
    }
    
  6. 非终结符表达式类 ModNode(求模节点类)

    public class ModNode extends SymbolNode {
    
        public ModNode(Node left, Node right) {
            super(left, right);
        }
    
        @Override
        public int interpret() {
            return super.left.interpret() % super.right.interpret();
        }
    }
    
  7. 注释器封装类 Calculator(盘算器类)

    Calculator 类是本实例的焦点类之一,Calculator 类中界说了若何组织一棵抽象语法树,在组织历程中使用了栈结构 Stack。通过一连串判断语句判断字符,若是是数字,实例化终结符表达式类 ValueNode 并压栈;若是判断为运算符号,则取出栈顶内容作为其左表达式,而将之后输入的数字封装在 ValueNode 类型的工具作为其右表达式,建立非终结符表达式 MulNode 类型的工具,最后将该表达式压栈。

    public class Calculator {
    
        private String statement;
        private Node node;
    
        public void build(String statement) {
    
            Node left = null, right = null;
            Stack<Node> stack = new Stack<Node>();
    
            String[] statementArr = statement.split(" ");
    
            for (int i = 0; i < statementArr.length; i++) {
                if (statementArr[i].equalsIgnoreCase(("*"))) {
                    left = stack.pop();
                    int val = Integer.parseInt(statementArr[++i]);
                    right = new ValueNode(val);
                    stack.push(new MulNode(left, right));
                } else if (statementArr[i].equalsIgnoreCase(("/"))) {
                    left = stack.pop();
                    int val = Integer.parseInt(statementArr[++i]);
                    right = new ValueNode(val);
                    stack.push(new DivNode(left, right));
                } else if (statementArr[i].equalsIgnoreCase(("%"))) {
                    left = stack.pop();
                    int val = Integer.parseInt(statementArr[++i]);
                    right = new ValueNode(val);
                    stack.push(new ModNode(left, right));
                } else {
                    stack.push(new ValueNode(Integer.parseInt(statementArr[i])));
                }
            }
            this.node = stack.pop();
        }
    
        public int compute() {
            return node.interpret();
        }
    }
    
  8. 客户端测试类 Client

    程序执行时将递归挪用每一个表达式类的 interpret() 的注释方式,最终完成对整棵抽象语法树的注释。

    public class Client {
    
        public static void main(String[] args) {
            String statement = "3 * 4 / 2 % 4";
            Calculator calculator = new Calculator();
            calculator.build(statement);
            int result = calculator.compute();
            System.out.println(statement + " = " + result);
        }
    }
    
  9. 运行效果

    软件设计模式学习(十九)解释器模式

模式优瑕玷

注释器模式优点如下:

  1. 易于改变和扩展文法。由于使用类来示意语言的文法规则,可以通过继续机制来改变或扩展文法。
  2. 易于实现文法。抽象语法树中每一个节点类的实现方式都是相似的,编写并不庞大。
  3. 增添了新的注释表达式的方式。增添新的表达式时无须对现有表达式类举行修改,相符开闭原则

注释器模式瑕玷如下:

  1. 对于庞大文法难以维护。
  2. 执行效率低。注释器模式使用了大量循环和递归挪用。
  3. 应用场景有限。

软件设计模式学习(十八)下令模式

原创文章,作者:admin,如若转载,请注明出处:https://www.2lxm.com/archives/11366.html