C ++のデザインパターン:インタプリタモード

通訳パターン

GOFにおける「デザインパターン:再利用可能なオブジェクト指向ソフトウェアベースの要素」:この説明、文法表現を定義し、与えられた言語を、そして通訳を定義本の解釈モードが言うことです言語の文章を解釈する表現を使用しています。問題の特定の種類の周波数が十分に高い場合、それは問題の各インスタンスは、言語の単純な文章として表現される価値があるかもしれません。あなたはこれらの文章を解釈することによって、問題を解決するために、通訳、通訳を構築することができるように。

そのゲーム上で言ったように、私が散歩5を入力した、私が従わなければならない:移動移動方向をモバイル+このフォーマットから私の指示を、この形式の命令は文法の一種で入力し、私だけに基づいて定義されて+移動するには、画面上の犬を制御するために入力するには、この文法。もちろん、私は徒歩5を入力した、画面上の子犬は確かにそれは必要性のこの時間を行う方法を、私の入力が何であるかを知らない、理解していませんでしたか?私は犬が理解できるものに入力した内容に変換するためのツールを必要とし、このツールはインタプリタで述べた定義であり、私はそれから、説明した命令を解釈し、コマンド入力のインタプリタがに送信されます画面上の子犬、子犬は、実際の移動中に、理解しています。

また、我々は、多くの場合、正規表現の開発に使用することを問題の種類を表します。特定のアルゴリズムを記述する必要があり、私たちはそれぞれの試合のための文法を定義することができ、我々は、各試合を宣告する必要はありませんし、次に文法定義を説明するために行ってきました。私たちは時々、電話番号、ID番号と一致する必要があります[OK]をクリックします。

 

文法規則および抽象構文木

文法:インタプリタモード、単語を参照するために上記で定義されました。コード実装インタプリタパターンそれを使用する前に、文法、文法ルールとどのように言語を表現するための概念について学ぶために非常に必要です。レッツは、私は次の5つの文法を定義することができ、上記の例では、このゲームには説明されて使用します。

 
  1. expression ::= direction action distance | composite //表达式

  2. composite ::= expression 'and' expression //复合表达式

  3. direction ::= 'up' | 'down' | 'left' | 'right' //移动方向

  4. action ::= 'move' | 'walk' //移动方式

  5. distance ::= an integer //移动距离

ターミネーター(別名ターミネーター発現)のための1つの、そのような言語で上記方向、作用距離、など:5上記規制を設けることにより、5つの単位に対応する言語は、これらのユニットは、2つのカテゴリに言語とすることができます端末または非終端記号の系列を含む、別の非終端記号(別名非終端式)、そのような完全な文である上記式およびコンポジットなど、最小構成単位は、分割できません文字。

そのため、我々はを定義する必要があり、文法、我々が直接理解していないここに記載されているコンピュータ、私たちは、より複雑な文を構成してもよい上記で定義されたいくつかの文法に従って、コンピュータプログラムは、これらのステートメントで何かを行いますされています文法が説明されている。我々はC ++コードを書く、のように、コンピュータが読み取ることができない、私たちは同じことをコンパイルする必要があります。モード通訳、我々はコンピュータに定義された文法を説明するためのモデルを提供し、コンピュータが私たちの文法に従って作業を行ってみましょう。

表すか、または、前記使用頻度、「*」は0回以上などを用いて、組み合わせを表し、「{」および「}」を使用して「|」一部の記号は、使用するなど異なるものを意味するために使用される文法ルールの定義に使用することができますこのような文法規則として、「|」最高シンボル「または」関係である「BOOL値:: = 0 | 1」値の値BOOLターミネーターの発現は0又は1であることを示します。

文法規則を定義するために使用される言語に加えて、解釈モードも視覚グラフィカル上記ゲームの抽象構文木と呼ばれる技術、言語の構文木の一例に対応するものを、言語を表すように構成することができます文法規則は、以下の抽象構文木で表現することができます。

抽象構文木では、発現ターミネーター発現および非終端複合ステートメントによって、言語の文法規則の各インスタンスは、それぞれの特定の文が同様に使用できることを、抽象構文ツリーとして表すことができます。図ASTは、非リーフノード上の発現のクラスのインスタンスとして、木ではなく、端のリーフノードとして図中ターミネーター発現クラスのインスタンスを発現しました。抽象構文木は、複雑な文を作成する方法について説明します。

 

UML类图

;:AbstractExpression抽象の動作を説明文、このインタフェースは、抽象構文木のノードのすべてによって共有される
実装は文法に終端記号に関連付けられた終端記号必要クラスの各インスタンスの文:TernimalExpression動作を説明する。
NonternimalExpressionは:

  • 各文法規則についてNonternimalExpressionのクラスが必要です。
  • 文法シンボルAbstractExpressionの各々は、インスタンス変数の型を維持し、
  • 为文法中的非终结符实现解释操作,在实现时,一般要递归地调用表示文法符号的那些对象的解释操作;

Context:包含解释器之外的一些全局信息;
Client:构建一个需要进行解释操作的文法句子,然后调用解释操作进行解释。

