(Boolan)C++設(shè)計(jì)模式學(xué)習(xí)筆記(第一周)

23種設(shè)計(jì)模式

“對(duì)象性能”模式

面向?qū)ο蠛芎玫慕鉀Q了“抽象”的問題,但是必不可免地要付出一定的代價(jià)。對(duì)于通常情況來講,面向?qū)ο蟮某杀敬蠖伎梢院雎圆挥?jì)。但是某些情況,面向?qū)ο笏鶐淼某杀颈仨氈?jǐn)慎處理。

典型模式

Singleton

Flyweight

14.Singleton單件模式

模式定義

保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)該實(shí)例的全局訪問點(diǎn)。

——《設(shè)計(jì)模式》GoF

動(dòng)機(jī)(Motivation)

在軟件系統(tǒng)中,經(jīng)常有這樣一個(gè)特殊的類,必須保證它們?cè)谙到y(tǒng)中只存在一個(gè)示例,才能確保他們的邏輯正確性、以及良好的效率。

如何繞過常規(guī)的構(gòu)造器,提供一種機(jī)制來保證一個(gè)類只有一個(gè)實(shí)例?

這個(gè)應(yīng)該類設(shè)計(jì)者的責(zé)任,而不是使用者的責(zé)任。

單件模式代碼:

[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;

}

//線程安全版本,但鎖的代價(jià)過高

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;

}

//線程安全版本,但鎖的代價(jià)過高

Singleton* Singleton::getInstance() {

Lock lock;

if (m_instance == nullptr) {

m_instance = new Singleton();

}

return m_instance;

}

結(jié)構(gòu):

要點(diǎn)總結(jié)

Singleton模式中的實(shí)例構(gòu)造器可以設(shè)置為protected以允許子類派生。

Singleton模式一般不要支持拷貝構(gòu)造函數(shù)和Clone接口,因?yàn)檫@有可能會(huì)導(dǎo)致多個(gè)對(duì)象實(shí)例,與Singleton模式的初衷相違背。

如何實(shí)現(xiàn)多線程環(huán)境下安全的Singleton?注意對(duì)雙檢查鎖的正確實(shí)現(xiàn)。

15.享元模式FlyWeight

模式定義

運(yùn)用共享技術(shù)有效地支持大量的細(xì)粒度對(duì)象

——《設(shè)計(jì)模式》GoF

動(dòng)機(jī)(Motivation)

在軟件系統(tǒng)中采用純粹對(duì)象方案的問題 在于大量細(xì)粒度的對(duì)象會(huì)很快充斥在系統(tǒng)中,從而帶來很高的運(yùn)行時(shí)代價(jià)——主要指內(nèi)存需求方面的代價(jià)。

如何在避免大量·細(xì)粒度對(duì)象問題的同事,讓外部客戶程序仍然能夠透明地使用面向?qū)ο蟮姆绞絹磉M(jìn)行操作?

享元模式代碼:

[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(){

//...

}

};

結(jié)構(gòu):

要點(diǎn)總結(jié)

面向?qū)ο蠛芎玫慕鉀Q了抽相性的問題,但是作為一個(gè)運(yùn)行在機(jī)器中的程序?qū)嶓w,我們需要考慮對(duì)象的代價(jià)問題。Flyweight主要解決面向的代價(jià)問題,一般不觸及面向?qū)ο蟮某橄笮詥栴}。

Flyweight采用對(duì)象共享的做法來降低系統(tǒng)中的對(duì)象的個(gè)數(shù),從而降低細(xì)粒度對(duì)象給系統(tǒng)帶來的內(nèi)存壓力。在具體實(shí)現(xiàn)方面,要注意對(duì)像狀態(tài)的處理。

對(duì)象的數(shù)量太大,從而導(dǎo)致對(duì)像內(nèi)存開銷加大——什么樣的數(shù)量才算大?這需要我們仔細(xì)根據(jù)具體應(yīng)用情況進(jìn)行評(píng)估,而不能憑空臆斷。

“狀態(tài)變化”模式

在組建構(gòu)建過程中,某些對(duì)象的狀態(tài)經(jīng)常面臨變化,如何對(duì)這些變化進(jìn)行有效的管理?同時(shí)又維持高層模塊的穩(wěn)定?“狀態(tài)變化”模式為這一個(gè)問題提供了一種解決方案。

