大卫的Design Patterns学习笔记15:Interpreter

一、概述
Interpreter(解释器)模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。在这里使用语言这个词似乎将Interpreter模式的应用范围限制到了一个过于狭小的范围,毕竟,我们不是自然语言或者编程语言设计者,需要注意的是,这里所讨论的语言并非指复杂的自然语言或者编程语言,而是一种语义标记,Interpreter模式负责实现这种标记的定义以及将其转换为实际功能,后面应用部分对此有进一步说明。

二、结构
Interpreter模式的结构如下:

1、Interpreter模式的类图示意
上述类图中Context用于包含一些解释器之外的全局信息,而Client则负责构建(或被给定)表示该文法定义的语言中一个特定的句子的抽象语法树。
从上述类图可以看出,Interpreter模式实际上只是Composite模式的针对特殊应用的一个特化版本,但这并不表示Interpreter模式的提出没有意义,Interpreter模式的提出使Composite模式扩展到了更深的领域 --语义转换,这一点有着一定的实际意义。
解释器模式使用类来表示每一条文法规则,通过类之间的组合来实现一定的语法规则。

三、应用
当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。
上面的说法未免有点过于formal,简单说来:
1.
当我们需要一个命令解释器以解释执行用户输入的指令时可以考虑使用Interpretor模式。
2.
当我们需要根据客户的输入对数据进行不同显示时可以考虑使用Interpretor模式。
<
Java Design Pattern : A Tutorial >一书给出了一个根据客户输入对数据进行不同形式输出的例子,很好地体现了以上几点。实质上,更多的情况下,我们可能通过组合客户选择(多项选择)得到一个命令串,交给专门的Interpretor进行解释执行,并将处理结果回显给客户,这样,可以很好地避免客户输入错误造成的不必要的复杂性。
当存在以下情况时Interpretor模式效果最好:
1
、文法简单的情况。对于复杂的文法,文法的类层次变得庞大而无法管理,此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式,这样可以节省空间而且还可能节省时间。
2
、效率不是一个关键问题的情况。最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下,转换器仍可用解释器模式实现,该模式仍是有用的。

四、优缺点
解释器模式提供了一个简单的方式来执行语法,而且容易修改或者扩展语法。
在解释器中不同的规则是由不同的类来实现的,这样使得添加一个新的语法规则变得简单。
正如应用部分所说,Interpretor模式比较适用于文法简单,并且对处理的效率要求较低的情况,由于Interpretor模式使用类来标示每一条文法规则,因此,当处理复杂文法时,各规则类之间的调用及组合关系将变得难以维护,效率也将大大降低。(那对于复杂的文法结构怎么办呢?)

五、举例
以下是一个运用Interpretor模式完成罗马数字到阿拉伯数字转换的例子,其中用到了后面将讲到的Template Method模式,注意,该程序只能处理万以下的数值转换,怎么将其改成可以支持万及以上数值转换,我还没有想清楚。

#include <vector>
#include <iostream>
using namespace std ;

// "Context"
class Context
{

private
:
    string input ;
    int
 output ;

public
:
    Context (  const string & input  ) : input (input ), output ( 0 ) { }
    friend class
 Expression ;
    friend
 ostream &  operator  << (ostream & os , Context & context )
    {

        return
 os  << context .output ;
    }
};


// "AbstractExpression"
class Expression
{

public
 :
    // Template Method
    void Interpret ( Context & context  )
    {

        string & input  = context .input ;
        int
& output  = context .output ;

        if
 ( 0  == input .length ())  return ;
        if
 (input .find (Nine ()) ==  0 )
        {

            output  +=  9  * Multiplier ();
            input  = input .substr ( 2 , input .length () -  2 );
        }

        else if
 (input .find (Four ()) ==  0 )
        {

            output  +=  4  * Multiplier ();
            input  = input .substr ( 2 , input .length () -  2 );
        }

        else if
 (input .find (Five ()) ==  0 )
        {

            output  +=  5  * Multiplier ();
            input  = input .substr ( 1 , input .length () -  1 );
        }

        while
 (input .find (One ()) ==  0 )
        {

            output  += Multiplier ();
            input  = input .substr ( 1 , input .length () -  1 );
        }
    }


