深入解析C++设计模式编程中解释器模式的运用

解释器模式(interpreter),给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

解释器模式需要解决的是,如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。用了解释器模式,就意味着可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,你可使用继承来改变或扩展该文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。

结构图:

实例:

音乐解释器

playContext.h

/************************************************************************
 * description: 演奏内容
 * remark:
************************************************************************/
#ifndef _PLAY_CONTEXT_H_
#define _PLAY_CONTEXT_H_
#include <string>
#include <iostream>
using namespace std;
class playContext
{
public:
  string getPlayText()
  {
    return m_strText;
  }
  void setPlayText(const string& strText)
  {
    m_strText = strText;
  }
private:
  string m_strText;
};
#endif// _PLAY_CONTEXT_H_

expression.h

/************************************************************************
 * description: 表达式类
 * remark:
************************************************************************/
#ifndef _EXPRESSION_H_
#define _EXPRESSION_H_
#include "playContext.h"
class expression
{
public:
  // 解释器
  void interpret(playContext& PlayContext)
  {
    if (PlayContext.getPlayText().empty())
    {
      return;
    }
    else
    {
      string strPlayKey = PlayContext.getPlayText().substr(0, 1);
      string strtemp = PlayContext.getPlayText().substr(2);
      PlayContext.setPlayText(strtemp); 

      size_t nPos = PlayContext.getPlayText().find(" ");
      string strPlayValue = PlayContext.getPlayText().substr(0, nPos);
      int  nPlayValue = atoi(strPlayValue.c_str());
      nPos = PlayContext.getPlayText().find(" ");
      PlayContext.setPlayText(PlayContext.getPlayText().substr(nPos + 1));
      excute(strPlayKey, nPlayValue);
    }
  }
  // 执行
  virtual void excute(string& strKey, const int nValue) = 0;
private:
};
#endif// _EXPRESSION_H_

note.h

/************************************************************************
 * description: 音符类
 * remark:
************************************************************************/
#ifndef _NOTE_H_
#define _NOTE_H_
#include "expression.h"
class note : public expression
{
public:
  virtual void excute(string& strKey, const int nValue)
  {
    char szKey[2];
    strncpy(szKey, strKey.c_str(), strKey.length());
    string strNote;
    switch (szKey[0])
    {
    case 'C':
      strNote = "1";
      break;
    case 'D':
      strNote = "2";
      break;
    case 'E':
      strNote = "3";
      break;
    case 'F':
      strNote = "4";
      break;
    case 'G':
      strNote = "5";
      break;
    case 'A':
      strNote = "6";
      break;
    case 'B':
      strNote = "7";
      break;
    default:
      strNote = "error";
      break;
    }
    cout << strNote << " ";
  }
};
#endif// _NOTE_H_

scale.h

/************************************************************************
 * description: 音阶类
 * remark:
************************************************************************/
#ifndef _SCALE_H_
#define _SCALE_H_
#include "expression.h"
class scale : public expression
{
public:
  virtual void excute(string& strKey, const int nValue)
  {
    string strScale;
    switch (nValue)
    {
    case 1:
      strScale = "低音";
      break;
    case 2:
      strScale = "中音";
      break;
    case 3:
      strScale = "高音";
      break;
    default:
      strScale = "error";
      break;
    }
    cout << strScale << " ";
  }
private:
};
#endif// _SCALE_H_

speed.h

#ifndef _SPEED_H_
#define _SPEED_H_
#include "expression.h"
class speed : public expression
{
public:
  virtual void excute(string& strKey, const int nValue)
  {
    string strSpeed;
    if (nValue < 3)
    {
      strSpeed = "快速";
    }
    else if (nValue >= 6)
    {
      strSpeed = "慢速";
    }
    else
    {
      strSpeed = "中速";
    }
    cout << strSpeed << " ";
  }
};
#endif// _SPEED_H_

客户端: InterpreterApp.cpp

// InterpreterApp.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "note.h"
#include "scale.h"
#include "speed.h"
#include "playContext.h"
int _tmain(int argc, _TCHAR* argv[])
{
  playContext context;
  cout << "Music:"; 

  context.setPlayText("T 2 O 2 E 3 G 5 G 5 ");
  expression* expressObj = NULL; 

  while (!context.getPlayText().empty())
  {
    string strSep = context.getPlayText().substr(0, 1);
    char szKey[2];
    strncpy(szKey, strSep.c_str(), strSep.length());
    switch (szKey[0])
    {
    case 'O':
      expressObj = new scale();
      break;
    case 'T':
      expressObj = new speed();
      break;
    case 'C':
    case 'D':
    case 'E':
    case 'F':
    case 'G':
    case 'A':
    case 'B':
    case 'P':
      expressObj = new note();
      break;
    default:
      break;
    }
    if (NULL != expressObj)
    {
      expressObj->interpret(context);
    }
  }
  system("pause");
  return 0;
}