典型模式

State

Memento

16.State狀態(tài)模式

模式定義

允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變是改變它的行為。從而使對(duì)像看起來似乎修改其行為。

——《設(shè)計(jì)模式》GoF

動(dòng)機(jī)(Motivation)

在軟件構(gòu)建過程中,某些對(duì)象的狀態(tài)如果改變,其行為也會(huì)隨之而發(fā)生變化,比如文檔處于只讀狀態(tài),其支持的行為和讀寫狀態(tài)支持的行為就可能會(huì)完全不同。

如何在運(yùn)行時(shí)根據(jù)對(duì)象的狀態(tài)來透明地更改對(duì)象的行為?而不會(huì)為對(duì)象操作和狀態(tài)轉(zhuǎn)化之間引入緊耦合?

狀態(tài)模式代碼:

[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;

//...

}

};

結(jié)構(gòu):

要點(diǎn)總結(jié)

State模式將所有與一個(gè)特定狀態(tài)相關(guān)的行為都放入一個(gè)State的子類對(duì)象中,在對(duì)像狀態(tài)切換時(shí), 切換相應(yīng)的對(duì)象;但同時(shí)維持State的接口,這樣實(shí)現(xiàn)了具體操作與狀態(tài)轉(zhuǎn)換之間的解耦。

為不同的狀態(tài)引入不同的對(duì)象使得狀態(tài)轉(zhuǎn)換變得更加明確,而且可以保證不會(huì)出現(xiàn)狀態(tài)不一致的情況,因?yàn)檗D(zhuǎn)換是原子性的——即要么徹底轉(zhuǎn)換過來,要么不轉(zhuǎn)換。

如果State對(duì)象沒有實(shí)例變量,那么各個(gè)上下文可以共享同一個(gè)State對(duì)象,從而節(jié)省對(duì)象開銷。

17.Memento備忘錄

模式定義

在不破壞封裝性的前提下,不活一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)像之外保存這個(gè)狀態(tài)。這樣以后就可以將該對(duì)像恢復(fù)到原想保存的狀態(tài)。

——《設(shè)計(jì)模式》GoF

動(dòng)機(jī)(Motivation)

在軟件構(gòu)建過程中,某些對(duì)象的狀態(tài)在轉(zhuǎn)會(huì)過程中,可能由于某種需求,要求程序能夠回溯到對(duì)像之前處于某個(gè)點(diǎn)時(shí)的狀態(tài)。如果使用一些公有借口來讓其它對(duì)象得到對(duì)象的狀態(tài),便會(huì)暴露對(duì)象的實(shí)現(xiàn)細(xì)節(jié)。

如何實(shí)現(xiàn)對(duì)象狀態(tài)的良好保存與恢復(fù)?但同時(shí)又不會(huì)因此而破壞對(duì)象本身的封裝性。

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;

//捕獲對(duì)象狀態(tài),存儲(chǔ)到備忘錄

Memento?mem?=?orginator.createMomento();

//...?改變orginator狀態(tài)

//從備忘錄中恢復(fù)

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;

//捕獲對(duì)象狀態(tài),存儲(chǔ)到備忘錄

Memento mem = orginator.createMomento();

//... 改變orginator狀態(tài)

//從備忘錄中恢復(fù)

orginator.setMomento(memento);

}

結(jié)構(gòu):

要點(diǎn)總結(jié)

備忘錄(Memento)存儲(chǔ)原發(fā)器(Originator)對(duì)象的內(nèi)部狀態(tài),在需要時(shí)恢復(fù)原發(fā)器的狀態(tài)。

Memento模式的核心是信息隱藏,即Originator需要向外接隱藏信息,保持其封裝性。但同時(shí)又需要將其狀態(tài)保持到外界(Memento)

