TypechoJoeTheme

IT技术分享

统计

解释器模式(Interpreter)——23种设计模式之行为型模式

2016-04-21
/
0 评论
/
630 阅读
/
正在检测是否收录...
04/21

在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普遍的编程方式来实现将面临非常频繁的变化。在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。

如果一种特定类型的问题发生的频率足够高, 那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释 这些句子来解决该问题。解释器模式描述了如何为简 单的语言定义一个文法并解释。

解释器模式描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发编译器中,当文法简单、效率不是关键问题的时候效果最好。

在实际应用中,可能很少碰到去构造一个语言 的文法的情况。

一、类图表示

解释器模式涉及到AbstractExpression(抽象表达式),TerminalExpression(终结符表达式),NonterminalExpression(非终结符表达式),Context(环境类),Client(客户类)

  • ①、AbstractExpression(抽象表达式):声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享
  • ②、TerminalExpression(终结符表达式):实现与文法中的终结符相关联的解释操作,一个句子中的每一个终结符需要该类的一个实例
  • ③、NonterminalExpression(非终结符表达式):对文法中的每一条规则R ::= R1R2. . . Rn都需要一个NonterminalExpression类,为从R1到Rn的每个符号都维护一个AbstractExpression类型的实例变量为文法中的非终结符实现Interpret操作,一般要递归
    地调用表示R1到Rn的那些对象的解释操作
  • ④、Context(环境类):包含解释器之外的一些全局信息
  • ⑤、Client(客户类):建造一个语法抽象树,调用interpret()

java-interpreter-model-1

二、代码示例

//上下文(环境)角色,使用HashMap来存储变量对应的数值

class Context{

    private Map valueMap = new HashMap();

    public void addValue(Variable x , int y){

        Integer yi = new Integer(y);

        valueMap.put(x , yi);

    }

    public int LookupValue(Variable x){

        int i = ((Integer)valueMap.get(x)).intValue();

        return i ;

    }

}
//抽象表达式角色,也可以用接口来实现

abstract class Expression{

    public abstract int interpret(Context con);

}
//终结符表达式角色

class Constant extends Expression{

    private int i ;

    public Constant(int i){

        this.i = i;

    }

    public int interpret(Context con){

        return i ;

    }

}
class Variable extends Expression{

    public int interpret(Context con){

        //this为调用interpret方法的Variable对象

        return con.LookupValue(this);

    }

}
//非终结符表达式角色

class Add extends Expression{

    private Expression left ,right ;

    public Add(Expression left , Expression right){

        this.left = left ;

        this.right= right ;

    }

    public int interpret(Context con){

        return left.interpret(con) + right.interpret(con);

    }

}
class Subtract extends Expression{

    private Expression left , right ;

    public Subtract(Expression left , Expression right){

        this.left = left ;

        this.right= right ;

    }

    public int interpret(Context con){

        return left.interpret(con) - right.interpret(con);

    }

}
class Multiply extends Expression{

    private Expression left , right ;

    public Multiply(Expression left , Expression right){

        this.left = left ;

        this.right= right ;

    }

    public int interpret(Context con){

        return left.interpret(con) * right.interpret(con);

    }

}
class Division extends Expression{

    private Expression left , right ;

    public Division(Expression left , Expression right){

        this.left = left ;

        this.right= right ;

    }

    public int interpret(Context con){

        try{

            return left.interpret(con) / right.interpret(con);

        }catch(ArithmeticException ae){

            System.out.println("被除数为0!");

            return -11111;

        }

    }

}
//测试程序,计算 (a*b)/(a-b+2)

public class Test{

    private static Expression ex ;

    private static Context con ;

    public static void main(String[] args){

        con = new Context();

        //设置变量、常量

        Variable a = new Variable();

        Variable b = new Variable();

        Constant c = new Constant(2);

        //为变量赋值

        con.addValue(a , 5);

        con.addValue(b , 7);

        //运算,对句子的结构由我们自己来分析,构造

        ex = new Division(new Multiply(a , b), new Add(new Subtract(a , b) , c));

        System.out.println("运算结果为:"+ex.interpret(con));

    }

}

三、模式分析

每一非终结符表达式节点定义相应子表达式的解释操作。而各终结符表达式的解释操作构成了递归的基础。

  • ①、每一节点的解释操作用上下文来存储和访问解释器的状态。
  • ②、抽象语法树描述了如何构成一个复杂的句子,通 过对抽象语法树的分析,可以识别出语言中的终结符和非终结符类。
  • ③、在解释器模式中,每一种终结符和非终结符都有 一个具体类与之对应,正因为使用类来表示每一 个语法规则,使得系统具有较好的扩展性和灵活 性。

四、优缺点分析

1、优点

  • ①、易于改变和扩展文法,也易于实现文法
  • ②、使用Interpreter模式来表示文法规则,从而可以使用 面向对象技巧来方便地“扩展”文法。
  • ③、解释器模式提供了一个简单的方式来执行语法,而且容易修改或者扩展语法。
  • ④、一般系统中很多类使用相似的语法,可以使用一个解释器来代替为每一个规则实现一个解释器。而且在解释器中不同的规则是由不同的类来实现的,这样使得添加一个新的语法规则变得简单。

2、缺点

  • ①、采用递归调用方法,复杂的文法难以维护,增加了新的解释表达式的方式
  • ②、会引起类膨胀。
  • ③、每一个规则要对应一个处理类,而且这些类还要递归调用抽象表达式 角色。调试复杂。
  • ④、使用大量循环与递归,效率是问题,尤其是一用于解析复杂/冗长的语法时,效率难以忍受。

五、模式间关系

1、与组合模式的关系

  • ①、抽象语法树是一个组合模式的实例

2、与享元模式的关系

  • ①、享元模式说明了如何在抽象语法树中共享终结符

3、与迭代器模式的关系

  • ①、解释器可用一个迭代器遍历该结构

4、与访问者模式的关系

  • ①、可用来在一个类中维护抽象语法树中的 各节点的行为

六、应用场景

当有一个语言需要解释执行,并且你可将该语言中的句 子表示为一个抽象语法树时,可使用解释器模式。

  • ②、当该文法简单,效率不是一个关键问题时,系统有一个简单的语言可供解释时该模式效果最好
  • ③、一些重复发生的问题可以用这种简单的语言表达时
  • ④、解释器模式在使用面向对象语言实现的编译器中得到 了广泛应用,如smalltalk编译器。

 七、总结

一般用于开发编译器中,特定领域问题发生频率特别高。

  • ①、Interpreter模式的应用场合是interpreter模式应用中的 难点,只有满足"业务规则频繁变化,且类似的模式 不断重复出现,并且容易抽象为语法规则的问题"才 适合使用Interpreter模式
  • ②、Interpreter模式比较适合简单的文法表示,对于复杂 的文法表示,Interpreter模式会产生比较大的类层次 结构,需要求助于语法分析生成器这样的标准工具
  • ③、在最宽泛的概念下,几乎每个使用组合模式的系统都使用了解释器模式。但一般只有在用一个类层次来定 义某个语言时,才强调使用解释器模式。
  • ④、解释器模式在实际的系统开发中用得较少,会引起效 率、性能及维护等问题,一般多在大中型的框架型项 目中使用,如一些数据分析工具、报表设计工具、科 学计算工具等,但现在有很多开源的解析工具包可直接使用。
朗读
赞 · 0
版权属于:

IT技术分享

本文链接:

https://idunso.com/archives/1887/(转载时请注明本文出处及文章链接)