1. 解釋器模式(Interpreter Pattern)的定義
(1)定義
給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
①文法:即語法規則。在解釋器模式中每一個語法都將對應一個解釋器對象,用來處理相應的語法規則。它對於擴展、改變文法以及增加新的文法規則都很方便。
②解釋器模式描述了如何為簡單的語言定義一個文法,如何在該語言中表示一個句子,以及如何解釋這些句子。
③在解釋器模式中可以通過一種稱之為抽象語法樹(Abstract Syntax Tree, AST)的圖形方式來直觀地表示語言的構成,每一棵抽象語法樹對應一個語言實例
(2)解釋器模式的結構和說明

①AbstractExpression:定義解釋器的接口,約定解釋器的解釋操作。其中的Interpret接口,正如其名字那樣,它是專門用來解釋該解釋器所要實現的功能。(如加法解釋器中的Interpret接口就是完成兩個操作數的相加功能)。
②TerminalExpression:終結符解釋器,用來實現語法規則中和終結符相關的操作,不再包含其他的解釋器,如果用組合模式來構建抽象語法樹的話,就相當於組合模式中的葉子對象,可以有多種終結符解釋器。
③NonterminalExpression:非終結符解釋器,用來實現語法規則中非終結符相關的操作,通常一個解釋器對應一個語法規則,可以包含其他解釋器,如果用組合模式構建抽象語法樹的話,就相當於組合模式中的組合對象。可以有多種非終結符解釋器。
④Context:上下文,通常包含各個解釋器需要的數據或是公共的功能。這個Context在解釋器模式中起着非常重要的作用。一般用來傳遞被所有解釋器共享的數據,后面的解釋器可以從這里獲取這些值。
⑤Client:客戶端,指的是使用解釋器的客戶端,通常在這里將按照語言的語法做的表達式轉換成使用解釋器對象描述的抽象語法樹,然后調用解釋操作。
【編程實驗】四則運算(注意終結符解釋器與非終結解釋器的划分)