不足之处
解释器模式不足的是,解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。

适用场景

  • 当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
  • 该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。
  • 效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。
(0)

相关推荐

  • 详解C++设计模式编程中责任链模式的应用

    职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 其思想很简单,比如考虑员工要求加薪.公司的管理者一共有三级,总经理.总监.经理,如果一个员工要求加薪,应该向主管的经理申请,如果加薪的数量在经理的职权内,那么经理可以直接批准,否则将申请上交给总监.总监的处理方式也一样,总经理可以处理所有请求.这就是典型的职责链模式,请求的处理形成了一条链,直到有一个对象处理请求.给出这个例子的UML图.

  • C++设计模式编程之Flyweight享元模式结构详解

    由遇到的问题引出享元模式: 在面向对象系统的设计何实现中,创建对象是最为常见的操作.这里面就有一个问题:如果一个应用程序使用了太多的对象,就会造成很大的存储开销.特别是对于大量轻量级(细粒度)的对象,比如在文档编辑器的设计过程中,我们如果为没有字母创建一个对象的话,系统可能会因为大量的对象而造成存储开销的浪费.例如一个字母"a"在文档中出现了100000 次,而实际上我们可以让这一万个字母"a"共享一个对象,当然因为在不同的位置可能字母"a"有不

  • 详解C++设计模式编程中对访问者模式的运用

    访问者模式(visitor),表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.访问者模式适用于数据结构相对稳定的系统.它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化.访问者模式的目的是要把处理从数据结构分离出来.很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易.反之,如果这样的系统的数据结

  • C++设计模式编程中简单工厂与工厂方法模式的实例对比

    简单工厂模式实例 题目:实现计算器的输入2个数和运算符,得到结果 工程结构: (1)头文件 COperationFactory.h(运算符工厂类) (2)源文件 SimpleFactory.cpp(客户端应用类,主函数所在) (3)运算类 COperation.cpp(运算符基类) COperation.h COperationAdd.h(加法运算符子类,继承于COperation) COperationDiv.h (除法运算符子类,继承于COperation) COperationMul.h

  • 设计模式中的备忘录模式解析及相关C++实例应用

    备忘录模式旨在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态.在命令模式中,备忘录模式经常还经常被用来维护可以撤销(Undo)操作的状态. 类图: Originator:负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态.Originator可根据需要决定Memento存储Originator的哪些内部状态. Memento:负责存储Originator对象的内部状态,并可防止Origin

  • 详解设计模式中的Command命令模式及相关C++实现

    命令模式的作用是将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤销的操作. 由于"行为请求者"与"行为实现者"的紧耦合,使用命令模式,可以对请求排队或记录请求日志,以及支持可撤销的操作. 命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开. Command模式关键就是讲一个请求封装到一个类中(Command),再提供处理对象(Receiver),最后Command命令由Invoker激活.另外,我们

  • C++设计模式编程中使用Bridge桥接模式的完全攻略

    桥接模式将抽象(Abstraction)与实现(Implementation)分离,使得二者可以独立地变化. 桥接模式典型的结构图为: 在桥接模式的结构图中可以看到,系统被分为两个相对独立的部分,左边是抽象部分,右边是实现部分,这两个部分可以互相独立地进行修改:例如上面问题中的客户需求变化,当用户需求需要从 Abstraction 派生一个具体子类时候,并不需要像上面通过继承方式实现时候需要添加子类 A1 和 A2 了.另外当上面问题中由于算法添加也只用改变右边实现(添加一个具体化子类),而右边

  • 实例讲解C++设计模式编程中State状态模式的运用场景

    State模式允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类. 在面向对象系统的开发和设计过程,经常会遇到一种情况就是需求变更(Requirement Changing),经常我们做好的一个设计.实现了一个系统原型,咱们的客户又会有了新的需求.我们又因此不得不去修改已有的设计,最常见就是解决方案就是给已经设计.实现好的类添加新的方法去实现客户新的需求,这样就陷入了设计变更的梦魇:不停地打补丁,其带来的后果就是设计根本就不可能封闭.编译永远都是整个系统代码. 访问者模式则提

  • 详解设计模式中的中介者模式在C++编程中的运用

    作用:用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 结构图如下: Colleage抽象同事类,而ConcreteColleage是具体同时类,每个具体同事只知道自己的行为,而不了解其他同事类的情况,但它们却都认识中介者对象,Mediator是抽象中介者,定义了同事对象到中介者对象的接口,ConcreteMediator是具体中介者对象,实现抽象类的方法,它需要知道所有具体同事类,并从具体同事接受消息,向具体同事对象

  • 解析C++编程中如何使用设计模式中的状态模式结构

    作用:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. UML图如下: State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为. ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为. Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态. 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把

随机推荐