天天看點

設計模式之解釋器模式(C++實作)介紹模式結構代碼示例

更多設計模式參看: 設計模式之模式概述(模式彙總)(C++實作)

文章目錄

  • 介紹
    • 意圖:
    • 解決問題:
    • 實作概述:
    • 要點:
    • 應用場景:
    • 優點:
    • 缺點:
  • 模式結構
    • 角色
    • 類圖
  • 代碼示例
    • GitHub
    • AbstractExpression(抽象表達式)
    • TerminalExpression(終結符表達式)
    • NonterminalExpression(非終結符表達式)
    • Context(環境類)
    • 測試
    • 輸出

介紹

解釋器模式描述了如何為簡單的語言定義一個文法,如何在該語言中表示一個句子,以及如何解釋這些句子。實際上,這裡的“語言”不僅僅指我們平時說的中、英、日、法等各種語言。從廣義上來講,隻要是能承載資訊的載體,都可以稱之為“語言”,比如,古代的結繩記事、盲文、啞語、摩斯密碼等。

意圖:

解釋器模式(Interpreter Pattern): 定義一個語言的文法,并且建立一個解釋器來解釋該語言中的句子,這裡的“語言”是指使用規定格式和文法的代碼。解釋器模式是一種類行為型模式。

解決問題:

如何對資訊載體(語言等)進行解釋的問題

實作概述:

每個文法規則的語言執行個體都可以表示為一個抽象文法樹,即每一條具體的語句都可以用抽象文法樹來表示。終結符表達式類的執行個體作為樹的葉子節點,而非終結符表達式類的執行個體作為非葉子節點,它們可以将終結符表達式類的執行個體以及包含終結符和非終結符執行個體的子表達式作為其子節點。抽象文法樹描述了如何構成一個複雜的句子,通過對抽象文法樹的分析,可以識别出語言中的終結符類和非終結符類。參見Redy文法分析--抽象文法樹簡介

要點:

建構文法樹,定義終結符與非終結符。

應用場景:

  • 可以将一個需要解釋執行的語言中的句子表示為一個抽象文法樹。
  • 一些重複出現的問題可以用一種簡單的語言進行表達;
  • 一個語言的文法較為簡單;
  • 不考慮執行效率的問題時可以使用解釋器模式。

優點:

  • 易于改變和擴充文法,在解釋器中使用類表示語言的文法規則,可以通過繼承等機制類改變或擴充文法;
  • 每一條文法規則都可以表示為一個類,是以可以友善地實作一個簡單的語言;
  • 如果要增加新的解釋表達式,隻需增加一個新的終結符表達式或非終結符表達式類,無需修改原有代碼,符合開閉原則。

缺點:

  • 對于複雜文法難以維護。由于在解釋器模式中每一條規則至少需要定義一個類,是以如果一個語言包含太多文法規則,類的個數将會大量增加,導緻系統難以管理和維護;
  • 執行效率低,因為解釋器模式中有大量循環和遞歸調用。

模式結構

角色

  • AbstractExpression(抽象表達式): 聲明了抽象的解釋操作,它是所有終結符表達式和非終結符表達式的公共父類。
  • TerminalExpression(終結符表達式): 終結符是文法規則的組成元素中最基本的語言機關,不能再分解。 實作了與文法規則中終結符相關的解釋操作,句子中的每一個終結符都是該類的一個執行個體。
  • NonterminalExpression(非終結符表達式): 實作了文法中非終結符的解釋操作,非終結符表達式中可以包含終結符表達式,也可以繼續包含非終結符表達式,是以其解釋操作一般通過遞歸的方式來完成。
  • Context(環境類): 環境類又稱為上下文類,存儲解釋器之外的一些全局資訊,通常它可以臨時存儲需要解釋的語句。

類圖

設計模式之解釋器模式(C++實作)介紹模式結構代碼示例

代碼示例

解釋機器人控制指令(網上很多都用的這個例子ヾ(。 ̄□ ̄)ツ゜゜゜)

說明:
機器人控制程式中包含一些簡單的英文控制指令,每一個指令對應一個表達式(expression),
該表達式可以是簡單表達式也可以是複合表達式,每一個簡單表達式由移動方向(direction),移動方式(action)和移動距離(distance)三部分組成,
其中移動方向包括上(up)、下(down)、左(left)、右(right);
移動方式包括移動(move)和快速移動(run);
移動距離為一個正整數。
兩個表達式之間可以通過與(and)連接配接,形成複合(composite)表達式。
           
