設計模式之解釋器模式

解釋器模式

  解釋器(Interpreter)模式,給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。ios

  解釋器模式須要解決的問題是:若是一種特定類型的問題發生的頻率足夠高,那麼可能就值得將該問題的各個實例表述爲一個簡單語言中的句子,這樣就能夠構建一個解釋器,該解釋器經過解釋這些句子來解決該問題。正則表達式就是一種解釋器模式的應用。早期字符串匹配都是爲某種特殊的應用場景單獨寫一個函數來作,後來使用通用的搜索算法來解釋執行一個正則表達式,該正則表達式定義了帶匹配字符串的集合。而所謂的解釋器模式,正則表達式就是它的一種應用,解釋器爲正則表達式定義了一個文法,如何表示一個特定的正則表達式,以及如何解釋這個正則表達式。正則表達式

解釋器模式UML類圖

Context:上下文,一般包含各個解釋器所須要的公共功能或者數據,這個Context在解釋器模式中起着很是重要的做用,通常用來傳遞被全部解釋器共享的數據,後面的解釋器能夠從這裏獲取這些值。算法

Client:客戶端,指的是使用解釋器的客戶端。一般在這裏將按照語言的語法作的表達式轉換成解釋器對象描述的抽象語法樹,而後調用解釋操做。設計模式

AbstractEcpression:定義解釋器的接口,約定解釋器的解釋操做,其中的Interpret方法,正如其名字同樣,專門用來解釋該解釋器所要實現的功能。這個解釋操做爲抽象語法樹中全部的結點所共享。ide

TerminalExpression:終結符解釋器,用來實現語法規則中和終結符相關的操做,再也不包含其它的解釋器。若是用組合模式來構建抽象語法樹的話,就至關於組合模式中的葉子節對象,能夠有多種終結符解釋器。文法中,每個終結符都有一個具體的終結表達式與之相對應函數

NonterminalExpression:非終結符解釋器,用來實現語法樹中非終結符相關的操做,一般一個解釋器對應一個語法規則,能夠包含其它解釋器。若是用組合模式構建抽象語法樹的話,就至關於組合模式中的分支節點。能夠有多種非終結符解釋器。對文法中每一條規則R一、R二、……Rn都須要一個具體的非終結符表達式類。經過實現抽象表達式中的Interpret方法來實現解釋操做。解釋操做以遞歸方式調用上面提到的表明R一、R2……Rn中各個符號的實例變量。spa

客戶端代碼:.net

  構建表示該文法定義的語言中一個特定的句子的抽象語法樹。調用解釋操做。設計

