GOF设计模式-对象行为型模式-解释器模式

26 篇文章 0 订阅
20 篇文章 0 订阅

自定义语言的实现——解释器模式

      为人处事是一门大学问,察言观色、听懂弦外之音都是非常重要的,老板跟你说“XX你最近表现平平啊,还得要多努力”,如果你不当回事,平常对待,可能下次就是“XX,恩,你人还是不错,平常工作也很努力,但是我想这份工作可能不是很适合你…..”。又比如你老大说“XX,你最近表现不错,工作积极性很高啊!继续保持啊!”,你高兴乐呵着心想是不是老板要给我加工资了,可能你等到花都谢了也没有,得到的可能会是更多的工作量。对于我们刚刚入社会的人不够圆滑,不会察言观色,更听不懂老板的弦外之音,所以我们期待如果有一个翻译机该多好,直接将别人的弦外之音给翻译出来就好了。

      在我们实际的生活中是这样,在软件的世界里也同样存在着翻译机,只不过在软件中我们称之为解释器。在系统中如果某一特定类型的问题在频繁的发生,此时我们就有必要将这些问题的实例表述为一个语言中句子,因此可以构建一个解释器,然后利用该解释器来解释这些句子来解决这些问题。

 

我们知道,像C++、Java和C#等语言无法直接解释类似“1+ 2 + 3 – 4 + 1”这样的字符串(如果 直接作为数值表达式时可以解释),我们必须自己定义一套文法规则来实现对这些语句的解 释,即设计一个自定义语言。在实际开发中,这些简单的自定义语言可以基于现有的编程语 言来设计,如果所基于的编程语言是面向对象语言,此时可以使用解释器模式来实现自定义 语言。

 

 

解释器模式概述

解释器模式是一种使用频率相对较低但学习难度较大的设计模式,它用于描述如何使用面向 对象语言构成一个简单的语言解释器。在某些情况下,为了更好地描述某一些特定类型的问 题,我们可以创建一种新的语言,这种语言拥有自己的表达式和结构,即文法规则,这些问 题的实例将对应为该语言中的句子。此时,可以使用解释器模式来设计这种新的语言。对解 释器模式的学习能够加深我们对面向对象思想的理解,并且掌握编程语言中文法规则的解释 过程。
解释器模式定义如下: 解释器模式(Interpreter Pattern):定义一个语言的文法,并且建立一个 解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。解释器模式是 一种类行为型模式。

解释器模式结构图:

 

在解释器模式结构图中包含如下几个角色:

● AbstractExpression(抽象表达式):在抽象表达式中声明了抽象的解释操作,它是所有终结 符表达式和非终结符表达式的公共父类。

● TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法 中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例。通常在一个 解释器模式中只有少数几个终结符表达式类,它们的实例可以通过非终结符表达式组成较为 复杂的句子。
● NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实 现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以 继续包含非终结符表达式,因此其解释操作一般通过递归的方式来完成。
● Context(环境类):环境类又称为上下文类,它用于存储解释器之外的一些全局信息,通 常它临时存储了需要解释的语句。
在解释器模式中,每一种终结符和非终结符都有一个具体类与之对应,正因为使用类来表示 每一条文法规则,所以系统将具有较好的灵活性和可扩展性。对于所有的终结符和非终结 符,我们首先需要抽象出一个公共父类,即抽象表达式类

所谓解释器模式就是定义语言的文法,并且建立一个解释器来解释该语言中的句子。

 

解释器模式描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发的编译器中。它描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。

在解释器模式中除了能够使用文法规则来定义一个语言,还有通过一个更加直观的方法来表示——使用抽象语法树。抽象语法树能够更好地,更直观地表示一个语言的构成,每一颗抽象语法树对应一个语言实例。

 

举个栗子:

现在我们用解释器模式来实现一个基本的加、减、乘、除和求模运算。例如用户输入表达式“3 * 4 / 2 % 4”,输出结果为2。下图为该实例的UML结构图:

 

抽象语法树:

 

 实现过程:

抽象表达式:

Node.java。

public interface Node
{
    public int interpret();
}

 非终结表达式:ValueNode.java。主要用解释该表达式的值。

public class ValueNode implements Node
{
    private int value;
    
    public ValueNode(int value)
    {
        this.value=value;
    }
        
    public int interpret()
    {
        return this.value;
    }
}

 终结表达式抽象类,由于该终结表达式需要解释多个运算符号,同时用来构建抽象语法树:

 

public abstract class SymbolNode implements Node
{
    protected Node left;
    protected Node right;
    
    public SymbolNode(Node left,Node right)
    {
        this.left=left;
        this.right=right;
    }
}

MulNode.java

 

public class MulNode extends SymbolNode
{
    public MulNode(Node left,Node right)
    {
        super(left,right);
    }
    
    public int interpret()
    {
        return left.interpret() * right.interpret();
    }
}

ModNode.java

 

public class ModNode extends SymbolNode{
    public ModNode(Node left,Node right){
        super(left,right);
    }
    
    public int interpret(){
        return super.left.interpret() % super.right.interpret();
    }
}

DivNode.java

 

public class DivNode extends SymbolNode{
    public DivNode(Node left,Node right){
        super(left,right);
    }
    
    public int interpret(){
        return super.left.interpret() / super.right.interpret();
    }
}


Calculator.java

import java.util.Stack;

public class Calculator {
    private String statement;
    private Node node;

    public void build(String statement) {
        Node left = null, right = null;
        Stack stack = new Stack();

        String[] statementArr = statement.split(" ");

        for (int i = 0; i < statementArr.length; i++) {
            if (statementArr[i].equalsIgnoreCase("*")) {
                left = (Node) stack.pop();
                int val = Integer.parseInt(statementArr[++i]);
                right = new ValueNode(val);
                stack.push(new MulNode(left, right));
            } else if (statementArr[i].equalsIgnoreCase("/")) {
                left = (Node) stack.pop();
                int val = Integer.parseInt(statementArr[++i]);
                right = new ValueNode(val);
                stack.push(new DivNode(left, right));
            } else if (statementArr[i].equalsIgnoreCase("%")) {
                left = (Node) 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 = (Node) stack.pop();
    }

    public int compute() {
        return node.interpret();
    }
}


客户端:Client.java

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

运行结果:3 * 2 * 4 / 6 % 5 = 4

 

解释器模式总结

解释器模式为自定义语言的设计和实现提供了一种解决方案,它用于定义一组文法规则并通 过这组文法规则来解释语言中的句子。虽然解释器模式的使用频率不是特别高,但是它在正 则表达式、XML文档解释等领域还是得到了广泛使用。与解释器模式类似,目前还诞生了很 多基于抽象语法树的源代码处理工具,例如Eclipse中的Eclipse AST,它可以用于表示Java语言 的语法结构,用户可以通过扩展其功能,创建自己的文法规则。
1. 主要优点
解释器模式的主要优点如下:
(1) 易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过 继承等机制来改变或扩展文法。
(2) 每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
(3) 实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类 的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
(4) 增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新 的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”。
1. 主要缺点
解释器模式的主要缺点如下:
(1) 对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一 个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考 虑使用语法分析程序等方式来取代解释器模式。
(2) 执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂 的句子时其速度很慢,而且代码的调试过程也比较麻烦。

 

图片及源码详细信息参考博客:https://www.cnblogs.com/chenssy/p/3346427.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值