C++實作設計模式——裝飾者(decorator)模式
- 裝飾者(decorator)模式定義
動态地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。不改變接口的前提下,增強所考慮的類的性能。
- 何時使用
1)需要擴充一個類的功能,或給一個類增加附加責任。
2)需要動态的給一個對象增加功能,這些功能可以再動态地撤銷。
3)需要增加一些基本功能的排列組合而産生的非常大量的功能。
注意:裝飾者模式的根本是裝飾而不是改變接口,原先調用的什麼接口,最後還是會調用到這個接口,也就是裝飾角色的接口與抽象構件角色的接口是完全一緻的,也叫做完全透明裝飾者模式,反之如果接口發生了改變,那就是一種擴充卡了。
舉個通俗的例子:
我們大家都玩遊戲吧,都知道有皮膚這個東西,假設原始角色展示是通過一個show這個接口來展現遊戲角色的,皮膚就是一個裝飾器,那穿戴了皮膚就是調用了裝飾器的show接口,差別在于裝飾器的show裡面比普通的原始的show接口多了添加皮膚的邏輯,裝飾器show在添加了擷取皮膚的邏輯後,最後還是會調用原來的原始show接口進行展示。它的根本還是用原始的show展示角色,沒有改變原來的接口,綜上就是兜兜轉轉還是你的那種感覺。
再給大家舉一個實際中會用到的例子,大家在開發中經常會用到生成日志檔案吧,而往往不同的場合會生成不同格式的日志内容,比如有json的,xml的,表格的各種形式,如果用繼承的方式就需要在列印日志的類下不停的派生各種子類,然後重載日志輸出接口,這樣類的複雜度越來越高。此時如果用裝飾者,可以需要什麼模式的時候就裝飾什麼。代碼如下:
#include<iostream>
using namespace std;
class log { //整個日志類
public:
virtual void log_out(string filename, string value) {
cout<<"輸出日志"<<value<<"到"<<filename<<"檔案"<<endl;
}
};
class errlog:public log { //錯誤日志輸出類
public:
void log_out(string filename, string value) {
cout<<"輸出錯誤日志"<<value<<"到"<<filename<<"檔案"<<endl;
}
};
class debuglog:public log { //debug日志輸出類
public:
void log_out(string filename, string value) {
cout<<"輸出debug日志"<<value<<"到"<<filename<<"檔案"<<endl;
}
};
class decoratorlog:public log {//日志輸出裝飾器
public:
log *m_logout;
decoratorlog() {}
decoratorlog(log *logout) {
this->m_logout = logout;
}
void log_out(string filename, string value) {
cout<<"輸出debug日志"<<value<<"到"<<filename<<"檔案"<<endl;
}
};
class jsondecorator:public decoratorlog {//json日志輸出裝飾器
public:
jsondecorator(log *logout) {
this->m_logout = logout;
}
void log_out(string filename, string value) {
cout<<"value進行了json轉換"<<endl; //裝飾器的作用,也就是需要擴充的新功能需求
m_logout->log_out(filename,value); //調用到原先的接口
}
};
class xmldecorator:public decoratorlog {//json日志輸出裝飾器
public:
xmldecorator(log *logout) {
this->m_logout = logout;
}
void log_out(string filename, string value) {
cout<<"value進行了xml轉換"<<endl; //裝飾器的作用,也就是需要擴充的新功能需求
m_logout->log_out(filename,value); //調用到原先的接口
}
};
int main() {
log *log1 = new jsondecorator( new debuglog());
log1->log_out("1.json","hello");
log *log2 = new xmldecorator( new errlog());
log2->log_out("1.xml","world");
return 0;
}
/*
value進行了json轉換
輸出debug日志hello到1.json檔案
value進行了xml轉換
輸出錯誤日志world到1.xml檔案
*/
這個模式的思想就是,裝飾者就隻是裝飾,無論用到哪個裝飾器子類,最後還是會調用到實際的log_out接口函數。
- 裝飾者模式的結構