(本段內容來自於:https://blog.csdn.net/zang141588761/article/details/53483645         感受講的比《大話模式》這個書裏的要好一些,就摘了過來)code

解釋器模式的優缺點

優勢:

  1.能夠很容易的擴展和改變文法,由於該模式使用類來表示文法規則,你可使用繼承來改變或者擴展該文法。也比較容易實現文法,由於定義抽象語法樹中各個節點的類的實現大致相似,這些類都易於直接編寫。

缺點:

  1.解釋器模式爲文法中的每一條規則至少定義了一個類,所以包含許多負責的文法可能難以管理和維護,建議當文法很是複雜時,使用其它的技術如語法分析程序或者編譯器生成器來處理。

使用場景:

  一般當有一個語言須要解釋執行,而且你可將該語言中的句子表示成一個抽象語法樹時,可使用解釋器模式。

代碼示例

  實現《大話設計模式》裏講的音樂解釋器。規則是:O表示音階,「O 1「」表示低音階,「O 2「」表示中音階,「O 3「」表示高音階,P表示休止符,CDEFGAB表示「Do-Re-Mi-Fa-So-La-Ti」;音符長度1表示一拍,2表示二拍,0.5表示半拍,0.25表示四分之一拍,一次類推;全部的字母和數字都要用半角空格分開。輸出爲音樂的簡譜。

1.AbstractExpression類

#ifndef ABSTRACTEXPRESSION_H_
#define ABSTRACTEXPRESSION_H_

#include "Context.h"
#include <sstream>
#include <vector>

class AbstractExpression
{
public:
    //解釋器
    void interpret(Context &musicContext)
    {
    std::string strPlayContext = musicContext.getPlayContext();
    if(true == strPlayContext.empty())
        return;
    else
    {
        //原始輸入形如:"O 1 E 0.5 G 0.5 A 3 ",每一個字母后跟一個空格,開始的"O 1"表示的是音階,在對原始輸入進行解釋的首,第一步就是先把音階部分提取出來
        //這裏利用了stringstream流以空格做爲結束標誌的特性來提取音階,能夠看到音階完整的音階是4個字符(不考慮不夠四個字符的狀況,就是簡單寫一下)
        std::vector<std::string> vecScale;
        std::string strBuff;
        std::stringstream ssIn(strPlayContext);
        while(ssIn >> strBuff)
        vecScale.push_back(strBuff);
        std::string strPlayKey = vecScale.at(0);
        std::string strPlayValue = vecScale.at(1);
        execute(strPlayKey,strPlayValue);        //演奏
        //把演奏完的部分從演奏內容中去掉
        vecScale.erase(vecScale.begin(),vecScale.begin()+2);
        //造成新的演奏內容,並更新Context保有的演奏內容
        std::vector<std::string>::iterator it;
        std::string strNewPlayContext;
        for(it = vecScale.begin();it != vecScale.end();++it)
        {
        strNewPlayContext += *it;
        if(it != vecScale.end()-1)
            strNewPlayContext += " ";
        }
        musicContext.setPlayContext(strNewPlayContext);
        }
    }
    virtual void execute(std::string strKey,std::string strValue) const = 0;
    AbstractExpression() = default;
    virtual ~AbstractExpression() = default;
};
#endif
AbstractExpression

2.Context類

#ifndef CONTEXT_H_
#define CONTEXT_H_
//演奏內容類,這裏是解釋器須要的公共數據
#include <string>

class Context
{
private:
    std::string m_strPlayContext;   //演奏文本
public:
    void setPlayContext(const std::string &strPlayContext)
    {
    m_strPlayContext = strPlayContext;
    }
    const std::string& getPlayContext()const
    {
    return m_strPlayContext;
    }
};
#endif
Context

3.進行表達式解析的兩個子規則表達式解析器

#ifndef NOTE_H_
#define NOTE_H_

#include "AbstractExpression.h"
#include <iostream>

//音符類----葉子結點
class TerminalExpression : public AbstractExpression
{
public:
    void execute(std::string strPlayKey,std::string strPlayValue) const override;
    TerminalExpression() = default;
    ~TerminalExpression() = default;
};
#endif 

#include "Note.h"

void TerminalExpression::execute(std::string strPlayKey,std::string strPlayValue) const 
{
    //若是key是C則演奏1,依次類推
    std::string strNote = "";
    switch(strPlayKey[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:
    break;
    }
    std::cout << strNote << " ";
}

#ifndef SCALE_H_
#define SCALE_H_

#include "AbstractExpression.h"
#include <iostream>

class Scale : public AbstractExpression
{
public:
    void execute(std::string strPlayKey,std::string strPlayValue) const override;
    Scale() = default;
    ~Scale() = default;
};
#endif

#include "Scale.h"

//音階類
void Scale::execute(std::string strPlayKey,std::string strPlayValue) const
{
    //若是得到的key是O,而且value是1則演奏低音,value是2則演奏中音,value是3則演奏高音
    std::string strScale="";
    switch(strPlayValue[0])
    {
    case '1':
        strScale = "Di Yin";
    break;
    case '2':
        strScale = "Zhong Yin";
    break;
    case '3':
        strScale = "Gao Yin";
    break;
    default:
    break;
    }
    std::cout << strScale << " ";
}
TerminalExpression

4.Client

#include "Context.h"
#include "AbstractExpression.h"
#include "Note.h"
#include "Scale.h"

using namespace std;

int main(int argc,char *argv[])
{
    std::cout <<"Shang hai tan :" ;
    Context objContext;
    objContext.setPlayContext("O 2 E 0.5 G 0.5 A 3 E 0.5 G 0.5 D 3 E 0.5 G 0.5 A 0.5 O 3 C 1 O 2 A 0.5 G 1 C 0.5 E 0.5 D 3 ");
    AbstractExpression *objExpression = nullptr;
    while(objContext.getPlayContext().size() > 0)
    {
    //這段while循環的意思其實就是不一樣的輸入按照不一樣的規則解釋
    char c = objContext.getPlayContext()[0];
    switch(c)
    {
    case 'O':
        //若是是音階,則將objExpression實例化爲音階類
    {
        objExpression = new Scale;
    }
    break;
    case 'C':case 'D':case 'E':case 'F':case'G':case 'A':case 'B':case 'P':
    {
        objExpression = new TerminalExpression;   //若是字母CDEFGABP,則實例化爲Note
    }
    break;
    default:
    break;
    }
    objExpression->interpret(objContext);
    if(nullptr != objExpression)
    {
        delete objExpression;
        objExpression = nullptr;
    }
    }
    std::cout << std::endl;
    return(1);
}
Client
相關文章
相關標籤/搜索