由于現(xiàn)代語言運(yùn)行時(shí)(如C#、java等)都具有相當(dāng)?shù)膶?duì)象序列化支持,因此往往采用效率較高、又較容易正確實(shí)現(xiàn)的序列化方案來實(shí)現(xiàn)Memento模式。

“數(shù)據(jù)結(jié)構(gòu)”模式

常常有一些組建在內(nèi)部具有特定的數(shù)據(jù)結(jié)構(gòu),如果讓客戶程序依賴這些特定的數(shù)據(jù)結(jié)構(gòu),將極大的破壞組件的復(fù)用。這時(shí)候,將這些數(shù)據(jù)結(jié)構(gòu)封裝在內(nèi)部,在外部提供統(tǒng)一的接口,來實(shí)現(xiàn)與特定數(shù)據(jù)結(jié)構(gòu)無關(guān)的訪問,是一種行之有效的解決方案。

典型模式

Composite

Iterator

Chain of Responsibility

18.Composite模式

模式定義

將對(duì)象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層級(jí)結(jié)構(gòu)。Compisite使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性(穩(wěn)定)。

——《設(shè)計(jì)模式》GoF

動(dòng)機(jī)(Motivation)

軟件在某些情況下,客戶代碼過多地依賴于對(duì)像容器復(fù)雜的內(nèi)部實(shí)現(xiàn)結(jié)構(gòu),對(duì)象容器內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)(而非抽象接口)的變化將因其客戶代碼的頻繁變化,帶來了代碼的維護(hù)性、擴(kuò)展性等弊端。

如何將“客戶代碼與復(fù)雜的對(duì)象容器結(jié)構(gòu)”解耦?讓對(duì)象容器自己來實(shí)現(xiàn)自身的復(fù)雜結(jié)構(gòu),從而使得客戶代碼就像處理簡(jiǎn)單對(duì)象一樣來處理復(fù)雜的對(duì)象容器?

Composite模式代碼:

[cpp]view plaincopyprint?

#include?

#include?

#include?

#include?

usingnamespacestd;

classComponent

{

public:

virtualvoidprocess()?=?0;

virtual~Component(){}

};

//樹節(jié)點(diǎn)

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();//多態(tài)調(diào)用

}

};

//葉子節(jié)點(diǎn)

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(){}

};

//樹節(jié)點(diǎn)

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(); //多態(tài)調(diào)用

}

};

//葉子節(jié)點(diǎn)

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);

}

結(jié)構(gòu):

要點(diǎn)總結(jié)

Composite模式采用樹形結(jié)構(gòu)來實(shí)現(xiàn)普遍存在的對(duì)象容器,從而將“一對(duì)多”的關(guān)系轉(zhuǎn)化為“一對(duì)一”的關(guān)系,使得客戶代碼可以一致地(復(fù)用)處理對(duì)象和對(duì)象容器,無需關(guān)心處理的是單個(gè)對(duì)象還是組合的對(duì)象容器。

將“客戶代碼與復(fù)雜的對(duì)象容器結(jié)構(gòu)”解耦是Composite的核心思想,解耦之后,客戶代碼將與純粹的抽象接口——而非對(duì)像容器的內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)——發(fā)生依賴,從而更能“應(yīng)對(duì)變化”。

Composite模式在具體實(shí)現(xiàn)中,可以讓父對(duì)象中的子對(duì)象反向追溯;如果父對(duì)象有頻繁的遍歷需求,可使用緩存技巧來改善效率。

19.Iterator迭代器

模式定義

提供一種方法順序訪問一個(gè)聚合對(duì)象中的各個(gè)元素,而又不暴露(隔離變化,穩(wěn)定)該對(duì)象的內(nèi)部表示。

——《設(shè)計(jì)模式》GoF

動(dòng)機(jī)(Motivation)

在軟件構(gòu)建過程中,集合對(duì)象內(nèi)部結(jié)構(gòu)常常變化各異。但對(duì)于這些集合對(duì)象,我們希望在不暴露其內(nèi)部結(jié)構(gòu)的同時(shí),可以讓外部客戶代碼透明的訪問其中包含的元素;同時(shí)這種“透明遍歷”也為“同一種算法在多種集合對(duì)象上進(jìn)行操作”提供了可能。

使用面向?qū)ο蠹夹g(shù)將這種遍歷機(jī)制抽象為“迭代器對(duì)象”為“因?qū)ψ兓械募蠈?duì)象”提供了一種優(yōu)雅的方式。

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;

}

}

結(jié)構(gòu):

要點(diǎn)總結(jié):

迭代抽象:訪問一個(gè)聚合對(duì)象的內(nèi)容而無需暴露他的內(nèi)部表示。