文法規則:
expression = direction + action + distance 或 composite //表達式
composite = expression + 'and' + expression //複合表達式
direction = 'up' 或 'down' 或 'left' 或 'right' //移動方向
action = 'move' 或 'run' //移動方式
distance = an integer //移動距離
           
終結符(也稱為終結符表達式):direction、action 和 distance,它們是語言的最小組成機關,不能再進行拆分;
非終結符(也稱為非終結符表達式),expression 和 composite,它們都是一個完整的句子,包含一系列終結符或非終結符。
           

AbstractExpression(抽象表達式): AbstractNode

TerminalExpression(終結符表達式):DirectionNode、ActionNode、DistanceNode

NonterminalExpression(非終結符表達式):AndNode 、SentenceNode

Context(環境類):Handler (指令處理類工具類)

GitHub

InterpreterPattern

AbstractExpression(抽象表達式)

/// AbstractExpression(抽象表達式): AbstractNode
class AbstractNode {
public:
    virtual ~AbstractNode() = default;
    virtual std::string interpret() = 0;
protected:
    AbstractNode() = default;
};
           

TerminalExpression(終結符表達式)

/// TerminalExpression(終結符表達式):DrectionNode 、ActionNode、DistanceNode
//方向解釋:終結符表達式
class DirectionNode : public AbstractNode {
public:
    explicit DirectionNode(const std::string &direction) {
        this->direction = direction;
        std::cout << "DirectionNode, Hello. Direction = " << this->direction << std::endl;
    }
    ~DirectionNode() override {
        std::cout << "DirectionNode, Bye. Direction = " << this->direction << std::endl;
    }

    //方向表達式的解釋操作
    std::string interpret() override {
        /// 注 這裡未處理 大寫 字母, 示範模式 預設 全小寫
        if (direction == "up") {
            return "向上";
        } else if (direction == "down") {
            return "向下";
        } else if (direction == "left") {
            return "向左";
        } else if (direction == "right") {
            return "向右";
        } else {
            return "無效指令";
        }

    }
private:
    std::string direction;
};

//動作解釋:終結符表達式
class ActionNode : public AbstractNode {
public:
    explicit ActionNode(const std::string &action) {
        this->action = action;
        std::cout << "ActionNode, Hello. Action = " << this->action << std::endl;
    }
    ~ActionNode() override {
        std::cout << "ActionNode, Bye. Action = " << this->action << std::endl;
    }
    //動作(移動方式)表達式的解釋操作
    std::string interpret() override {
        if (action == "move") {
            return "移動";
        }
        else if (action == "run") {
            return "快速移動";
        }
        else {
            return "無效指令";
        }
    }

private:
    std::string action;
};
//距離解釋:終結符表達式
class DistanceNode : public AbstractNode {
public:
    explicit DistanceNode(const std::string &distance) {
        this->distance = distance;
        std::cout << "DistanceNode, Hello. Distance = " << this->distance << std::endl;
    }
    ~DistanceNode() override {
        std::cout << "DistanceNode, Bye. Distance = " << this->distance << std::endl;
    }
    //距離表達式的解釋操作
    std::string interpret() override {
        return distance;
    }

private:
    std::string distance;
};
           

NonterminalExpression(非終結符表達式)

/// NonterminalExpression(非終結符表達式):AndNode 、SentenceNode
//And解釋:非終結符表達式
class AndNode : public AbstractNode {
public:
    AndNode(AbstractNode *left, AbstractNode *right) {
        this->left = left;
        this->right = right;
        std::cout << "AndNode, Hello." << "left = " << this->left
        << " right = " << this->right << std::endl;
    }
    ~AndNode() override{
        std::cout << "AndNode, Bye." << "left = " << this->left
        << " right = " << this->right << std::endl;
        delete left;
        delete right;
    }

    std::string interpret() override {
        return left->interpret() + "再" + right->interpret();
    }
private:
    AbstractNode *left; //And的左表達式
    AbstractNode *right; //And的右表達式
};
//簡單句子解釋:非終結符表達式
class SentenceNode : public AbstractNode {
public:
    SentenceNode(AbstractNode *direction, AbstractNode *action, AbstractNode *distance) {
        this->direction = direction;
        this->action = action;
        this->distance = distance;
        std::cout << "SentenceNode, Hello." << "direction = " << this->direction
        << " action = " << this->action << " distance = " << this->distance << std::endl;
    }
    ~SentenceNode() override{
        std::cout << "SentenceNode, Bye." << "direction = " << this->direction
                  << " action = " << this->action << " distance = " << this->distance << std::endl;
        delete direction;
        delete action;
        delete distance;

    }

