“對象性能”模式
面向對象很好的解決了“抽象”的問題,但是必不可免地要付出一定的代價。對于通常情況來講,面向對象的成本大都可以忽略不計。但是某些情況,面向對象所帶來的成本必須謹慎處理。
典型模式
Singleton
Flyweight
模式定義
保證一個類僅有一個實例,并提供一個該實例的全局訪問點。
——《設計模式》GoF
動機(Motivation)
在軟件系統中,經常有這樣一個特殊的類,必須保證它們在系統中只存在一個示例,才能確保他們的邏輯正確性、以及良好的效率。
如何繞過常規的構造器,提供一種機制來保證一個類只有一個實例?
這個應該類設計者的責任,而不是使用者的責任。
單件模式代碼:
[cpp]view plaincopyprint?
classSingleton{
private:
Singleton();
Singleton(constSingleton&?other);
public:
staticSingleton*?getInstance();
staticSingleton*?m_instance;
};
Singleton*?Singleton::m_instance=nullptr;
//線程非安全版本
Singleton*?Singleton::getInstance()?{
if(m_instance?==?nullptr)?{
m_instance?=newSingleton();
}
returnm_instance;
}
//線程安全版本,但鎖的代價過高
Singleton*?Singleton::getInstance()?{
Lock?lock;
if(m_instance?==?nullptr)?{
m_instance?=newSingleton();
}
returnm_instance;
}
class Singleton{
private:
Singleton();
Singleton(const Singleton& other);
public:
static Singleton* getInstance();
static Singleton* m_instance;
};
Singleton* Singleton::m_instance=nullptr;
//線程非安全版本
Singleton* Singleton::getInstance() {
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
//線程安全版本,但鎖的代價過高
Singleton* Singleton::getInstance() {
Lock lock;
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
結構:
要點總結
Singleton模式中的實例構造器可以設置為protected以允許子類派生。
Singleton模式一般不要支持拷貝構造函數和Clone接口,因為這有可能會導致多個對象實例,與Singleton模式的初衷相違背。
如何實現多線程環境下安全的Singleton?注意對雙檢查鎖的正確實現。
模式定義
運用共享技術有效地支持大量的細粒度對象
——《設計模式》GoF
動機(Motivation)
在軟件系統中采用純粹對象方案的問題 在于大量細粒度的對象會很快充斥在系統中,從而帶來很高的運行時代價——主要指內存需求方面的代價。
如何在避免大量·細粒度對象問題的同事,讓外部客戶程序仍然能夠透明地使用面向對象的方式來進行操作?
享元模式代碼:
[cpp]view plaincopyprint?
classFont?{
private:
//unique?object?key
string?key;
//object?state
//....
public:
Font(conststring&?key){
//...
}
};
?
classFontFactory{
private:
map?fontPool;
public:
Font*?GetFont(conststring&?key){
map::iterator?item=fontPool.find(key);
if(item!=footPool.end()){
returnfontPool[key];
}
else{
Font*?font?=newFont(key);
fontPool[key]=?font;
returnfont;
}
}
voidclear(){
//...
}
};
class Font {
private:
//unique object key
string key;
//object state
//....
public:
Font(const string& key){
//...
}
};
?
class FontFactory{
private:
map fontPool;
public:
Font* GetFont(const string& key){
map::iterator item=fontPool.find(key);
if(item!=footPool.end()){
return fontPool[key];
}
else{
Font* font = new Font(key);
fontPool[key]= font;
return font;
}
}
void clear(){
//...
}
};
結構:
要點總結
面向對象很好的解決了抽相性的問題,但是作為一個運行在機器中的程序實體,我們需要考慮對象的代價問題。Flyweight主要解決面向的代價問題,一般不觸及面向對象的抽象性問題。
Flyweight采用對象共享的做法來降低系統中的對象的個數,從而降低細粒度對象給系統帶來的內存壓力。在具體實現方面,要注意對像狀態的處理。
對象的數量太大,從而導致對像內存開銷加大——什么樣的數量才算大?這需要我們仔細根據具體應用情況進行評估,而不能憑空臆斷。
“狀態變化”模式
在組建構建過程中,某些對象的狀態經常面臨變化,如何對這些變化進行有效的管理?同時又維持高層模塊的穩定?“狀態變化”模式為這一個問題提供了一種解決方案。
典型模式
State
Memento
模式定義
允許一個對象在其內部狀態改變是改變它的行為。從而使對像看起來似乎修改其行為。
——《設計模式》GoF
動機(Motivation)
在軟件構建過程中,某些對象的狀態如果改變,其行為也會隨之而發生變化,比如文檔處于只讀狀態,其支持的行為和讀寫狀態支持的行為就可能會完全不同。
如何在運行時根據對象的狀態來透明地更改對象的行為?而不會為對象操作和狀態轉化之間引入緊耦合?
狀態模式代碼:
[cpp]view plaincopyprint?
classNetworkState{
public:
NetworkState*?pNext;
virtualvoidOperation1()=0;
virtualvoidOperation2()=0;
virtualvoidOperation3()=0;
virtual~NetworkState(){}
};
classOpenState?:publicNetworkState{
staticNetworkState*?m_instance;
public:
staticNetworkState*?getInstance(){
if(m_instance?==?nullptr)?{
m_instance?=newOpenState();
}
returnm_instance;
}
voidOperation1(){
//**********
pNext?=?CloseState::getInstance();
}
voidOperation2(){
//..........
pNext?=?ConnectState::getInstance();
}
voidOperation3(){
//
$$
pNext?=?OpenState::getInstance();
}
};
classCloseState:publicNetworkState{?}
//...
classNetworkProcessor{
NetworkState*?pState;
public:
NetworkProcessor(NetworkState*?pState){
this->pState?=?pState;
}
voidOperation1(){
//...
pState->Operation1();
pState?=?pState->pNext;
//...
}
voidOperation2(){
//...
pState->Operation2();
pState?=?pState->pNext;
//...
}
voidOperation3(){
//...
pState->Operation3();
pState?=?pState->pNext;
//...
}
};
class NetworkState{
public:
NetworkState* pNext;
virtual void Operation1()=0;
virtual void Operation2()=0;
virtual void Operation3()=0;
virtual ~NetworkState(){}
};
class OpenState :public NetworkState{
static NetworkState* m_instance;
public:
static NetworkState* getInstance(){
if (m_instance == nullptr) {
m_instance = new OpenState();
}
return m_instance;
}
void Operation1(){
//**********
pNext = CloseState::getInstance();
}
void Operation2(){
//..........
pNext = ConnectState::getInstance();
}
void Operation3(){
//$$$$$$$$$$
pNext = OpenState::getInstance();
}
};
class CloseState:public NetworkState{ }
//...
class NetworkProcessor{
NetworkState* pState;
public:
NetworkProcessor(NetworkState* pState){
this->pState = pState;
}
void Operation1(){
//...
pState->Operation1();
pState = pState->pNext;
//...
}
void Operation2(){
//...
pState->Operation2();
pState = pState->pNext;
//...
}
void Operation3(){
//...
pState->Operation3();
pState = pState->pNext;
//...
}
};
結構:
要點總結
State模式將所有與一個特定狀態相關的行為都放入一個State的子類對象中,在對像狀態切換時, 切換相應的對象;但同時維持State的接口,這樣實現了具體操作與狀態轉換之間的解耦。
為不同的狀態引入不同的對象使得狀態轉換變得更加明確,而且可以保證不會出現狀態不一致的情況,因為轉換是原子性的——即要么徹底轉換過來,要么不轉換。
如果State對象沒有實例變量,那么各個上下文可以共享同一個State對象,從而節省對象開銷。
模式定義
在不破壞封裝性的前提下,不活一個對象的內部狀態,并在該對像之外保存這個狀態。這樣以后就可以將該對像恢復到原想保存的狀態。
——《設計模式》GoF
動機(Motivation)
在軟件構建過程中,某些對象的狀態在轉會過程中,可能由于某種需求,要求程序能夠回溯到對像之前處于某個點時的狀態。如果使用一些公有借口來讓其它對象得到對象的狀態,便會暴露對象的實現細節。
如何實現對象狀態的良好保存與恢復?但同時又不會因此而破壞對象本身的封裝性。
Memento模式代碼:
[cpp]view plaincopyprint?
classMemento
{
string?state;
//..
public:
Memento(conststring?&?s)?:?state(s)?{}
string?getState()const{returnstate;?}
voidsetState(conststring?&?s)?{?state?=?s;?}
};
classOriginator
{
string?state;
//....
public:
Originator()?{}
Memento?createMomento()?{
Memento?m(state);
returnm;
}
voidsetMomento(constMemento?&?m)?{
state?=?m.getState();
}
};
intmain()
{
Originator?orginator;
//捕獲對象狀態,存儲到備忘錄
Memento?mem?=?orginator.createMomento();
//...?改變orginator狀態
//從備忘錄中恢復
orginator.setMomento(memento);
}
class Memento
{
string state;
//..
public:
Memento(const string & s) : state(s) {}
string getState() const { return state; }
void setState(const string & s) { state = s; }
};
class Originator
{
string state;
//....
public:
Originator() {}
Memento createMomento() {
Memento m(state);
return m;
}
void setMomento(const Memento & m) {
state = m.getState();
}
};
int main()
{
Originator orginator;
//捕獲對象狀態,存儲到備忘錄
Memento mem = orginator.createMomento();
//... 改變orginator狀態
//從備忘錄中恢復
orginator.setMomento(memento);
}
結構:
要點總結
備忘錄(Memento)存儲原發器(Originator)對象的內部狀態,在需要時恢復原發器的狀態。
Memento模式的核心是信息隱藏,即Originator需要向外接隱藏信息,保持其封裝性。但同時又需要將其狀態保持到外界(Memento)
由于現代語言運行時(如C#、java等)都具有相當的對象序列化支持,因此往往采用效率較高、又較容易正確實現的序列化方案來實現Memento模式。
“數據結構”模式
常常有一些組建在內部具有特定的數據結構,如果讓客戶程序依賴這些特定的數據結構,將極大的破壞組件的復用。這時候,將這些數據結構封裝在內部,在外部提供統一的接口,來實現與特定數據結構無關的訪問,是一種行之有效的解決方案。
典型模式
Composite
Iterator
Chain of Responsibility
模式定義
將對象組合成樹形結構以表示“部分-整體”的層級結構。Compisite使得用戶對單個對象和組合對象的使用具有一致性(穩定)。
——《設計模式》GoF
動機(Motivation)
軟件在某些情況下,客戶代碼過多地依賴于對像容器復雜的內部實現結構,對象容器內部實現結構(而非抽象接口)的變化將因其客戶代碼的頻繁變化,帶來了代碼的維護性、擴展性等弊端。
如何將“客戶代碼與復雜的對象容器結構”解耦?讓對象容器自己來實現自身的復雜結構,從而使得客戶代碼就像處理簡單對象一樣來處理復雜的對象容器?
Composite模式代碼:
[cpp]view plaincopyprint?
#include?
#include?
#include?
#include?
usingnamespacestd;
classComponent
{
public:
virtualvoidprocess()?=?0;
virtual~Component(){}
};
//樹節點
classComposite?:publicComponent{
string?name;
list?elements;
public:
Composite(conststring?&?s)?:?name(s)?{}
voidadd(Component*?element)?{
elements.push_back(element);
}
voidremove(Component*?element){
elements.remove(element);
}
voidprocess(){
//1.?process?current?node
//2.?process?leaf?nodes
for(auto?&e?:?elements)
e->process();//多態調用
}
};
//葉子節點
classLeaf?:publicComponent{
string?name;
public:
Leaf(string?s)?:?name(s)?{}
voidprocess(){
//process?current?node
}
};
voidInvoke(Component?&?c){
//...
c.process();
//...
}
intmain()
{
Composite?root("root");
Composite?treeNode1("treeNode1");
Composite?treeNode2("treeNode2");
Composite?treeNode3("treeNode3");
Composite?treeNode4("treeNode4");
Leaf?leat1("left1");
Leaf?leat2("left2");
root.add(&treeNode1);
treeNode1.add(&treeNode2);
treeNode2.add(&leaf1);
root.add(&treeNode3);
treeNode3.add(&treeNode4);
treeNode4.add(&leaf2);
process(root);
process(leaf2);
process(treeNode3);
}
#include
#include
#include
#include
using namespace std;
class Component
{
public:
virtual void process() = 0;
virtual ~Component(){}
};
//樹節點
class Composite : public Component{
string name;
list elements;
public:
Composite(const string & s) : name(s) {}
void add(Component* element) {
elements.push_back(element);
}
void remove(Component* element){
elements.remove(element);
}
void process(){
//1. process current node
//2. process leaf nodes
for (auto &e : elements)
e->process(); //多態調用
}
};
//葉子節點
class Leaf : public Component{
string name;
public:
Leaf(string s) : name(s) {}
void process(){
//process current node
}
};
void Invoke(Component & c){
//...
c.process();
//...
}
int main()
{
Composite root("root");
Composite treeNode1("treeNode1");
Composite treeNode2("treeNode2");
Composite treeNode3("treeNode3");
Composite treeNode4("treeNode4");
Leaf leat1("left1");
Leaf leat2("left2");
root.add(&treeNode1);
treeNode1.add(&treeNode2);
treeNode2.add(&leaf1);
root.add(&treeNode3);
treeNode3.add(&treeNode4);
treeNode4.add(&leaf2);
process(root);
process(leaf2);
process(treeNode3);
}
結構:
要點總結
Composite模式采用樹形結構來實現普遍存在的對象容器,從而將“一對多”的關系轉化為“一對一”的關系,使得客戶代碼可以一致地(復用)處理對象和對象容器,無需關心處理的是單個對象還是組合的對象容器。
將“客戶代碼與復雜的對象容器結構”解耦是Composite的核心思想,解耦之后,客戶代碼將與純粹的抽象接口——而非對像容器的內部實現結構——發生依賴,從而更能“應對變化”。
Composite模式在具體實現中,可以讓父對象中的子對象反向追溯;如果父對象有頻繁的遍歷需求,可使用緩存技巧來改善效率。
模式定義
提供一種方法順序訪問一個聚合對象中的各個元素,而又不暴露(隔離變化,穩定)該對象的內部表示。
——《設計模式》GoF
動機(Motivation)
在軟件構建過程中,集合對象內部結構常常變化各異。但對于這些集合對象,我們希望在不暴露其內部結構的同時,可以讓外部客戶代碼透明的訪問其中包含的元素;同時這種“透明遍歷”也為“同一種算法在多種集合對象上進行操作”提供了可能。
使用面向對象技術將這種遍歷機制抽象為“迭代器對象”為“因對變化中的集合對象”提供了一種優雅的方式。
Iterator模式代碼:
[cpp]view plaincopyprint?
template
classIterator
{
public:
virtualvoidfirst()?=?0;
virtualvoidnext()?=?0;
virtualboolisDone()const=?0;
virtualT&?current()?=?0;
};
template
classMyCollection{
public:
Iterator?GetIterator(){
//...
}
};
template
classCollectionIterator?:publicIterator{
MyCollection?mc;
public:
CollectionIterator(constMyCollection?&?c):?mc(c){?}
voidfirst()?override?{
}
voidnext()?override?{
}
boolisDone()constoverride{
}
T&?current()?override{
}
};
voidMyAlgorithm()
{
MyCollection?mc;
Iterator?iter=?mc.GetIterator();
for(iter.first();?!iter.isDone();?iter.next()){
cout?<<?iter.current()?<<?endl;
}
}
template
class Iterator
{
public:
virtual void first() = 0;
virtual void next() = 0;
virtual bool isDone() const = 0;
virtual T& current() = 0;
};
template
class MyCollection{
public:
Iterator GetIterator(){
//...
}
};
template
class CollectionIterator : public Iterator{
MyCollection mc;
public:
CollectionIterator(const MyCollection & c): mc(c){ }
void first() override {
}
void next() override {
}
bool isDone() const override{
}
T& current() override{
}
};
void MyAlgorithm()
{
MyCollection mc;
Iterator iter= mc.GetIterator();
for (iter.first(); !iter.isDone(); iter.next()){
cout << iter.current() << endl;
}
}
結構:
要點總結:
迭代抽象:訪問一個聚合對象的內容而無需暴露他的內部表示。
迭代多態:為遍歷不同的集合結構提供一個統一的接口,從而支持同樣的算法在不同的集合結構上進行操作。
迭代器健壯性考慮:遍歷的同時更改迭代器所在的集合結構,會導致問題。
模式定義
使多個對像都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關系。將這些對像連成一條鏈,并沿著這條鏈傳遞請求,直到有一個對象處理它為止。
——《設計模式》GoF
動機(Motivation)
在軟件構建的過程中,一個請求可能被多個對象處理,但是每個請求在運行時只能有一個接受者,如果顯示指定,將必不可少的帶來請求發送者與接受者的耦合。
如何使請求的發送者不需要指定具體的接受者?讓請求的接受者自己在運行時決定來處理請求,從而使兩者解耦。
職責鏈模式代碼:
[cpp]view plaincopyprint?
#include?
#include?
usingnamespacestd;
enumclassRequestType
{
REQ_HANDLER1,
REQ_HANDLER2,
REQ_HANDLER3
};
classReqest
{
string?description;
RequestType?reqType;
public:
Reqest(conststring?&?desc,?RequestType?type)?:?description(desc),?reqType(type)?{}
RequestType?getReqType()const{returnreqType;?}
conststring&?getDescription()const{returndescription;?}
};
classChainHandler{
ChainHandler?*nextChain;
voidsendReqestToNextHandler(constReqest?&?req)
{
if(nextChain?!=?nullptr)
nextChain->handle(req);
}
protected:
virtualboolcanHandleRequest(constReqest?&?req)?=?0;
virtualvoidprocessRequest(constReqest?&?req)?=?0;
public:
ChainHandler()?{?nextChain?=?nullptr;?}
voidsetNextChain(ChainHandler?*next)?{?nextChain?=?next;?}
voidhandle(constReqest?&?req)
{
if(canHandleRequest(req))
processRequest(req);
else
sendReqestToNextHandler(req);
}
};
classHandler1?:publicChainHandler{
protected:
boolcanHandleRequest(constReqest?&?req)?override
{
returnreq.getReqType()?==?RequestType::REQ_HANDLER1;
}
voidprocessRequest(constReqest?&?req)?override
{
cout?<<"Handler1?is?handle?reqest:?"<<?req.getDescription()?<<?endl;
}
};
classHandler2?:publicChainHandler{
protected:
boolcanHandleRequest(constReqest?&?req)?override
{
returnreq.getReqType()?==?RequestType::REQ_HANDLER2;
}
voidprocessRequest(constReqest?&?req)?override
{
cout?<<"Handler2?is?handle?reqest:?"<<?req.getDescription()?<<?endl;
}
};
classHandler3?:publicChainHandler{
protected:
boolcanHandleRequest(constReqest?&?req)?override
{
returnreq.getReqType()?==?RequestType::REQ_HANDLER3;
}
voidprocessRequest(constReqest?&?req)?override
{
cout?<<"Handler3?is?handle?reqest:?"<<?req.getDescription()?<<?endl;
}
};
intmain(){
Handler1?h1;
Handler2?h2;
Handler3?h3;
h1.setNextChain(&h2);
h2.setNextChain(&h3);
Reqest?req("process?task?...?",?RequestType::REQ_HANDLER3);
h1.handle(req);
return0;
}
#include
#include
using namespace std;
enum class RequestType
{
REQ_HANDLER1,
REQ_HANDLER2,
REQ_HANDLER3
};
class Reqest
{
string description;
RequestType reqType;
public:
Reqest(const string & desc, RequestType type) : description(desc), reqType(type) {}
RequestType getReqType() const { return reqType; }
const string& getDescription() const { return description; }
};
class ChainHandler{
ChainHandler *nextChain;
void sendReqestToNextHandler(const Reqest & req)
{
if (nextChain != nullptr)
nextChain->handle(req);
}
protected:
virtual bool canHandleRequest(const Reqest & req) = 0;
virtual void processRequest(const Reqest & req) = 0;
public:
ChainHandler() { nextChain = nullptr; }
void setNextChain(ChainHandler *next) { nextChain = next; }
void handle(const Reqest & req)
{
if (canHandleRequest(req))
processRequest(req);
else
sendReqestToNextHandler(req);
}
};
class Handler1 : public ChainHandler{
protected:
bool canHandleRequest(const Reqest & req) override
{
return req.getReqType() == RequestType::REQ_HANDLER1;
}
void processRequest(const Reqest & req) override
{
cout << "Handler1 is handle reqest: " << req.getDescription() << endl;
}
};
class Handler2 : public ChainHandler{
protected:
bool canHandleRequest(const Reqest & req) override
{
return req.getReqType() == RequestType::REQ_HANDLER2;
}
void processRequest(const Reqest & req) override
{
cout << "Handler2 is handle reqest: " << req.getDescription() << endl;
}
};
class Handler3 : public ChainHandler{
protected:
bool canHandleRequest(const Reqest & req) override
{
return req.getReqType() == RequestType::REQ_HANDLER3;
}
void processRequest(const Reqest & req) override
{
cout << "Handler3 is handle reqest: " << req.getDescription() << endl;
}
};
int main(){
Handler1 h1;
Handler2 h2;
Handler3 h3;
h1.setNextChain(&h2);
h2.setNextChain(&h3);
Reqest req("process task ... ", RequestType::REQ_HANDLER3);
h1.handle(req);
return 0;
}
結構:
要點總結
Chain of Responsibility模式的應用場合在于“一個請求可能有多個接受者,但是最后真正接受者只有一個”,這時候請求發送者與接受者的耦合可能出現“變化脆弱”的癥狀,職責鏈的目的就是將二者解耦,從而更好的應對變化。
應用了Chain of Responsibility模式后,對象的職責分派將更具靈活性。我們可以在運行時動態添加/修改請求的處理指責。
如果請求傳遞到職責鏈的末尾仍得不到處理,應該有一個合理的缺省機制。這也是每一個接受者對象的責任,而不是發出請求的對象的責任。
“行為變化”模式
在組建的構建過程中,組建行為的變化經常導致組建本身劇烈的變化?!靶袨樽兓蹦J綄⒔M建的行為和組建本身進行解耦,從而主持組件的變化,實現兩者之間的松耦合。
典型模式
Command
Visitor
模式定義
將一個請求(行為)封裝為對象,從而使你可用不同的請求,對客戶進行參數化;對請求排隊或記錄請求日志以及支持可撤銷的操作。
——《設計模式》GoF
動機(Motivation)
在軟件構建構成中,“行為請求者”與“行為實現者”通常呈現一種“緊耦合”。但在某些場合——比如需要對行為進行“記錄、撤銷(undo)、事務”等處理,這種無法抵御變化的緊耦合是不合適的。
在這種情況下,如何將“行為請求者”與“行為實現者”解耦?將一組行為抽象為對象,可以實現二者之間的松耦合。
Command模式代碼:
[cpp]view plaincopyprint?
#include?
#include?
#include?
usingnamespacestd;
classCommand
{
public:
virtualvoidexecute()?=?0;
};
classConcreteCommand1?:publicCommand
{
string?arg;
public:
ConcreteCommand1(conststring?&?a)?:?arg(a)?{}
voidexecute()?override
{
cout<<"#1?process..."<
}
};
classConcreteCommand2?:publicCommand
{
string?arg;
public:
ConcreteCommand2(conststring?&?a)?:?arg(a)?{}
voidexecute()?override
{
cout<<"#2?process..."<
}
};
classMacroCommand?:publicCommand
{
vector?commands;
public:
voidaddCommand(Command?*c)?{?commands.push_back(c);?}
voidexecute()?override
{
for(auto?&c?:?commands)
{
c->execute();
}
}
};
intmain()
{
ConcreteCommand1?command1(receiver,"Arg?###");
ConcreteCommand2?command2(receiver,"Arg?$$$");
MacroCommand?macro;
macro.addCommand(&command1);
macro.addCommand(&command2);
macro.execute();
}
#include
#include
#include
using namespace std;
class Command
{
public:
virtual void execute() = 0;
};
class ConcreteCommand1 : public Command
{
string arg;
public:
ConcreteCommand1(const string & a) : arg(a) {}
void execute() override
{
cout<< "#1 process..."<
}
};
class ConcreteCommand2 : public Command
{
string arg;
public:
ConcreteCommand2(const string & a) : arg(a) {}
void execute() override
{
cout<< "#2 process..."<
}
};
class MacroCommand : public Command
{
vector commands;
public:
void addCommand(Command *c) { commands.push_back(c); }
void execute() override
{
for (auto &c : commands)
{
c->execute();
}
}
};
int main()
{
ConcreteCommand1 command1(receiver, "Arg ###");
ConcreteCommand2 command2(receiver, "Arg $$$");
MacroCommand macro;
macro.addCommand(&command1);
macro.addCommand(&command2);
macro.execute();
}
結構
要點總結
Command模式的根本目的在于“行為請求者”與“行為實現者”解耦,在面向對象的語言中,常見的實現手段是“將行為抽象為對象”
實現Command接口的具體命令對象ConcreteCommand有時候根據需要可能會保存一些額外的狀態信息。通過使用Composite模式,可以將多個“命令”封裝為一個“符合命令”MacroCommand
Command模式與C++中的函數對像有些類似。但兩者定義行為接口的規范有所區別:Command以面向對象中的“接口-實現”來定義行為接口規范,更嚴格,但有性能損失;C++函數對象以函數簽名來定義行為接口規范,更靈活,性能能高。
模式定義
表示一個作用與某對像結構中的各元素的操作。使得可以在不改變(穩定)各元素的類的前提下定義(擴展)作用于這些元素的新操作(變化)。
——《設計模式》GoF
動機(Motivation)
在軟件構建的過程中,由于需求的改變,某些類層次結構中常常需要增加新的行為(方法)。如果直接在類中做這樣的更改,將會給子類帶來很繁重的變更負擔,甚至破壞原有設計。
如何在不更改類層次結構的前提下,在運行時根據需要透明地為類層次結構上的各個類動態添加新的操作,從而避免上述問題?
Visitor模式代碼:
[cpp]view plaincopyprint?
#include?
usingnamespacestd;
classVisitor;
classElement
{
public:
virtualvoidFunc1()?=?0;
virtualvoidFunc2(intdata)=0;
virtualvoidFunc3(intdata)=0;
//...
virtual~Element(){}
};
classElementA?:publicElement
{
public:
voidFunc1()?override{
//...
}
voidFunc2(intdata)?override{
//...
}
};
classElementB?:publicElement
{
public:
voidFunc1()?override{
//***
}
voidFunc2(intdata)?override?{
//***
}
};
#include
using namespace std;
class Visitor;
class Element
{
public:
virtual void Func1() = 0;
virtual void Func2(int data)=0;
virtual void Func3(int data)=0;
//...
virtual ~Element(){}
};
class ElementA : public Element
{
public:
void Func1() override{
//...
}
void Func2(int data) override{
//...
}
};
class ElementB : public Element
{
public:
void Func1() override{
//***
}
void Func2(int data) override {
//***
}
};
結構
要點總結
Vistor模式通過所謂的雙重分發(double dispatch)來實現在不更改(不添加新的操作-編譯時)Element類層次結構的前提下,在運行時透明地為類層次結構上的各個類動態添加新的操作(支持變化)。
所謂雙重分發即Vistor模式中間包括了兩個多態分發(注意其中的多態機制):第一個accept方法的多態解析;第二個為visitElementX方法的多態辨析。
Visitor模式最大的缺點在于擴展類層次結構(增添新的Element子類),會導致Visitor類的改變。因此Visitor模式適用于“Element類層次結構穩定,而其中的操作卻進場面臨頻繁改動”。
“領域規則”模式
在特定領域內,某些變化雖然頻繁,但可以抽象為某種規則。這時候,結合特定領域,將問題抽象為語法規則,從而給出該領域下的一般性解決方案。
典型模式
Interpreter
模式定義
給定一個語言,定義它的文法的一種表示,并定義一種解釋器,這個解釋器使用該表示來解釋語言中的句子。
——《設計模式》GoF
動機(Motivation)
在軟件構建過程中,如果某一特定領域的問題比較復雜,類似的結構不斷的重復出現,如果使用普通的變成方式來實現將面臨非常頻繁的變化。
在這種情況下,將特定領域的問題表達為某種語法規則下的句子,然后構建一個解析器來解釋這樣的句子,從而達到解決問題的目的。
Interpret模式代碼:
[cpp]view plaincopyprint?
#include?
#include?
#include?
usingnamespacestd;
classExpression?{
public:
virtualintinterpreter(map?var)=0;
virtual~Expression(){}
};
//變量表達式
classVarExpression:publicExpression?{
charkey;
public:
VarExpression(constchar&?key)
{
this->key?=?key;
}
intinterpreter(map?var)?override?{
returnvar[key];
}
};
//符號表達式
classSymbolExpression?:publicExpression?{
//?運算符左右兩個參數
protected:
Expression*?left;
Expression*?right;
public:
SymbolExpression(?Expression*?left,??Expression*?right):
left(left),right(right){
}
};
//加法運算
classAddExpression?:publicSymbolExpression?{
public:
AddExpression(Expression*?left,?Expression*?right):
SymbolExpression(left,right){
}
intinterpreter(map?var)?override?{
returnleft->interpreter(var)?+?right->interpreter(var);
}
};
//減法運算
classSubExpression?:publicSymbolExpression?{
public:
SubExpression(Expression*?left,?Expression*?right):
SymbolExpression(left,right){
}
intinterpreter(map?var)?override?{
returnleft->interpreter(var)?-?right->interpreter(var);
}
};
Expression*??analyse(string?expStr)?{
stack?expStack;
Expression*?left?=?nullptr;
Expression*?right?=?nullptr;
for(inti=0;?i
{
switch(expStr[i])
{
case'+':
//?加法運算
left?=?expStack.top();
right?=newVarExpression(expStr[++i]);
expStack.push(newAddExpression(left,?right));
break;
case'-':
//?減法運算
left?=?expStack.top();
right?=newVarExpression(expStr[++i]);
expStack.push(newSubExpression(left,?right));
break;
default:
//?變量表達式
expStack.push(newVarExpression(expStr[i]));
}
}
Expression*?expression?=?expStack.top();
returnexpression;
}
voidrelease(Expression*?expression){
//釋放表達式樹的節點內存...
}
intmain(intargc,constchar*?argv[])?{
string?expStr?="a+b-c+d-e";
map?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);
intresult=expression->interpreter(var);
cout<
release(expression);
return0;
}
#include
#include
#include
using namespace std;
class Expression {
public:
virtual int interpreter(map var)=0;
virtual ~Expression(){}
};
//變量表達式
class VarExpression: public Expression {
char key;
public:
VarExpression(const char& key)
{
this->key = key;
}
int interpreter(map var) override {
return var[key];
}
};
//符號表達式
class SymbolExpression : public Expression {
// 運算符左右兩個參數
protected:
Expression* left;
Expression* right;
public:
SymbolExpression( Expression* left,? Expression* right):
left(left),right(right){
}
};
//加法運算
class AddExpression : public SymbolExpression {
public:
AddExpression(Expression* left, Expression* right):
SymbolExpression(left,right){
}
int interpreter(map 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 var) override {
return left->interpreter(var) - right->interpreter(var);
}
};
Expression*? analyse(string expStr) {
stack expStack;
Expression* left = nullptr;
Expression* right = nullptr;
for(int i=0; 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;
}
void release(Expression* expression){
//釋放表達式樹的節點內存...
}
int main(int argc, const char * argv[]) {
string expStr = "a+b-c+d-e";
map 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<
release(expression);
return 0;
}
結構
要點總結
Interpreter模式的應用場合是Interpreter模式應用中的難點,只有滿足“業務規則頻繁變化,且類似的結構不斷重復出現,并且容易抽象為語法規則的問題”才適合使用Interpreter模式。
使用Interpreter模式來表示文法規則,從而可以使用面向對象技巧來方便地“擴展”文法。
Interpreter模式比較適合簡單的文法表示,對于復雜的文法表示,Interpreter模式會產生比較大的類層次結構,需要求助于語法分析生成器這樣的標準工具。