    virtual const
 char * One () =  0 ;
    virtual const
 char * Four () =  0 ;
    virtual const
 char * Five () =  0 ;
    virtual const
 char * Nine () =  0 ;
    virtual
 int Multiplier () =  0 ;
};


// Thousand checks for the Roman Numeral M
// "TerminalExpression"
class ThousandExpression  :  public Expression
{

    // Methods
    const  char * One () {  return  "M" ; }
    const
 char * Four (){  return  " " ; }
    const
 char * Five (){  return  " " ; }
    const
 char * Nine (){  return  " " ; }
    int
 Multiplier () {  return  1000 ; }
};


// Hundred checks C, CD, D or CM
// "TerminalExpression"
class HundredExpression  :  public Expression
{

    // Methods
    const  char * One () {  return  "C" ; }
    const
 char * Four (){  return  "CD" ; }
    const
 char * Five (){  return  "D" ; }
    const
 char * Nine (){  return  "CM" ; }
    int
 Multiplier () {  return  100 ; }
};


// Ten checks for X, XL, L and XC
// "TerminalExpression"
class TenExpression  :  public Expression
{

    // Methods
    const  char * One () {  return  "X" ; }
    const
 char * Four (){  return  "XL" ; }
    const
 char * Five (){  return  "L" ; }
    const
 char * Nine (){  return  "XC" ; }
    int
 Multiplier () {  return  10 ; }
};


// One checks for I, II, III, IV, V, VI, VII, VIII, IX
// "TerminalExpression"
class OneExpression  :  public Expression
{

    // Methods
    const  char * One () {  return  "I" ; }
    const
 char * Four (){  return  "IV" ; }
    const
 char * Five (){  return  "V" ; }
    const
 char * Nine (){  return  "IX" ; }
    int
 Multiplier () {  return  1 ; }
};


int
 main ()
{

    string roman ( "MCMXXVIII" );
    Context context (roman );

    // Build the 'parse tree'
    Expression * exp [] = {
        new
 ThousandExpression (),  new HundredExpression (),
        new
 TenExpression (),  new OneExpression ()};
    vector <Expression *> v_exp (exp , exp  +  sizeof (exp ) /  sizeof (Expression *));
    vector <Expression *>::iterator it  = v_exp .begin ();
    for
 (; it  != v_exp .end (); it ++)
    {
        (*
it )->Interpret (context );
        delete
 (*it );
    }


    cout  << roman .c_str () <<  "="  << context  << endl ;
}


参考:
1
、http : //www.dofactory.com/Patterns/PatternInterpreter.aspx
现代C++中的设计模式是用于对象重用的可重复性方法。设计模式是一种在不同情况下解决相似问题的经验总结,可以通过将问题解决方案的关键部分抽象出来,从而提供灵活性和可重用性。设计模式不是编程语言特定的功能,而是一种通用的方法论。 在现代C++中,有许多常用的设计模式可以用于对象的可重用性。以下是几个常见的设计模式示例: 1.单例模式:用于确保一个类只能创建一个实例,并提供对该实例的全局访问点。对于有些对象只需要一个实例的情况,单例模式可以确保该实例的唯一性,从而方便访问和管理。 2.工厂模式:用于创建对象的过程中封装创建逻辑,让客户端代码无需关心对象的具体创建细节。通过工厂模式,可以通过一个工厂类来创建对象,从而提供更高的灵活性和可扩展性。 3.观察者模式:用于对象之间的发布-订阅机制,让一个对象(主题)的状态发生变化时,能够通知并自动更新其他依赖于该对象的对象(观察者)。通过观察者模式,可以实现对象之间的松耦合和消息传递,提高对象的可重用性和可维护性。 4.适配器模式:用于将一个类的接口转换成客户端所期望的另一个接口。适配器模式可以解决接口不兼容的问题,从而使得原本不兼容的类能够一起工作,提高可重用性和互操作性。 5.策略模式:用于定义一系列算法/行为,并将其封装成独立的类,使得它们可以互相替换。策略模式可以在运行时根据需要动态切换算法/行为,从而提供更高的灵活性和可重用性。 这些设计模式都是在现代C++中常见且有用的重用性方法,可以根据具体的应用场景选择合适的设计模式来提高代码的可维护性、可扩展性和可重用性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值