迭代多態(tài):為遍歷不同的集合結(jié)構(gòu)提供一個(gè)統(tǒng)一的接口,從而支持同樣的算法在不同的集合結(jié)構(gòu)上進(jìn)行操作。

迭代器健壯性考慮:遍歷的同時(shí)更改迭代器所在的集合結(jié)構(gòu),會(huì)導(dǎo)致問題。

20.Chain of Resposibility職責(zé)鏈

模式定義

使多個(gè)對(duì)像都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對(duì)像連成一條鏈,并沿著這條鏈傳遞請(qǐng)求,直到有一個(gè)對(duì)象處理它為止。

——《設(shè)計(jì)模式》GoF

動(dòng)機(jī)(Motivation)

在軟件構(gòu)建的過程中,一個(gè)請(qǐng)求可能被多個(gè)對(duì)象處理,但是每個(gè)請(qǐng)求在運(yùn)行時(shí)只能有一個(gè)接受者,如果顯示指定,將必不可少的帶來請(qǐng)求發(fā)送者與接受者的耦合。

如何使請(qǐng)求的發(fā)送者不需要指定具體的接受者?讓請(qǐng)求的接受者自己在運(yùn)行時(shí)決定來處理請(qǐng)求,從而使兩者解耦。

職責(zé)鏈模式代碼:

[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;

}

結(jié)構(gòu):

要點(diǎn)總結(jié)

Chain of Responsibility模式的應(yīng)用場(chǎng)合在于“一個(gè)請(qǐng)求可能有多個(gè)接受者,但是最后真正接受者只有一個(gè)”,這時(shí)候請(qǐng)求發(fā)送者與接受者的耦合可能出現(xiàn)“變化脆弱”的癥狀,職責(zé)鏈的目的就是將二者解耦,從而更好的應(yīng)對(duì)變化。

應(yīng)用了Chain of Responsibility模式后,對(duì)象的職責(zé)分派將更具靈活性。我們可以在運(yùn)行時(shí)動(dòng)態(tài)添加/修改請(qǐng)求的處理指責(zé)。

如果請(qǐng)求傳遞到職責(zé)鏈的末尾仍得不到處理,應(yīng)該有一個(gè)合理的缺省機(jī)制。這也是每一個(gè)接受者對(duì)象的責(zé)任,而不是發(fā)出請(qǐng)求的對(duì)象的責(zé)任。

“行為變化”模式

在組建的構(gòu)建過程中,組建行為的變化經(jīng)常導(dǎo)致組建本身劇烈的變化?!靶袨樽兓蹦J綄⒔M建的行為和組建本身進(jìn)行解耦,從而主持組件的變化,實(shí)現(xiàn)兩者之間的松耦合。

典型模式

Command

Visitor

21.Command命令模式

模式定義

將一個(gè)請(qǐng)求(行為)封裝為對(duì)象,從而使你可用不同的請(qǐng)求,對(duì)客戶進(jìn)行參數(shù)化;對(duì)請(qǐng)求排隊(duì)或記錄請(qǐng)求日志以及支持可撤銷的操作。

——《設(shè)計(jì)模式》GoF

動(dòng)機(jī)(Motivation)

在軟件構(gòu)建構(gòu)成中,“行為請(qǐng)求者”與“行為實(shí)現(xiàn)者”通常呈現(xiàn)一種“緊耦合”。但在某些場(chǎng)合——比如需要對(duì)行為進(jìn)行“記錄、撤銷(undo)、事務(wù)”等處理,這種無法抵御變化的緊耦合是不合適的。

在這種情況下,如何將“行為請(qǐng)求者”與“行為實(shí)現(xiàn)者”解耦?將一組行為抽象為對(duì)象,可以實(shí)現(xiàn)二者之間的松耦合。

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();

}

結(jié)構(gòu)

要點(diǎn)總結(jié)

Command模式的根本目的在于“行為請(qǐng)求者”與“行為實(shí)現(xiàn)者”解耦,在面向?qū)ο蟮恼Z言中,常見的實(shí)現(xiàn)手段是“將行為抽象為對(duì)象”

實(shí)現(xiàn)Command接口的具體命令對(duì)象ConcreteCommand有時(shí)候根據(jù)需要可能會(huì)保存一些額外的狀態(tài)信息。通過使用Composite模式,可以將多個(gè)“命令”封裝為一個(gè)“符合命令”MacroCommand

