茶網(wǎng)站建設(shè)實(shí)訓(xùn)報(bào)告/百度收錄要多久
(19) 橋接模式 Bridge,不是采用類繼承,而是采用類組合,一個(gè)類的數(shù)據(jù)成員是類對象,來擴(kuò)展類的功能。源碼如下:
class OS // 操作系統(tǒng)負(fù)責(zé)繪圖
{
public:virtual ~OS() {}virtual void draw(char * ptrCache , int lengthCache) = 0 ;
};class OS_Window : public OS
{
public:void draw(char* ptrCache, int lengthCache) { cout << " windows 下繪圖\n\n"; }
};class OS_Linux : public OS
{
public:void draw(char* ptrCache, int lengthCache) { cout << " Linux 下繪圖\n\n"; }
};class Image // 本基類有成員函數(shù)負(fù)責(zé)解析與加載圖片至緩存,但繪圖調(diào)用跟操作系統(tǒng)有關(guān)的底層函數(shù)
{
protected: OS * ptrOS;
public:virtual ~Image() {}Image(OS* p) : ptrOS(p) {}virtual void parseImage( const char * name) = 0;
};class Image_jpg : public Image
{
public:Image_jpg(OS * p) : Image(p){}void parseImage(const char* name){cout << " 解析 jpg 圖片 , ";char c[100]; // 假定分析 jpg 格式后的圖片信息統(tǒng)一用 100 字節(jié)存儲。ptrOS->draw(c , 100);}
};class Image_png : public Image
{
public:Image_png(OS* p) : Image(p) {}void parseImage( const char* name){cout << " 解析 png 圖片 , ";char c[50]; // 假定分析 png 格式后的圖片信息統(tǒng)一用 50 字節(jié)存儲。ptrOS->draw(c, 50);}
};int main()
{OS_Window osWindow;OS_Linux osLinux;Image_jpg jpgA(&osLinux) , jpgB(&osWindow) ;Image_png pngA(&osLinux) , pngB(&osWindow) ;jpgA.parseImage("aaa"); jpgB.parseImage("b");pngA.parseImage("c");pngB.parseImage("dd");return 0;
}
測試結(jié)果如下:
(20)中介者模式 Mediator 。比如 UI 設(shè)計(jì),頁面上的各種控件,牽一發(fā)而動全身,彼此聯(lián)系緊密。若把控件間的聯(lián)系代碼寫入各個(gè)控件,會很繁瑣,龐大。因此可以創(chuàng)建一個(gè)中介對象,專門處理當(dāng)一個(gè) UI 控件的狀態(tài)變化時(shí)的邏輯。也像電腦主板上的總線,借著總線,實(shí)現(xiàn)電腦各部件見的連接與通信。以下代碼配套的是這個(gè) UI 登錄界面:
配套的代碼如下:
class Control; // 類的前向聲明class Media // 中介者的基類
{
public:virtual ~Media() {}virtual void changed(Control * ptrCtrl) = 0;
};class Control // 控件的基類
{
protected: string name; // 控件名稱,以此區(qū)分控件,所以該名稱將是唯一的Media* ptrMedia; // 顯示本控件屬于哪個(gè)中介管理
public:virtual ~Control() {}Control(const string& s, Media* pM) : name(s), ptrMedia(pM) {}virtual void enable(bool enabled) = 0;virtual void changed() { ptrMedia->changed(this); } // 控件產(chǎn)生了變化,交由 中介者 來處理這種相關(guān)聯(lián)的變化
};class Control_Button : public Control
{
public:Control_Button(const string& s, Media* ptr) : Control(s, ptr) {}void enable(bool enabled){ if (enabled)cout << " 按鈕 " << name << " 被啟用\n\n";elsecout << " 按鈕 " << name << " 被禁用\n\n";}
};class Control_Radio : public Control
{
public:Control_Radio(const string& s, Media* ptr) : Control(s, ptr) {}void enable(bool enabled) {} // 對于單選按鈕,不必使用此函數(shù)void select(bool clicked) // 對于單選按鈕,增加此函數(shù){if(clicked)cout << " 單選按鈕 " << name << " 被啟用\n\n";elsecout << " 單選按鈕 " << name << " 被禁用\n\n";}
};class Control_text : public Control
{
private : string text; // 文本框里保存了賬號或密碼的內(nèi)容
public:Control_text(const string & t , const string& s, Media* ptr) : text(t) , Control(s, ptr) {}Control_text(const string& s, Media* ptr) : text(""), Control(s, ptr) {}void enable(bool enabled){if (enabled)cout << " 文本框 " << name << " 被啟用\n\n";elsecout << " 文本框 " << name << " 被禁用\n\n";}void setText(const string& s) { text = s; } // 對文本框增加此函數(shù)bool isEmpty() { return text.empty(); } // 判斷文本框是否為空
};class Media_UI : public Media
{
private:Control_Button* pLogin, * pLogout;Control_Radio* pRadioTourist, * pRadioAccount;Control_text *pTextName, * pTextPwd;
public:Media_UI(){}void init(Control_Button* pLin, Control_Button*pLout, Control_Radio* pTourist, Control_Radio* pAccount, Control_text* pName, Control_text* pPwd){pLogin = pLin; pLogout = pLout;pRadioTourist = pTourist; pRadioAccount = pAccount;pTextName = pName; pTextPwd = pPwd;}virtual void changed(Control* ptrCtrl){if (ptrCtrl == pLogout) { cout << " 游戲退出!\n\n"; return; }if (ptrCtrl == pRadioTourist){pRadioTourist->select(true);pRadioAccount->select(false);pTextName->enable(false);pTextPwd->enable(false);pLogin->enable(true);return;}if (ptrCtrl == pLogin) { cout << " 開始登錄驗(yàn)證\n\n"; }if (ptrCtrl == pRadioAccount){pRadioTourist->select(false);pRadioAccount->select(true);pTextName->enable(true);pTextPwd->enable(true);if (pTextName->isEmpty() || pTextPwd->isEmpty())pLogin->enable(false);elsepLogin->enable(true);}if (ptrCtrl == pTextName || ptrCtrl == pTextPwd){if (pTextName->isEmpty() || pTextPwd->isEmpty())pLogin->enable(false);elsepLogin->enable(true);}}
};int main()
{Media_UI media_UI;Control_Button login("登錄", &media_UI), logout("退出" , &media_UI);Control_Radio tourist("游客登錄", &media_UI), account("賬戶登錄" , &media_UI);Control_text name("昵稱", &media_UI), password("密碼" , &media_UI);media_UI.init(&login , &logout , & tourist , & account , & name , & password);login.changed(); logout.changed();tourist.changed(); account.changed();name.changed(); password.changed();name.setText("aaa"); password.setText("1234556");login.changed(); account.changed(); name.changed();return 0;
}
測試結(jié)果如下:
(21) 備忘錄模式 Memento , 也可以翻譯為更簡單的 Note 。例如游戲里在某個(gè)時(shí)刻保存玩家的當(dāng)時(shí)信息。
class Fighter; class Note // 備忘錄模式
{
private:int life, magic, attack;friend Fighter; // 本類對 Fighter 類開放所有權(quán)限Note(int life, int m, int a) :life(life), magic(m), attack(a) {} // 私有的構(gòu)造函數(shù)int getLife() { return life; }int getMagic() { return magic; }int getAttack() { return attack; }void setLife(int t) { life = t; }void setMagic(int t) { magic = t; }void setAttack(int t) { attack = t; }
public:};class Fighter
{
private:int life, magic, attack;
public:Fighter(int life, int m, int a) :life(life), magic(m), attack(a) {}Note* createNote() { return new Note(life ,magic , attack); } // 此處返回去的指針要記得回收內(nèi)存,可以用智能指針包裝下;void restoreFromNote(Note* ptr) { life = ptr->life; magic = ptr->magic; attack = ptr->attack; }void show() {cout << " 玩家當(dāng)前的 life , magic , attack : " << life << " " << magic << " " << attack << '\n';}void setStatus() { life /= 2; magic /= 2; attack /= 2; }
};int main()
{Fighter f(100,200,300);unique_ptr<Note> uniNote(f.createNote());f.show();f.setStatus();f.show();f.restoreFromNote(uniNote.get());f.show();return 0;
}
測試結(jié)果如下:
( 22) 責(zé)任鏈模式 Chain Of Responsibility 。比如員工的加薪申請,申請的金額越大,就越需要更高層的領(lǐng)導(dǎo)審批??梢园沿?fù)責(zé)審批的管理,創(chuàng)建為一個(gè)個(gè)對象,并把它們串起來。
class RaiseRequest // 本類的含義是來自某個(gè)人的加薪請求
{
private:string name;int request;
public:RaiseRequest(const string& s, int t) : name(s), request(t) {}int getRequest() const { return request; } // 這里的 const 不能省略,const 修飾的是 this
};class Manager // 可以審批加薪請求的領(lǐng)導(dǎo)
{
private:Manager* next = nullptr;
public:virtual ~Manager() {}void setChain(Manager* n) { next = n; }Manager* getNext() { return next; }virtual void processRequest(const RaiseRequest& raiReq) = 0;
};class Manager_Low : public Manager
{
public:virtual void processRequest(const RaiseRequest& raiReq){auto t = raiReq.getRequest();if (t < 5000)cout << " 加薪請求 " << t << " 已被低級管理處理\n\n";else if (getNext())getNext()->processRequest(raiReq);elsecout << " 沒有合適的管理;來處理此加薪請求\n\n";}
};class Manager_High : public Manager
{
public:virtual void processRequest(const RaiseRequest& raiReq){auto t = raiReq.getRequest();if ( t >= 5000 && t < 10000)cout << " 加薪請求 " << t << " 已被高級管理處理\n\n";else if (getNext())getNext()->processRequest(raiReq);elsecout << " 沒有合適的管理來處理此 " << t << " 的加薪請求\n\n";}
};int main()
{Manager_Low mL;Manager_High mH;mL.setChain(&mH);RaiseRequest a("zhangsan" , 500) , b("Lisi" , 6000) , c("wangWu" , 13000);mL.processRequest(a);mL.processRequest(b);mL.processRequest(c);return 0;
}
測試結(jié)果如下:
以上的責(zé)任鏈中,只需要有一個(gè)鏈節(jié)處理事務(wù)即可,叫單純責(zé)任鏈。但換個(gè)場景,比如網(wǎng)絡(luò)用語過濾,需要進(jìn)行性 、臟話 、 政治敏感詞,多重過濾,就叫非單純的責(zé)任鏈模式,鏈中的每個(gè)鏈節(jié)都要被調(diào)用,處理事務(wù)。
class Filter // 過濾器過濾文本,以使文本文明
{
private:Filter* next = nullptr;
public:virtual ~Filter() {}void setChain(Filter* n) { next = n; }Filter* getNext() { return next; }virtual void processRequest(string& str) = 0; // 以形參引用帶回返回值
};class Filter_Sexy : public Filter // 過濾性敏感詞
{
public:virtual void processRequest(string& str){cout << " sexy 敏感被處理\n\n";str += "XXX";if(getNext())getNext()->processRequest(str);}
};class Filter_Policy : public Filter // 過濾政治敏感詞
{
public:virtual void processRequest(string& str){cout << " policy 敏感被處理\n\n";str += "ZZZ";if (getNext())getNext()->processRequest(str);}
};int main()
{Filter_Sexy fSex;Filter_Policy fPlcy;fSex.setChain(&fPlcy);string s(" 測試文字 ");fSex.processRequest(s);cout << " 最終的測試文字: " << s << "\n\n";return 0;
}
測試結(jié)果如下:
(23) 訪問者模式 Visitor 。此模式是為了解決如下案例的編碼:
某人去看病,大夫列了一個(gè)藥品單。這些藥品就是一個(gè)個(gè)被訪問的對象。這些對象會被不同的人訪問。收費(fèi)處要根據(jù)藥的價(jià)格收費(fèi),藥房根據(jù)藥名取藥,營養(yǎng)師根據(jù)藥制定食譜,健身教練根據(jù)藥制定訓(xùn)練方案。這里引入雙重分派機(jī)制(double dispatch):誰被訪問,又是誰來訪問。非常貼合訪問者模式。以下給出代碼例子與測試結(jié)果。
class Visitor;class Medicine
{
public:virtual ~Medicine() {}virtual string getName() = 0;virtual double getPrice() = 0;virtual void accept(Visitor* visitor) = 0;
};class Medicine_Eye : public Medicine
{
public:virtual string getName() { return " 眼藥 "; }virtual double getPrice() { return 10.5; }virtual void accept(Visitor* visitor); // 這一步聲明不可少,不然報(bào)錯(cuò),而且與上面的 = 0 ,還不一樣
};class Medicine_Nose : public Medicine
{
public:virtual string getName() { return " 鼻藥 "; }virtual double getPrice() { return 100.5; }virtual void accept(Visitor* visitor); // 這一步聲明不可少
};class Visitor
{
public:virtual ~Visitor() {}virtual void visit_MediEye(Medicine_Eye * mediEye) = 0; virtual void visit_MediNose(Medicine_Nose * mediNose) = 0;
};class Visitor_Charge : public Visitor // 收費(fèi)人員
{
private: double totalPrice = 0;
public:void visit_MediEye(Medicine_Eye* mediEye){totalPrice += mediEye->getPrice();cout << " 收費(fèi)人員統(tǒng)計(jì)藥品 " << mediEye->getName() << " ,價(jià)格為: " << mediEye->getPrice() << "\n\n";}void visit_MediNose(Medicine_Nose* mediNose){totalPrice += mediNose->getPrice();cout << " 收費(fèi)人員統(tǒng)計(jì)藥品 " << mediNose->getName() << " ,價(jià)格為: " << mediNose->getPrice() << "\n\n";}double getTotalPrice() { return totalPrice; }
};class Visitor_Distribute : public Visitor // 收費(fèi)人員
{
public:void visit_MediEye(Medicine_Eye* mediEye){cout << " 藥房人員分發(fā)藥品 " << mediEye->getName() << "\n\n";}void visit_MediNose(Medicine_Nose* mediNose){cout << " 藥房人員分發(fā)藥品 " << mediNose->getName() << "\n\n";}
};class Visitor_Trainer : public Visitor
{
public:void visit_MediEye(Medicine_Eye* mediEye){cout << " 健身教練認(rèn)為要做眼保健操\n\n";}void visit_MediNose(Medicine_Nose* mediNose){cout << " 健身教練認(rèn)為要多跑步\n\n";}
};void Medicine_Eye ::accept(Visitor* visitor) { visitor->visit_MediEye(this); }
void Medicine_Nose::accept(Visitor* visitor) { visitor->visit_MediNose(this); }int main()
{Medicine_Eye mediEye;Medicine_Nose mediNose;Visitor_Charge visiCharge;Visitor_Distribute visiDistri;Visitor_Trainer visiTrainer;mediEye.accept(&visiCharge);mediEye.accept(&visiDistri);mediEye.accept(&visiTrainer);cout << " -------------------------------\n\n";mediNose.accept(&visiCharge);mediNose.accept(&visiDistri);mediNose.accept(&visiTrainer);cout << " 總的藥錢: " << visiCharge.getTotalPrice() << endl;return 0;
}
測試結(jié)果如下:
(24) 解釋器模式 Interpreter 。一門計(jì)算機(jī)語言,必須背后有一個(gè)匹配的編譯器對其支持,將其翻譯為機(jī)器語言,此門新的編程語言才是有效的。編譯器模式就是講如何為一門語言編寫匹配的編譯器。為舉例簡單,規(guī)定本語言只支持整數(shù)的加減運(yùn)算,沒有括號,且表示整數(shù)的字符必須是單個(gè)的英文字母。整數(shù)與加減運(yùn)算符之間,書寫的時(shí)候不留空格。因?yàn)闉檎Z言編寫編譯器具有類似的地方,故稱其為一種模式。
以下是例子代碼:
#include<xstring>
#include<map>
#include<stack>class Node // 對語言翻譯的結(jié)果是將其組織成語法樹。故需要定義樹節(jié)點(diǎn)的結(jié)構(gòu)
{
public:char type; // 節(jié)點(diǎn)類型,取值: v + -virtual ~Node() {}Node(char c) : type(c) {}virtual int interpret(map<char , int>& mapVar) = 0; // 形參對運(yùn)算符節(jié)點(diǎn)沒用,但對整數(shù)節(jié)點(diǎn)有用,返回變量名對應(yīng)的值
};class Node_Var : public Node // 變量節(jié)點(diǎn)
{
private : char varName ;
public:Node_Var(char name, char type) : varName(name), Node(type) {}int interpret( map<char, int>& mapVar) { return mapVar[varName]; }
};class Node_Symbol : public Node // 運(yùn)算符節(jié)點(diǎn)的父類
{
protected : Node* ptrLeft, * ptrRight;
public:Node_Symbol(Node* left, Node* right, char type) : ptrLeft(left), ptrRight(right), Node(type) {}Node* getLeftChild() { return ptrLeft; }Node* getRightChild() { return ptrRight; }
};class Node_Add : public Node_Symbol // 加號運(yùn)算符
{
public:Node_Add(Node* left, Node* right, char type) : Node_Symbol(left , right , type) {}virtual int interpret(map<char, int>& mapVar) // 本函數(shù)可以被遞歸調(diào)用,返回左子樹與右子樹的值的和{return ptrLeft->interpret(mapVar) + ptrRight->interpret(mapVar);}
};class Node_Sub : public Node_Symbol // 減號運(yùn)算符
{
public:Node_Sub(Node* left, Node* right, char type) : Node_Symbol(left, right, type) {}virtual int interpret(map<char, int>& mapVar) // 本函數(shù)可以被遞歸調(diào)用,返回左子樹與右子樹的值的差{return ptrLeft->interpret(mapVar) - ptrRight->interpret(mapVar);}
};Node* buildTree(string& expr) // #include<stack>
{stack<Node* > stackNode; // 這個(gè)棧很有作用Node* ptrLeft = nullptr, * ptrRight = nullptr;for (int i = 0; i < expr.size(); i++)switch (expr[i]){case '+':ptrLeft = stackNode.top();++i;ptrRight = new Node_Var(expr[i], 'v');stackNode.push( new Node_Add(ptrLeft , ptrRight , '+') );break;case '-':ptrLeft = stackNode.top();++i;ptrRight = new Node_Var(expr[i], 'v');stackNode.push(new Node_Sub(ptrLeft, ptrRight, '-'));break;default:stackNode.push( new Node_Var(expr[i] , 'v') );break;}return stackNode.top();
}void release(Node * node) // 回收語法樹占據(jù)的內(nèi)存,可能被遞歸調(diào)用
{if (node->type == 'v')delete node;else{release(((Node_Symbol*)node)->getLeftChild() );release(((Node_Symbol*)node)->getRightChild() );delete node;}
}int main()
{string expr("a-b+c+d");map<char, int> mapVar;mapVar.insert(pair('a' , 7));mapVar.insert(pair('b' , 9));mapVar.insert(pair('c' , 3));mapVar.insert(pair('d' , 2));Node* root = buildTree(expr);cout << " 表達(dá)式為: " << expr << "\n\n";cout << " 結(jié)果為 : " << root->interpret(mapVar) << "\n\n";release(root);return 0;
}
測試結(jié)果如下:
接著為本解釋器模式再舉一例。機(jī)器人控制,為此創(chuàng)建一門語言,我們可以使用如下 的語句: left walk 15 and up run 20 。
該語言有如下幾個(gè)關(guān)鍵字 : left 、 right 、 up 、 down ,表示運(yùn)動的方向; walk 、 run 表示運(yùn)動的方式 , 整數(shù)表示運(yùn)動的距離;and 表示銜接兩個(gè)運(yùn)動。用空格分隔關(guān)鍵字。我們?yōu)槠鋭?chuàng)建解釋器。
#pragma warning(disable : 4996) // 必須要有,要不 strcpy 與 strtok 都會報(bào)錯(cuò)#include<string>
#include<xstring>
#include<map>
#include<stack>
#include<vector>class Node
{
public:virtual ~Node() {}virtual string interpret() = 0;
};class Node_Direction : public Node
{
private: string direction;
public:Node_Direction(const string& s) : direction(s) {}virtual string interpret(){if (direction == "left") return "向左";else if (direction == "right") return "向右";else if (direction == "up") return "向上";else if (direction == "down") return "向下";else return "方向錯(cuò)誤";}
};class Node_mode : public Node // walk or run
{
private: string mode;
public:Node_mode(const string& s) : mode(s) {}virtual string interpret(){if (mode == "walk") return "走步";else if (mode == "run") return "跑步";else return "動作錯(cuò)誤";}
};class Node_Distance : public Node // 距離
{
private: string distance;
public:Node_Distance(const string& s) : distance(s) {}virtual string interpret() { return " " + distance + " 米 "; }
};class Node_Sentence : public Node
{
private:Node* ptrDistance; // 這里用父類指針,指向子類對象,否則報(bào)錯(cuò)Node* ptrDirection;Node* ptrMode;
public:Node_Sentence(Node* pDirect, Node* pMode, Node* pDistance) : ptrDirection(pDirect), ptrMode(pMode), ptrDistance(pDistance) {}Node* getDirection() { return ptrDirection; }Node* getMode() { return ptrMode; }Node* getDistance() { return ptrDistance; }virtual string interpret(){return ptrDirection->interpret() + ptrMode->interpret() + ptrDistance->interpret() ;}
};class Node_And : public Node
{
private:Node* ptrLeft , * ptrRight;
public:Node_And(Node* pL, Node* pR) : ptrLeft(pL), ptrRight(pR) {}Node* getLeft() { return ptrLeft; }Node* getRight() { return ptrRight; }virtual string interpret() { return ptrLeft->interpret() + " 又 " + ptrRight->interpret(); }
};Node* buidTree(string & expr)
{stack<Node*> stackNode;vector<string> vecStr;Node* pDirection = nullptr, * pMode = nullptr, * pDistence = nullptr, * pLeft = nullptr, * pRight = nullptr;char* cache = new char[expr.size() + 1 ];strcpy(cache , expr.c_str());char* pstrChild = strtok(cache , " ");while (pstrChild){vecStr.push_back(string(pstrChild)); // 把原來的語句拆分成了一個(gè)個(gè)語法節(jié)點(diǎn)pstrChild = strtok(NULL , " ");}delete[] cache;for( auto iter = vecStr.begin() ; iter != vecStr.end() ; iter++ )if (*iter == "and"){pLeft = stackNode.top();++iter;pDirection = new Node_Direction(*iter);++iter;pMode = new Node_mode(*iter);++iter;pDistence = new Node_Distance(*iter);pRight = new Node_Sentence(pDirection, pMode, pDistence);stackNode.push(new Node_And(pLeft , pRight));}else{pDirection = new Node_Direction(* iter);++iter;pMode = new Node_mode(* iter);++iter;pDistence = new Node_Distance(* iter);stackNode.push(new Node_Sentence(pDirection , pMode , pDistence));}return stackNode.top();
}void release(Node* ptr) // 本函數(shù)也會被遞歸調(diào)用
{Node_And* pAnd = dynamic_cast<Node_And*>(ptr);if (pAnd){release(pAnd->getLeft()) ;release(pAnd->getRight());}else{Node_Sentence* pSentance = dynamic_cast<Node_Sentence*>(ptr);if (pSentance){release(pSentance->getDirection());release(pSentance->getMode());release(pSentance->getDistance());}}delete ptr;
}int main()
{string expr("right walk 15 and up run 30");Node* ptrRoot = buidTree(expr);cout << " 機(jī)器人控制代碼為: " << expr << "\n\n";cout << " 解釋器解釋后: " << ptrRoot->interpret() << "\n\n";release(ptrRoot);return 0;
}
測試結(jié)果如下:
經(jīng) MFC 框架測試,沒有內(nèi)存泄露:
至此,王老師的 《c++ 設(shè)計(jì)模式》 的課本例子,已抄寫完畢,謝謝王老師。
謝謝