//行為型模式:解釋器模式 //場景:四則運算 #include <iostream> #include <string> #include <map> #include <stack> #include <typeinfo> using namespace std; //*******************************************抽象表達式類*********************************** class Expression { public: //解析公式和數值,其中var中的key是公式中的參數,value值是具體的數字 //如a = 100; b = 20; c = 40 virtual int interpreter(map<string, int>& var) = 0; virtual ~Expression(){}; }; //變量解析器(終結符表達式) class VarExpression : public Expression { string key; public: VarExpression(string key) { this->key = key; } //從map中取出變量的值 int interpreter(map<string, int>& var) { return var[key]; } ~VarExpression() { cout << "~VarExpression()" << endl; } }; //**********抽象運算符號解析器*********************** //抽象運算符號解析器 class SymbolExpression : public Expression { protected: Expression* left; Expression* right; public: SymbolExpression(Expression* left, Expression* right) { this -> left = left; this -> right = right; } Expression* getLeft() { return left; } Expression* getRight() { return right; } }; //加法解析器 class AddExpression : public SymbolExpression { public: AddExpression(Expression* left, Expression* right): SymbolExpression(left,right) { } //把左右兩個表達式運算的結果加起來 int interpreter(map<string, int>& var) { return left->interpreter(var) + right ->interpreter(var); } ~AddExpression() { cout << "~AddExpression()" << endl; } }; //減法解析器 class SubExpression : public SymbolExpression { public: SubExpression(Expression* left, Expression* right): SymbolExpression(left,right) { } //把左右兩個表達式運算的結果相減 int interpreter(map<string, int>& var) { return left->interpreter(var) - right ->interpreter(var); } ~SubExpression() { cout << "~SubExpression()" << endl; } }; //*********************************解析器封裝類*************************************** //解析器封裝類,這個類是根據迪米特法則進行封裝,目的是讓Client只與直接朋友打交道,相當於Facade class Calculator { private: Expression* expression; public: //構造函數傳參,並解析表達式,構建語法樹 Calculator(string expStr) { expression = NULL; //棧,用來暫存中間結果 stack<Expression*> stkExp; Expression* left = NULL; Expression* right = NULL; /*從左到向分析表達式(如:a+b-c),最終的語法樹如下: * - * / \ * + c * / \ * a b */ for(unsigned int i = 0; i< expStr.length(); i++) { switch(expStr[i]) { case '+': //加法 //1.先從棧中取出左操作數 left = stkExp.top(); stkExp.pop(); //2.從表達式中取出+號后面的右操作數,並生成終結符解析對象 right = new VarExpression(expStr.substr(++i,1)); //3.將左右操作數相加,並把結果放入棧中 stkExp.push(new AddExpression(left, right)); break; case '-': //1.先從棧中取出左操作數 left = stkExp.top(); stkExp.pop(); //2.從表達式中取出+號后面的右操作數,並生成終結符解析對象 right = new VarExpression(expStr.substr(++i,1)); //3.將左右操作數相減,並把結果放入棧中 stkExp.push(new SubExpression(left, right)); break; default: //如果是變量(終結符):如a+b+c中的a\b\c, //則直接生成對應的變量解析器對象 stkExp.push(new VarExpression(expStr.substr(i,1))); } } //棧中保存的就是最終語法樹的根結點(本例為SuuExpression對象) if(!stkExp.empty()) { expression = stkExp.top(); stkExp.pop(); } } void deltree(Expression* expression) { SymbolExpression* branch = dynamic_cast<SymbolExpression*>(expression); //葉子結點 if (branch == NULL) { delete expression; } else //分支結點 { //左子樹 deltree(branch->getLeft()); //右子樹 deltree(branch->getRight()); //結點 delete expression; } } ~Calculator() { deltree(expression); expression = NULL; } //開始運算 int run(map<string, int>& var) { return (expression == NULL) ? 0 : expression->interpreter(var); } }; int main() { string expStr = "a+b-c"; //為簡化處理,這里必須是合法的表達式 map<string, int> var; //相當於Interpreter模式中的Context var["a"] = 100; var["b"] = 20; var["c"] = 40; Calculator cal(expStr); cout <<"運算結果為:" << expStr << " = " << cal.run(var) << endl; return 0; } /* 運算結果為:a+b-c = 80 ~VarExpression() ~VarExpression() ~AddExpression() ~VarExpression() ~SubExpression() */
2. 思考解釋器模式
(1)解釋器模式的本質:分離實現,解釋執行。通過一個解釋器對象處理一個語法規則的方式,把復雜的功能分離開,然后選擇需要被執行的功能,並把這些功能組合成需要解釋執行的抽象語法樹,再按照抽象語法樹來解釋執行,實現相應的功能。從本質上看,解釋器模式的思路仍然是分離、封裝和簡化,這與很多其他模式是一樣的。
(2)誰來構建抽象語法樹——解析器
解析器的工作主要就是用來構建抽象語法樹的,這個角色會客戶端轉來的語言,負責按語法規則,生成一個個解釋器,並將這些解釋器組合成一顆抽象語法樹。注意,解析器的工作主要是構造語法樹,而解釋器的工作是解釋這顆語法樹。
(3)誰來負責解釋操作 ——解釋器(Interpreter)
只要定義好了抽象語法樹,肯定是解釋器來負責解釋執行,而選擇解釋器的工作,在構建抽象語法樹的時候就完成了,一般由根結點的解釋器開始解釋,然后遞歸地調用其他解釋器。
【編程實驗】中文數字轉阿拉伯數字(注意Context的應用)