Command模式與C++中的函數(shù)對(duì)像有些類似。但兩者定義行為接口的規(guī)范有所區(qū)別:Command以面向?qū)ο笾械摹敖涌?實(shí)現(xiàn)”來定義行為接口規(guī)范,更嚴(yán)格,但有性能損失;C++函數(shù)對(duì)象以函數(shù)簽名來定義行為接口規(guī)范,更靈活,性能能高。

22.Visitor訪問器

模式定義

表示一個(gè)作用與某對(duì)像結(jié)構(gòu)中的各元素的操作。使得可以在不改變(穩(wěn)定)各元素的類的前提下定義(擴(kuò)展)作用于這些元素的新操作(變化)。

——《設(shè)計(jì)模式》GoF

動(dòng)機(jī)(Motivation)

在軟件構(gòu)建的過程中,由于需求的改變,某些類層次結(jié)構(gòu)中常常需要增加新的行為(方法)。如果直接在類中做這樣的更改,將會(huì)給子類帶來很繁重的變更負(fù)擔(dān),甚至破壞原有設(shè)計(jì)。

如何在不更改類層次結(jié)構(gòu)的前提下,在運(yùn)行時(shí)根據(jù)需要透明地為類層次結(jié)構(gòu)上的各個(gè)類動(dòng)態(tài)添加新的操作,從而避免上述問題?

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 {

//***

}

};

結(jié)構(gòu)

要點(diǎn)總結(jié)

Vistor模式通過所謂的雙重分發(fā)(double dispatch)來實(shí)現(xiàn)在不更改(不添加新的操作-編譯時(shí))Element類層次結(jié)構(gòu)的前提下,在運(yùn)行時(shí)透明地為類層次結(jié)構(gòu)上的各個(gè)類動(dòng)態(tài)添加新的操作(支持變化)。

所謂雙重分發(fā)即Vistor模式中間包括了兩個(gè)多態(tài)分發(fā)(注意其中的多態(tài)機(jī)制):第一個(gè)accept方法的多態(tài)解析;第二個(gè)為visitElementX方法的多態(tài)辨析。

Visitor模式最大的缺點(diǎn)在于擴(kuò)展類層次結(jié)構(gòu)(增添新的Element子類),會(huì)導(dǎo)致Visitor類的改變。因此Visitor模式適用于“Element類層次結(jié)構(gòu)穩(wěn)定,而其中的操作卻進(jìn)場(chǎng)面臨頻繁改動(dòng)”。

“領(lǐng)域規(guī)則”模式

在特定領(lǐng)域內(nèi),某些變化雖然頻繁,但可以抽象為某種規(guī)則。這時(shí)候,結(jié)合特定領(lǐng)域,將問題抽象為語法規(guī)則,從而給出該領(lǐng)域下的一般性解決方案。

典型模式

Interpreter

23.Interpreter解析器

模式定義

給定一個(gè)語言,定義它的文法的一種表示,并定義一種解釋器,這個(gè)解釋器使用該表示來解釋語言中的句子。

——《設(shè)計(jì)模式》GoF

動(dòng)機(jī)(Motivation)

在軟件構(gòu)建過程中,如果某一特定領(lǐng)域的問題比較復(fù)雜,類似的結(jié)構(gòu)不斷的重復(fù)出現(xiàn),如果使用普通的變成方式來實(shí)現(xiàn)將面臨非常頻繁的變化。

在這種情況下,將特定領(lǐng)域的問題表達(dá)為某種語法規(guī)則下的句子,然后構(gòu)建一個(gè)解析器來解釋這樣的句子,從而達(dá)到解決問題的目的。

Interpret模式代碼:

[cpp]view plaincopyprint?

#include?

#include?

#include?

usingnamespacestd;

classExpression?{

public:

virtualintinterpreter(map?var)=0;

virtual~Expression(){}

};

//變量表達(dá)式

classVarExpression:publicExpression?{

charkey;

public:

VarExpression(constchar&?key)

{

this->key?=?key;

}

intinterpreter(map?var)?override?{

returnvar[key];

}

};

//符號(hào)表達(dá)式