实际进行解释时,按照以下时序进行的:

    1. Client构建一个句子,它是NonterminalExpression和TerminalExpression的实例的一个抽象语法树,然后初始化上下文并调用解释操作;
    2. 每一非终结符表达式节点定义相应子表达式的解释操作。而各终结符表达式的解释操作构成了递归的基础;
    3. 每一节点的解释操作用作用上下文来存储和访问解释器的状态。

 

使用场合

在以下情况下可以考虑使用解释器模式:

  • 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树;
  • 一些重复出现的问题可以用一种简单的语言来进行表达;
  • 一个语言的文法较为简单;
  • 执行效率不是关键问题。【注:高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。】

 

代码实现

我们这里用代码来实现上面的游戏,只不过不是控制小狗在屏幕上移动了,而是将对应的控制指令翻译成汉语进行表示,这和翻译成控制小狗移动的指令的原理是一样的。比如现在有指令:down run 10;那么,经过解释器模式得到的结果为:向下跑动10。

 

代码解释:

#include <iostream>
#include <vector>
using namespace std;
 
#define MAX_SIZE 256
#define SAFE_DELETE(p) if (p) { delete p; p = NULL; }
 
const wchar_t *const DOWN = L"down";
const wchar_t *const UP = L"up";
const wchar_t *const LEFT = L"left";
const wchar_t *const RIGHT = L"right";
 
const wchar_t *const MOVE = L"move";
const wchar_t *const WALK = L"walk";
 
class AbstractNode
{
public:
     virtual wchar_t *Interpret() = 0;
};
 
class AndNode : public AbstractNode
{
public:
     AndNode(AbstractNode *left, AbstractNode *right) : m_pLeft(left), m_pRight(right){}
 
     wchar_t *Interpret()
     {
          wchar_t *pResult = new wchar_t[MAX_SIZE];
          memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
 
          wchar_t *pLeft = m_pLeft->Interpret();
          wchar_t *pRight = m_pRight->Interpret();
          wcscat_s(pResult, MAX_SIZE, pLeft);
          wcscat_s(pResult, MAX_SIZE, pRight);
 
          SAFE_DELETE(pLeft);
          SAFE_DELETE(m_pRight);
 
          return pResult;
     }
 
private:
     AbstractNode *m_pLeft;
     AbstractNode *m_pRight;
};
 
class SentenceNode : public AbstractNode
{
public:
     SentenceNode(AbstractNode *direction, AbstractNode *action, AbstractNode *distance) :
          m_pDirection(direction), m_pAction(action), m_pDistance(distance){}
 
     wchar_t *Interpret()
     {
          wchar_t *pResult = new wchar_t[MAX_SIZE];
          memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
 
          wchar_t *pDirection = m_pDirection->Interpret();
          wchar_t *pAction = m_pAction->Interpret();
          wchar_t *pDistance = m_pDistance->Interpret();
          wcscat_s(pResult, MAX_SIZE, pDirection);
          wcscat_s(pResult, MAX_SIZE, pAction);
          wcscat_s(pResult, MAX_SIZE, pDistance);
 
          SAFE_DELETE(pDirection);
          SAFE_DELETE(pAction);
          SAFE_DELETE(pDistance);
 
          return pResult;
     }
 
private:
     AbstractNode *m_pDirection;
     AbstractNode *m_pAction;
     AbstractNode *m_pDistance;
};
 
class DirectionNode : public AbstractNode
{
public:
     DirectionNode(wchar_t *direction) : m_pDirection(direction){}
 
     wchar_t *Interpret()
     {
          wchar_t *pResult = new wchar_t[MAX_SIZE];
          memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
 
          if (!_wcsicmp(m_pDirection, DOWN))
          {
               wcscat_s(pResult, MAX_SIZE, L"向下");
          }
          else if (!_wcsicmp(m_pDirection, UP))
          {
               wcscat_s(pResult, MAX_SIZE, L"向上");
          }
          else if (!_wcsicmp(m_pDirection, LEFT))
          {
               wcscat_s(pResult, MAX_SIZE, L"向左");
          }
          else if (!_wcsicmp(m_pDirection, RIGHT))
          {
               wcscat_s(pResult, MAX_SIZE, L"向右");
          }
          else
          {
               wcscat_s(pResult, MAX_SIZE, L"无效指令");
          }
 
          SAFE_DELETE(m_pDirection);
          return pResult;
     }
 
private:
     wchar_t *m_pDirection;
};
 
class ActionNode : public AbstractNode
{
public:
     ActionNode(wchar_t *action) : m_pAction(action){}
 
     wchar_t *Interpret()
     {
          wchar_t *pResult = new wchar_t[MAX_SIZE];
          memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
 
          if (!_wcsicmp(m_pAction, MOVE))
          {
               wcscat_s(pResult, MAX_SIZE, L"移动");
          }
          else if (!_wcsicmp(m_pAction, WALK))
          {
               wcscat_s(pResult, MAX_SIZE, L"走动");
          }
          else
          {
               wcscat_s(pResult, MAX_SIZE, L"无效指令");
          }
 
          SAFE_DELETE(m_pAction);
          return pResult;
     }
 
private:
     wchar_t *m_pAction;
};
 