//行為型模式:解釋器模式 //場景:中文數字轉阿拉伯數字 /* 1、使用Interpreter模式來將中文數字轉換為數學數字的好處是可以應對中文數字的變化, 雖然可以用一很好的算法將中文轉化為數字,解釋器的擴展性能比較好,如果出現億、兆的情況, 可以寫出兩個類(YiExpression、ZhaoExpression)來繼承Expression類,而其他地方的代碼都不變化。 2、思路:用單位用分解出不同的解釋器.其中個位、十位、百位和千位是終結符解釋器,萬位是非結終符 解釋器,因為萬以上的單位可以形成如果九百零一萬之類的數字,需要進一進拆分成由結終符 構成的解釋器來完成任務。 3、轉化:將中文數字串由低位向高位方向不斷轉化 */ #include <iostream> #include <string> #include <map> #include <stack> #include <list> using namespace std; //字符串上下文信息:保存沒有處理的字符串信息 class Context { private: string statement; int data; public: Context(string statement) { this ->statement = statement; data = 0; } string& getStatement() { return statement; } void setStatement(string statement) { this -> statement = statement; } int getData() { return data; } void setData(int data) { this -> data = data; } }; //抽象類:解釋器 class Expression { protected: //數據字典:保存中文數字一到九 static map<string, int> table; //輔助函數,用來判判斷src字符串是否以tail串結尾 bool stringEndsWith(const string& src, const string& tail) { if(src.size() < tail.size()) return false; string tmp = src.substr(src.size() - tail.size(), tail.size()); return (0==tmp.compare(0,tail.size(),tail)); } public: //虛方法:中文數字到數字的轉換 virtual void interpret(Context& context) { if(context.getStatement().length() == 0) return; map<string, int>::iterator iter = table.begin(); while (iter != table.end()) { string& statement = context.getStatement(); string tail = iter->first + getPostfix(); //從低位往高位分析(如九千三百零五,從右向左分析) if(stringEndsWith(statement,tail)) { context.setData(context.getData() + iter->second * multiplier()); //注意,string是ASCII編碼,每個中文字符的長度為2 context.setStatement(statement.substr(0, statement.length()-2 - getPostfix().length())); } if(stringEndsWith(statement,"零")) { //”零“則直接跳過 context.setStatement(statement.substr(0, statement.length()-2)); } ++iter; } } //表達式的后綴是以什么表示的(十、百...) virtual string getPostfix() = 0; //表達式的數量級 virtual int multiplier() = 0; virtual ~Expression(){}; }; //映射表,保存中文數字與羅馬數字的映射 static map<string, int>::value_type init_table[] = { map<string, int>::value_type("一",1), map<string, int>::value_type("二",2), map<string, int>::value_type("三",3), map<string, int>::value_type("四",4), map<string, int>::value_type("五",5), map<string, int>::value_type("六",6), map<string, int>::value_type("七",7), map<string, int>::value_type("八",8), map<string, int>::value_type("九",9) }; map<string,int> Expression::table(init_table,init_table + 9); //個位數解釋器(終結符表達式) class GeExpression : public Expression { public: string getPostfix() { return ""; } int multiplier() { return 1; } }; //十位數解釋器(終結符表達式) class ShiExpression : public Expression { public: string getPostfix() { return "十"; } int multiplier() { return 10; } }; //百位數解釋器(終結符表達式) class BaiExpression : public Expression { public: string getPostfix() { return "百"; } int multiplier() { return 100; } }; //千位數解釋器(終結符表達式) class QianExpression : public Expression { public: string getPostfix() { return "千"; } int multiplier() { return 1000; } }; //萬位數解釋器(非終結符表達式) class WanExpression : public Expression { public: string getPostfix() { return "萬"; } int multiplier() { return 10000; } void interpret(Context& context) { if(context.getStatement().length() == 0) return ; if (stringEndsWith(context.getStatement(),getPostfix())) { list<Expression*> exps; exps.clear(); exps.push_back(new GeExpression()); exps.push_back(new ShiExpression()); exps.push_back(new BaiExpression()); exps.push_back(new QianExpression()); int temp = context.getData(); string& sm = context.getStatement(); context.setData(0); //string類中每個中文長度為2. context.setStatement(sm.substr(0, sm.length()-2)); list<Expression*>::iterator iter = exps.begin(); while (iter != exps.end()) { (*iter)->interpret(context); ++iter; } context.setData(temp + multiplier()* context.getData()); iter = exps.begin(); while (iter != exps.end()) { delete (*iter); ++iter; } exps.clear(); } } }; //轉換器 class Convertor { private: Context context; string chineseNum; int result; list<Expression*> exps; void reset() { context.setStatement(chineseNum); context.setData(0); list<Expression*>::iterator iter = exps.begin(); while (iter != exps.end()) { delete (*iter); ++iter; } exps.clear(); } public: Convertor(const string chineseNum):context(chineseNum) { this ->chineseNum = chineseNum; result = 0; } void convert() { reset(); exps.push_back(new GeExpression()); exps.push_back(new ShiExpression()); exps.push_back(new BaiExpression()); exps.push_back(new QianExpression()); exps.push_back(new WanExpression()); list<Expression*>::iterator iter = exps.begin(); while (iter != exps.end()) { (*iter)->interpret(context); ++iter; } result = context.getData(); } int getRoman() { return result; } void setChineseNum(const string& chineseNum) { this ->chineseNum = chineseNum; } ~Convertor() { reset(); } }; int main() { string chineseNum = "四百九十六萬二千三百一十五"; Convertor conv(chineseNum); conv.convert(); cout << chineseNum << " -> " << conv.getRoman() << endl; chineseNum = "九千零五萬六千零七十二"; conv.setChineseNum(chineseNum); conv.convert(); cout << chineseNum << " -> " << conv.getRoman() << endl; return 0; } /*輸出結果: 四百九十六萬二千三百一十五 -> 4962315 九千零五萬六千零七十二 -> 90056072 */
3. 解釋器模式的優缺點
(1)優點
①易於實現文法:在解釋器模式中,一條語法規則用一個解釋器對象來解釋執行。對於解釋器的實現來講,功能就變得比較簡單,只需要考慮這一條語法規則的實現就可以了,其他的都不用管。
②易於擴展新的語法。由於解釋器采用類來描述語法規則,因此可以通過繼承等機制創建相應的解釋器對象,在創建抽象語法樹的時候使用這個新的解釋器對象就可以了。
(2)缺點
①執行效率較低。由於在解釋器模式中使用了大量的循環和遞歸調用,因此在解釋較為復雜的句子時其速度很慢,而且代碼的調試過程也比較麻煩。
②對於復雜文法難以維護。在解釋器模式中,每一條規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數將會急劇增加,導致系統難以管理和維護,此時可以考慮使用語法分析程序等方式來取代解釋器模式。
4. 解釋器模式的應用場景
(1)當一個語言需要解釋執行,並可以將該語言中的句子表示為一個抽象語法樹的時候,可以考慮使用解釋器模式(如XML文檔解釋、正則表達式等領域)
(2)一些重復出現的問題可以用一種簡單的語言來進行表達。
(3)一個語言的文法較為簡單.
(4)當執行效率不是關鍵和主要關心的問題時可考慮解釋器模式(注:高效的解釋器通常不是通過直接解釋抽象語法樹來實現的,而是需要將它們轉換成其他形式,使用解釋器模式的執行效率並不高。)
【編程實驗】機器人控制程序