classSymbolExpression?:publicExpression?{

//?運(yùn)算符左右兩個(gè)參數(shù)

protected:

Expression*?left;

Expression*?right;

public:

SymbolExpression(?Expression*?left,??Expression*?right):

left(left),right(right){

}

};

//加法運(yùn)算

classAddExpression?:publicSymbolExpression?{

public:

AddExpression(Expression*?left,?Expression*?right):

SymbolExpression(left,right){

}

intinterpreter(map?var)?override?{

returnleft->interpreter(var)?+?right->interpreter(var);

}

};

//減法運(yùn)算

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'+':

//?加法運(yùn)算

left?=?expStack.top();

right?=newVarExpression(expStr[++i]);

expStack.push(newAddExpression(left,?right));

break;

case'-':

//?減法運(yùn)算

left?=?expStack.top();

right?=newVarExpression(expStr[++i]);

expStack.push(newSubExpression(left,?right));

break;

default:

//?變量表達(dá)式

expStack.push(newVarExpression(expStr[i]));

}

}

Expression*?expression?=?expStack.top();

returnexpression;

}

voidrelease(Expression*?expression){

//釋放表達(dá)式樹的節(jié)點(diǎn)內(nèi)存...

}

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(){}

};

//變量表達(dá)式

class VarExpression: public Expression {

char key;

public:

VarExpression(const char& key)

{

this->key = key;

}

int interpreter(map var) override {

return var[key];

}

};

//符號(hào)表達(dá)式

class SymbolExpression : public Expression {

// 運(yùn)算符左右兩個(gè)參數(shù)

protected:

Expression* left;

Expression* right;

public:

SymbolExpression( Expression* left,? Expression* right):

left(left),right(right){

}

};

//加法運(yùn)算

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);

}

};

//減法運(yùn)算

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 '+':

// 加法運(yùn)算

left = expStack.top();

right = new VarExpression(expStr[++i]);

expStack.push(new AddExpression(left, right));

break;

case '-':

// 減法運(yùn)算

left = expStack.top();

right = new VarExpression(expStr[++i]);

expStack.push(new SubExpression(left, right));

break;

default:

// 變量表達(dá)式

expStack.push(new VarExpression(expStr[i]));

}

}

Expression* expression = expStack.top();

return expression;

}

void release(Expression* expression){

//釋放表達(dá)式樹的節(jié)點(diǎn)內(nèi)存...

}

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;

}

結(jié)構(gòu)

要點(diǎn)總結(jié)

Interpreter模式的應(yīng)用場(chǎng)合是Interpreter模式應(yīng)用中的難點(diǎn),只有滿足“業(yè)務(wù)規(guī)則頻繁變化,且類似的結(jié)構(gòu)不斷重復(fù)出現(xiàn),并且容易抽象為語法規(guī)則的問題”才適合使用Interpreter模式。

使用Interpreter模式來表示文法規(guī)則,從而可以使用面向?qū)ο蠹记蓙矸奖愕亍皵U(kuò)展”文法。

Interpreter模式比較適合簡(jiǎn)單的文法表示,對(duì)于復(fù)雜的文法表示,Interpreter模式會(huì)產(chǎn)生比較大的類層次結(jié)構(gòu),需要求助于語法分析生成器這樣的標(biāo)準(zhǔn)工具。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 23種設(shè)計(jì)模式 “對(duì)象性能”模式 面向?qū)ο蠛芎玫慕鉀Q了“抽象”的問題,但是必不可免地要付出一定的代價(jià)。對(duì)于通常情況...
    孫浩_9bfd閱讀 598評(píng)論 0 0
  • 23種設(shè)計(jì)模式 “對(duì)象性能”模式 面向?qū)ο蠛芎玫慕鉀Q了“抽象”的問題,但是必不可免地要付出一定的代價(jià)。對(duì)于通常情況...
    博覽網(wǎng)小學(xué)員閱讀 348評(píng)論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評(píng)論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,623評(píng)論 18 399
  • 面向?qū)ο蟮牧笤瓌t 單一職責(zé)原則 所謂職責(zé)是指類變化的原因。如果一個(gè)類有多于一個(gè)的動(dòng)機(jī)被改變,那么這個(gè)類就具有多于...
    JxMY閱讀 1,017評(píng)論 1 3

友情鏈接更多精彩內(nèi)容