    std::string interpret() override {
        return direction->interpret() + action->interpret() + distance->interpret();
    }
private:
    AbstractNode *direction; // 移動方向
    AbstractNode *action; // 移動方式
    AbstractNode *distance; // 移動距離
};
           

Context(環境類)

///Context(環境類):Handler  (指令處理類工具類)
class Handler  {
public:
    explicit Handler (const std::string &input) {
        expression = input;
        finalExp = nullptr;
        std::cout << "Handler , Hello: " << expression << std::endl;
    }
    ~Handler () {
        std::cout << "Handler , Bye: " << expression << std::endl;
        delete finalExp;
    }
    void handle() {
        AbstractNode *left = nullptr, *right = nullptr;
        AbstractNode *direction = nullptr, *action = nullptr, *distance = nullptr;

        // 分割 expression 這裡 借助strtok實作split, 其他方式自行百度
        char* inputCh = const_cast<char *>(expression.c_str());
        char *token = strtok(inputCh, " ");
        while (token != nullptr){
            expVector.emplace_back(token);
            token = strtok(nullptr, " ");
        }
        for (int i = 0; i < expVector.size(); i++) {
            // 如果遇到and則将其後的三個單詞連成一個簡單句子(Sentence)作為"and"的右表達式,而将棧頂彈出的表達式作為"and"的左表達式,最後将新的And表達式壓入棧中
            if("and" == expVector[i]) { //
                //從彈出棧頂作為and的左表達式
                left = stackExp.top();
                stackExp.pop();

                std::string dir = expVector[++i]; // i 先 +1
                direction = new DirectionNode (dir);
                std::string act = expVector[++i];
                action = new ActionNode(act);
                std::string dis = expVector[++i];
                distance = new DistanceNode(dis);

                //組成一個簡單表達式作為And的右表達式
                right = new SentenceNode(direction, action, distance);

                //生成And表達式,并壓入棧中
                stackExp.push(new AndNode (left, right));
            } else {
                //如果不是and表達式,就從頭開始進行解釋,将前3個單詞作為Sentence的三個操作數,生成簡單表達式解析器後壓入棧中
                std::string dir = expVector[i];
                direction = new DirectionNode (dir);
                std::string act = expVector[++i]; // i 先 +1
                action = new ActionNode(act);
                std::string dis = expVector[++i];
                distance = new DistanceNode(dis);

                //組成一個簡單表達式作為And的右表達式
                stackExp.push(new SentenceNode(direction, action, distance));
            }
        }
        /// 最後 解釋成一條表達式語句并存放在 棧 stackExp中
        if(!stackExp.empty()){
            finalExp = stackExp.top();
            stackExp.pop();
        } else {
            finalExp = nullptr;
        }
    }

    std::string output(){
        return finalExp == nullptr ? "**empty**": finalExp->interpret();
    }

private:
    //聲明一個棧對象用于存儲抽象文法樹
    std::stack<AbstractNode*> stackExp; // 存儲表達式的棧
    std::vector<std::string> expVector; // expression 按空格分割後 存儲 vector
    std::string expression;
    AbstractNode *finalExp;
};
           

測試

int main() {
    {
        std::cout << "=========Test 1 start==========="  << std::endl;
        std::string instruction = "up move 5 and down run 10 and left move 5";
        Handler handler(instruction);
        handler.handle();
        std::cout <<"輸入指令: " <<instruction <<std::endl;
        std::cout <<"移動結果:" << handler.output() << std::endl;
        std::cout << "=========Test 1 end==========="  << std::endl;
    }
    std::cout << "--------------------------------"  << std::endl;
    {
        std::cout << "=========Test 2 start==========="  << std::endl;
        std::string instruction = "right run 20 and down move 10 and left run 40 and up run 10";
        Handler handler(instruction);
        handler.handle();
        std::cout <<"輸入指令: " <<instruction << std::endl;
        std::cout <<"移動結果:" << handler.output() << std::endl;
        std::cout << "=========Test 2 end==========="  << std::endl;
    }

    return 0;
}
           

輸出

設計模式之解釋器模式(C++實作)介紹模式結構代碼示例
設計模式之解釋器模式(C++實作)介紹模式結構代碼示例

個人能力有限,如有錯誤之處或者其他建議,敬請告知歡迎探讨,謝謝!

繼續閱讀