前提:領域規則模式
在特定領域內,某些變化雖然頻繁,但可以抽象為某種規則。這時候,結合特定領域,將問題抽象為語法規則,從而給出該領域下的一般性解決方案。
典型模式
解析器模式:Interpreter
一:解釋器模式Interpreter
(一)概念
一些應用提供了內建(Build-In)的腳本或者宏語言來讓用戶定義他們能夠在系統中進行的操作。
Interpreter模式的目的就是使用一個解釋器為用戶提供一個一門定義語言的語法表示的解釋器,然后通過解釋器來解釋語言中的句子。
Interpreter模式提供了一個實現語法解釋器的框架。
(二)動機
在軟件構建過程中,如果某一特定領域的問題比較復雜,類似的結構不斷的重復出現,如果使用普通的編程方式來實現將面臨非常頻繁的變化。
在這種情況下,將特定領域的問題表達為某種語法規則下的句子,然后構建一個解析器來解釋這樣的句子,從而達到解決問題的目的。
(三)代碼分析(加減運算)
利用操作樹來實現語法規則提取
string expStr = "a+b-c+d"; //使用表達式樹來表示規則
0.表達式基類
class Expression { public: virtual int interpreter(map<char, int> var)=0; //解析結果 virtual ~Expression(){} };
1.構建變量表達式(葉子結點)
//變量表達式 class VarExpression: public Expression { char key; public: VarExpression(const char& key) { this->key = key; } int interpreter(map<char, int> var) override { return var[key]; } };
2.符號表達式(樹結點基類)
//符號表達式 class SymbolExpression : public Expression { // 運算符左右兩個參數 protected: Expression* left; Expression* right; public: SymbolExpression( Expression* left, Expression* right): left(left),right(right){ } };
3.符號表達式子類實現(樹結點),進行執行
//加法運算 class AddExpression : public SymbolExpression { public: AddExpression(Expression* left, Expression* right): SymbolExpression(left,right){ } int interpreter(map<char, int> var) override { return left->interpreter(var) + right->interpreter(var); //執行操作 } }; //減法運算 class SubExpression : public SymbolExpression { public: SubExpression(Expression* left, Expression* right): SymbolExpression(left,right){ } int interpreter(map<char, int> var) override { return left->interpreter(var) - right->interpreter(var); } };
4.解析表達式(獲取的是解析的表達式,不是結果)
Expression* analyse(string expStr) { stack<Expression*> expStack; //使用棧來存儲表達式 Expression* left = nullptr; Expression* right = nullptr; for(int i=0; i<expStr.size(); i++) { switch(expStr[i]) { case '+': // 加法運算 left = expStack.top(); //獲取棧頂數據做左運算數 right = new VarExpression(expStr[++i]); //獲取表達式下一個數做有運算數 expStack.push(new AddExpression(left, right)); //將運算結果入棧 break; case '-': // 減法運算 left = expStack.top(); right = new VarExpression(expStr[++i]); expStack.push(new SubExpression(left, right)); break; default: // 變量表達式 expStack.push(new VarExpression(expStr[i])); //不是符號,我們就進行入棧操作 } } Expression* expression = expStack.top(); //獲取最后棧頂表達式返回出去就是結果 return expression; }
5.表達式樹構建
int main(int argc, const char * argv[]) { string expStr = "a+b-c+d-e"; //結構類似,容易抽象為語法規則,業務頻繁變化 map<char, int> var; var.insert(make_pair('a',5)); var.insert(make_pair('b',2)); var.insert(make_pair('c',1)); var.insert(make_pair('d',6)); var.insert(make_pair('e',10)); Expression* expression= analyse(expStr); int result=expression->interpreter(var); //使用解析得到的表達式,可以獲取結果返回 cout<<result<<endl; release(expression); return 0; }
6.釋放空間,釋放順序下至上
void release(Expression* expression){ //釋放表達式樹的節點內存... }
(四)模式定義
給定一個語言,定義它的文法的一種表示,並定義一種解釋器,這個解釋器使用該表示來解釋語言中的句子。
——《設計模式》GoF
(五)類圖(結構)
(六)要點總結
(一)Interpreter模式的應用場合是Interpreter模式應用中的難點,只有滿足“業務規則頻繁變化,且類似的結構不斷重復出現,並且容易抽象為語法規則的問題”才適合使用Interpreter模式。
(二)使用Interpreter模式來表示文法規則,從而可以使用面向對象技巧來方便地“擴展”文法。
(三)Interpreter模式比較適合簡單的文法表示,對於復雜的文法表示,Interpreter模式會產生比較大的類層次結構,需要求助於語法分析生成器這樣的標准工具。
(七)案例實現(沒有上面講解使用的案例好)
#include <iostream> using namespace std; #include "string" class Context { public: Context(int num) { m_num = num; } public: void setNum(int num) { m_num = num; } int getNum() { return m_num; } void setRes(int res) { m_res = res; } int getRes() { return m_res; } private: int m_num; int m_res; }; class Expression { public: virtual void interpreter(Context *context) = 0; }; class PlusExpression:public Expression { public: virtual void interpreter(Context *context) { int num = context->getNum(); num++; context->setNum(num); context->setRes(num); } }; class MinusExpression:public Expression { public: virtual void interpreter(Context *context) { int num = context->getNum(); num--; context->setNum(num); context->setRes(num); } }; int main(void) { Context *pcxt = new Context(100); Expression *e1 = new PlusExpression(); e1->interpreter(pcxt); cout << "PlusExpression:" << pcxt->getRes() << endl; Expression *e2 = new MinusExpression(); e2->interpreter(pcxt); cout << "MinusExpression:" << pcxt->getRes() << endl; delete e1; delete e2; system("pause"); return 0; }