//行為型模式:解釋器模式 //場景:開發一套機器人控制程序 /*說明: 機器人控制程序中包含一些簡單的英文控制指令,每一個指令對應一個表達式(expression), 該表達式可以是簡單表達式也可以是復合表達式,每一個簡單表達式由移動方向(direction), 移動方式(action)和移動距離(distance)三部分組成,其中移動方向包括上(up)、下(down)、 左(left)、右(right);移動方式包括移動(move)和快速移動(run);移動距離為一個正整數。 兩個表達式之間可以通過與(and)連接,形成復合(composite)表達式。 用戶通過對圖形化的設置界面進行操作可以創建一個機器人控制指令,機器人在收到指令 后將按照指令的設置進行移動,例如輸入控制指令:up move 5,則“向上移動5個單位”;輸入控 制指令:down run 10 and left move 20,則“向下快速移動10個單位再向左移動20個單位”。 */ /*文法規則 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,它們都是一個完整的句子,包含一系列終結符或非終結符。 */ //本實例對機器人控制指令的輸出結果進行模擬,將英文指令翻譯為中文指令,實際情況是調用不同的控制程序進行機器人的控制, //包括對移動方向、方式和距離的控制等 #include <iostream> #include <string> #include <stack> #include <vector> #include <typeinfo> using namespace std; ////////////////////////////////////////////////////////////////////////// // 字符串分割 // // ------------------------------------------------------------------------- // 函數 : Split // 功能 : 分割STL標准字符串 // 返回值 : void // 參數 : Container<std::basic_string<CharT> >& v 存放分割結果 // 參數 : const std::basic_string<CharT>& s 待分割字符串 // 參數 : const std::basic_string<CharT>& c 分割字符串 // ------------------------------------------------------------------------- template<typename CharT, template<typename S, typename Q = std::allocator<S> > class Container> void Split(Container<std::basic_string<CharT> >& v, const std::basic_string<CharT>& s, const std::basic_string<CharT>& c); template<template<typename S, typename Q = std::allocator<S> > class Container> void Split(Container<std::basic_string<char> >& v, const std::basic_string<char>& s, const std::basic_string<char>& c) { if (0 == c.length()) return; std::basic_string<char>::size_type pos1 = 0; std::basic_string<char>::size_type pos2 = 0; pos1 = 0; pos2 = s.find(c); while (std::basic_string<char>::npos != pos2) { v.push_back(s.substr(pos1, pos2 - pos1)); pos1 = pos2 + c.size(); pos2 = s.find(c, pos1); } if (pos1 != s.length()) { v.push_back(s.substr(pos1)); } } //抽象類:解釋器 class AbstractExpression { public: virtual string interpret() = 0; virtual ~AbstractExpression(){} }; //And解釋器:非終結符表達式 class AndExpression : public AbstractExpression { private: AbstractExpression* left ; //And的左表達式 AbstractExpression* right; //And的右表達式 public: AndExpression(AbstractExpression* left, AbstractExpression* right) { this->left = left; this->right = right; } //And表達式解釋操作 string interpret() { return left->interpret() + "再" + right->interpret(); } AbstractExpression* getLeft() { return left; } AbstractExpression* getRight() { return right; } }; //簡單句子解釋器:非終結符表達式 //如:up move 5,表示“向上移動5個單位 class SentenceExpression : public AbstractExpression { private: AbstractExpression* direction; AbstractExpression* action; AbstractExpression* distance; public: SentenceExpression(AbstractExpression* direction, AbstractExpression* action, AbstractExpression* distance) { this->direction = direction; this->action = action; this->distance = distance; } //解釋操作 string interpret() { return direction->interpret() + action->interpret() + distance->interpret(); } AbstractExpression* getDirection() { return direction; } AbstractExpression* getAction() { return action; } AbstractExpression* getDistance() { return distance; } }; //方向解釋器:終結符表達式 class DirectionExpression : public AbstractExpression { private: string direction; public: DirectionExpression(string direction) { this->direction = direction; } //方向表達式的解釋操作 string interpret() { if(direction =="up") { return "向上"; } else if(direction == "down") { return "向下"; } else if(direction == "left") { return "向左"; } else if(direction == "right") { return "向右"; } else { return "無效指令"; } } }; //動作解釋器:(終結符表達式) class ActionExpression : public AbstractExpression { private: string action; public: ActionExpression(string action) { this->action = action; } //動作移動表達式的解釋操作 string interpret() { if(action == "move") { return "移動"; } else if(action == "run") { return "快速移動"; } else { return "無效指令"; } } }; //距離解釋器:(終結符表達式) class DistanceExpression : public AbstractExpression { private: string distance; public: DistanceExpression(string distance) { this->distance = distance; } string interpret() { return distance; } }; //指令處理類:工具類 class InstructionHandler { private: AbstractExpression* mExp; void delTree(AbstractExpression* exp) //刪除最終的生成的抽象樹 { //葉子結果 bool bLeaf = typeid(*exp) ==typeid(DirectionExpression) || typeid(*exp) ==typeid(ActionExpression) || typeid(*exp) ==typeid(DistanceExpression); AndExpression* andExp = dynamic_cast<AndExpression*>(exp); SentenceExpression* sentenceExp = dynamic_cast<SentenceExpression*>(exp); if(bLeaf) { delete exp; } else if (andExp != NULL) //And表達式 { AbstractExpression* left = andExp->getLeft(); AbstractExpression* right = andExp->getRight(); delTree(left); delTree(right); delete andExp; } else if(sentenceExp != NULL) //簡單句子表達式 { AbstractExpression* dir = sentenceExp->getDirection(); AbstractExpression* act = sentenceExp->getAction(); AbstractExpression* dis = sentenceExp->getDistance(); delTree(dir); delTree(act); delTree(dis); delete sentenceExp; } else { } } public: InstructionHandler():mExp(NULL){} void handle(string instruction){ AbstractExpression* left = NULL; AbstractExpression* right = NULL; AbstractExpression* direction = NULL; AbstractExpression* action = NULL; AbstractExpression* distance = NULL; if (mExp!=NULL) { delTree(mExp); mExp = NULL; } //聲明一個棧對象用於存儲抽象語法樹 stack<AbstractExpression*> stkExp; vector<string> words; Split(words, instruction, " "); //以空格分隔指令字符串 for(unsigned int i=0; i<words.size(); i++){ //本實例采用棧的方式來處理指令,如果遇到and則將其后的三個單詞連成一個簡單句子(Sentence) //作為"and"的右表達式,而將棧頂彈出的表達式作為"and"的左表達式,最后將新的And表達式壓入棧中 string dir(""); string act(""); string dis(""); if(words[i] =="and"){ //從彈出棧頂作為and的左表達式 left = stkExp.top(); stkExp.pop(); dir = words[++i]; direction = new DirectionExpression(dir); act = words[++i]; action = new ActionExpression(act); dis = words[++i]; distance = new DistanceExpression(dis); //組成一個簡單表達式作為And的右表達式 right = new SentenceExpression(direction, action, distance); //生成And表達式,並壓入棧中 stkExp.push(new AndExpression(left, right)); } //如果不是and表達式,就從頭開始進行解釋,將前3個單詞作為Sentence //的三個操作數,生成簡單表達式解析器后壓入棧中 else{ dir = words[i]; direction = new DirectionExpression(dir); act = words[++i]; action = new ActionExpression(act); dis = words[++i]; distance = new DistanceExpression(dis); //組成一個簡單表達式作為And的右表達式 stkExp.push(new SentenceExpression(direction, action, distance)); } } if(!stkExp.empty()){ mExp = stkExp.top(); stkExp.pop(); } else mExp = NULL; } string output(){ return mExp==NULL ? "": mExp->interpret(); } }; int main() { string instruction = "up move 5 and down run 10 and left move 5"; InstructionHandler handler; handler.handle(instruction); cout <<"輸入指令: " <<instruction <<endl; cout <<"移動結果:" << handler.output() << endl; instruction = "right run 20 and down move 10 and left run 40 and up run 10"; handler.handle(instruction); cout <<"輸入指令: " <<instruction <<endl; cout <<"移動結果:" << handler.output() << endl; return 0; } /*輸出結果: 輸入指令: up move 5 and down run 10 and left move 5 移動結果:向上移動5再向下快速移動10再向左移動5 輸入指令: right run 20 and down move 10 and left run 40 and up run 10 移動結果:向右快速移動20再向下移動10再向左快速移動40再向上快速移動10 */
5. 相關模式
(1)解釋器和組合模式
這兩種可以組合使用,一般非終結符解釋器相當於組合模式中的組合對象,終結符解釋器相當於葉子對象。
(2)解釋器模式和迭代器模式
由於解釋器模式通常使用組合模式來實現,因此在遍歷整個對象結構時,可以使用迭代器模式。
(3)解釋器模式和享元模式
在使用解釋器模式的時候,可能會造成多個細粒度對象,如各式各樣的終結符解釋器,而這些終結符解釋器對不同的表達式來說是一樣的,是可以共用的,因此可以引入享元模式來共享這些對象。
(4)解釋器模式和訪問者模式
在解釋器模式中,語法規則和解釋器對象是有對應關系的。語法規則的變動意味着功能的變化。自然會導致使用不同的解釋器對象;而且一個語法規由可以被不同的解釋器解釋執行。因此在構建抽象語法樹的時候,如果每個節點所對應的解釋器對象是固定的,這意味着該節點對應的功能是固定的,那么就不得不根據需要來構建不同的抽象語法樹。
為了讓構建的抽象語法樹較為通用,那就要求解釋器的功能不要那么固定,要能很方便地改變解釋器的功能,這個時候就變成了如何能夠很方便地更改樹形結構中節點對象的功能了,訪問者模式可以很好的實現這個功能。