class DistanceNode : public AbstractNode
{
public:
     DistanceNode(wchar_t *distance) : m_pDistance(distance){}
 
     wchar_t *Interpret()
     {
          wchar_t *pResult = new wchar_t[MAX_SIZE];
          memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
 
          wcscat_s(pResult, MAX_SIZE, m_pDistance);
 
          SAFE_DELETE(m_pDistance);
          return pResult;
     }
 
private:
     wchar_t *m_pDistance;
};
 
class InstructionHandler
{
public:
     InstructionHandler(wchar_t *instruction) : m_pInstruction(instruction), m_pTree(NULL){}
 
     void Handle();
     void Output();
 
private:
     void SplitInstruction(wchar_t **&instruction, int &size);
 
     wchar_t *m_pInstruction;
     AbstractNode *m_pTree;
};
 
void InstructionHandler::Handle()
{
     AbstractNode *pLeft = NULL;
     AbstractNode *pRight = NULL;
     AbstractNode *pDirection = NULL;
     AbstractNode *pAction = NULL;
     AbstractNode *pDistance = NULL;
 
     vector<AbstractNode *> node; // Store the instruction expression
 
     // Split the instruction by " "
     wchar_t **InstructionArray = NULL;
     int size;
     SplitInstruction(InstructionArray, size);
     for (int i = 0; i < size; ++i)
     {
          if (!_wcsicmp(InstructionArray[i], L"and")) // The instruction is composited by two expressions
          {
               wchar_t *pDirectionStr = InstructionArray[++i];
               pDirection = new DirectionNode(pDirectionStr);
 
               wchar_t *pActionStr = InstructionArray[++i];
               pAction = new ActionNode(pActionStr);
 
               wchar_t *pDistanceStr = InstructionArray[++i];
               pDistance = new DistanceNode(pDistanceStr);
 
               pRight = new SentenceNode(pDirection, pAction, pDistance);
               node.push_back(new AndNode(pLeft, pRight));
          }
          else
          {
               wchar_t *pDirectionStr = InstructionArray[i];
               pDirection = new DirectionNode(pDirectionStr);
 
               wchar_t *pActionStr = InstructionArray[++i];
               pAction = new ActionNode(pActionStr);
 
               wchar_t *pDistanceStr = InstructionArray[++i];
               pDistance = new DistanceNode(pDistanceStr);
 
               pLeft = new SentenceNode(pDirection, pAction, pDistance);
               node.push_back(pLeft);
          }
     }
 
     m_pTree = node[node.size() - 1];
}
 
void InstructionHandler::Output()
{
     wchar_t *pResult = m_pTree->Interpret();
 
     setlocale(LC_ALL,"");
     wprintf_s(L"%s\n", pResult);
 
     SAFE_DELETE(pResult);
}
 
void InstructionHandler::SplitInstruction(wchar_t **&instruction, int &size)
{
     instruction = new wchar_t*[10];
     memset(instruction, 0, 10 * sizeof( wchar_t*));
 
     for (int i = 0; i < 10; ++i)
     {
          instruction[i] = new wchar_t[10];
          memset(instruction[i], 0, 10 * sizeof(wchar_t));
     }
 
     size = 0;
     int n = 0;
     while (*m_pInstruction != L'\0')
     {
          if (*m_pInstruction == L' ')
          {
               size++;
               m_pInstruction++;
               n = 0;
               continue;
          }
          instruction[size][n++] = *m_pInstruction++;
     }
     size++;
}
int main()
{
     wchar_t *pInstructionStr = L"up move 5 and down walk 10";
     InstructionHandler *pInstructionHandler = new InstructionHandler(pInstructionStr);
     pInstructionHandler->Handle();
     pInstructionHandler->Output();
     SAFE_DELETE(pInstructionHandler);
}

在上面的代码中,我没有用到Context类,一般Context类作为环境上下文类,用于存储解释器之外的一些全局信息,它通常作为参数被传递到所有表达式的解释方法interpret中,可以在Context对象中存储和访问表达式解释器的状态,向表达式解释器提供一些全局的、公共的数据,此外还可以在Context中增加一些所有表达式解释器都共有的功能,减轻解释器的职责。而我们在代码中定义的一些常量,完全可以放入到Context类中,作为上下文的全局数据。

 

主要优点

  1. 易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法;
  2. 每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言;
  3. 实现文法较为容易;在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂;
  4. 增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式类或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”。

 

主要缺点

  1. 对于复杂文法难以维护;在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式;
  2. 执行效率低;由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也很麻烦。

 

总结

解释器模式在实际的系统开发中使用的非常少,因为它会引起效率、性能以及维护方面的问题,并且难度较大,一般在一些大中型的框架型项目中能够找到它的身影。而现在又有很多的开源库提供了对实际需要的支持,所以,我们在实际开发中没有必要再去重复造轮子,能够理解了解释器模式就好了。

发布了199 篇原创文章 · 获赞 77 · 访问量 20万+

おすすめ

転載: blog.csdn.net/tianguiyuyu/article/details/103911679