博覽網(wǎng):C++設(shè)計模 第三周 筆記

23種設(shè)計模式

“對象性能”模式

面向?qū)ο蠛芎玫慕鉀Q了“抽象”的問題痢士,但是必不可免地要付出一定的代價。對于通常情況來講茂装,面向?qū)ο蟮某杀敬蠖伎梢院雎圆挥嫷□濉5悄承┣闆r,面向?qū)ο笏鶐淼某杀颈仨氈?jǐn)慎處理少态。

典型模式:Singleton ? ? ? ? ? ? ?Flyweight

14.Singleton單件模式

模式定義

保證一個類僅有一個實例城侧,并提供一個該實例的全局訪問點。

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

動機(Motivation)

1)在軟件系統(tǒng)中彼妻,經(jīng)常有這樣一個特殊的類赞庶,必須保證它們在系統(tǒng)中只存在一個示例,才能確保他們的邏輯正確性澳骤、以及良好的效率。

2)如何繞過常規(guī)的構(gòu)造器澜薄,提供一種機制來保證一個類只有一個實例为肮?

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

單件模式代碼:

class Singleton{

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;

}

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



要點總結(jié)

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

2)Singleton模式一般不要支持拷貝構(gòu)造函數(shù)和Clone接口忘分,因為這有可能會導(dǎo)致多個對象實例棋枕,與Singleton模式的初衷相違背。

3)如何實現(xiàn)多線程環(huán)境下安全的Singleton妒峦?注意對雙檢查鎖的正確實現(xiàn)重斑。

15.享元模式FlyWeight

模式定義

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

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

動機(Motivation)

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

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

享元模式代碼:

class Font?{

private:

//unique?object?key

string?key;

//object?state

//....

public:

Font(conststring&?key){

//...

}

};

class FontFactory{

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

//...

}

};

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



要點總結(jié)

1)面向?qū)ο蠛芎玫慕鉀Q了抽相性的問題,但是作為一個運行在機器中的程序?qū)嶓w漾脂,我們需要考慮對象的代價問題假颇。Flyweight主要解決面向的代價問題,一般不觸及面向?qū)ο蟮某橄笮詥栴}骨稿。

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

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

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

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

典型模式:State ? ? ??Memento

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

模式定義

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

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

動機(Motivation)

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

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

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

class NetworkState{

public:

NetworkState*?pNext;

virtual voidOperation1()=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();

}

returnm_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):



要點總結(jié)

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

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

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

17.Memento備忘錄

模式定義

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

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

動機(Motivation)

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

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

Memento模式代碼:

class Memento

{

string?state;

//..

public:

Memento(const string?&?s)?:?state(s)?{}

string?getState() const {returnstate;?}

void setState(const string?&?s)?{?state?=?s;?}

};

class Originator

{

string?state;

//....

public:

Originator()?{}

Memento?createMomento()?{

Memento?m(state);

returnm;

}

void setMomento(constMemento?&?m)?{

state?=?m.getState();

}

};

int main()

{

Originator?orginator;

//捕獲對象狀態(tài)抵乓,存儲到備忘錄

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

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

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

orginator.setMomento(memento);

}

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



要點總結(jié)

1)備忘錄(Memento)存儲原發(fā)器(Originator)對象的內(nèi)部狀態(tài),在需要時恢復(fù)原發(fā)器的狀態(tài)靶衍。

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

3)由于現(xiàn)代語言運行時(如C#蜈出、java等)都具有相當(dāng)?shù)膶ο笮蛄谢С郑虼送捎眯瘦^高涛酗、又較容易正確實現(xiàn)的序列化方案來實現(xiàn)Memento模式铡原。

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

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

典型模式:Composite ? ? ? ??Iterator ? ? ? ? ? ? ?Chain of Responsibility

18.Composite模式

模式定義

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

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

動機(Motivation)

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

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

Composite模式代碼:

#include ?<iostream>

#include <list>

#include <string>

#include <algorithm>

using namespace std;

class Component

{

public:

virtual void process()?=?0;

virtual ~Component(){}

};

//樹節(jié)點

class Composite?:public Component{

string?name;

list?elements;

public:

Composite (conststring?&?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é)點

class Leaf?:publicComponent{

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



要點總結(jié)

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

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

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

19.Iterator迭代器

模式定義

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

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

動機(Motivation)

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

2)使用面向?qū)ο蠹夹g(shù)將這種遍歷機制抽象為“迭代器對象”為“因?qū)ψ兓械募蠈ο蟆碧峁┝艘环N優(yōu)雅的方式。

3)Iterator模式代碼:

template

class Iterator

{

public:

virtual void first()?=?0;

virtua lvoid next()?=?0;

virtual bool isDone()const=?0;

virtual T&?current()?=?0;

};

template

class MyCollection{

public:

Iterator?GetIterator(){

//...

}

};

template

class CollectionIterator?:publicIterator{

MyCollection?mc;

public:

CollectionIterator(constMyCollection?&?c):?mc(c){?}

void first()?override?{

}

void next()?override?{

}

bool isDone()constoverride{

}

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



要點總結(jié):

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

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

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

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

模式定義

使多個對像都有機會處理請求,從而避免請求的發(fā)送者和接收者之間的耦合關(guān)系牌捷。將這些對像連成一條鏈墙牌,并沿著這條鏈傳遞請求,直到有一個對象處理它為止暗甥。

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

動機(Motivation)

1)在軟件構(gòu)建的過程中喜滨,一個請求可能被多個對象處理,但是每個請求在運行時只能有一個接受者撤防,如果顯示指定虽风,將必不可少的帶來請求發(fā)送者與接受者的耦合。

2)如何使請求的發(fā)送者不需要指定具體的接受者?讓請求的接受者自己在運行時決定來處理請求辜膝,從而使兩者解耦无牵。

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

#include ?<iostream>

#include <string>

using names pacestd;

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 {returnreqType;?}

const string &?getDescription() const{returndescription;?}

};

class ChainHandler{

ChainHandler?*nextChain;

void sendReqestToNextHandler(const Reqest?&?req)

{

if(nextChain?!=?nullptr)

nextChain->handle(req);

}

protected:

virtual bool canHandleRequest(const Reqest?&?req)?=?0;

virtua lvoid processRequest(const Reqest?&?req)?=?0;

public:

ChainHandler()?{?nextChain?=?nullptr;?}

void setNextChain(ChainHandler?*next)?{?nextChain?=?next;?}

void handle(constReqest?&?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(cons tReqest?&?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;

}

};

intmain(){

Handler1?h1;

Handler2?h2;

Handler3?h3;

h1.setNextChain(&h2);

h2.setNextChain(&h3);

Reqest?req("process?task?...?",?RequestType::REQ_HANDLER3);

h1.handle(req);

return0;

}

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



要點總結(jié)

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

2)應(yīng)用了Chain of Responsibility模式后充岛,對象的職責(zé)分派將更具靈活性。我們可以在運行時動態(tài)添加/修改請求的處理指責(zé)耕蝉。

3)如果請求傳遞到職責(zé)鏈的末尾仍得不到處理崔梗,應(yīng)該有一個合理的缺省機制。這也是每一個接受者對象的責(zé)任垒在,而不是發(fā)出請求的對象的責(zé)任蒜魄。

“行為變化”模式

在組建的構(gòu)建過程中,組建行為的變化經(jīng)常導(dǎo)致組建本身劇烈的變化场躯√肝“行為變化”模式將組建的行為和組建本身進(jìn)行解耦,從而主持組件的變化踢关,實現(xiàn)兩者之間的松耦合伞鲫。

典型模式:Command ? ? ? ??Visitor

21.Command命令模式

模式定義

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

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

動機(Motivation)

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

2)在這種情況下魂仍,如何將“行為請求者”與“行為實現(xiàn)者”解耦拐辽?將一組行為抽象為對象,可以實現(xiàn)二者之間的松耦合擦酌。

Command模式代碼:

#include <iostream>

#include <vector>

#include <string>

using namespace std;

class Command

{

public:

virtua lvoid 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)



要點總結(jié)

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

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

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

22.Visitor訪問器

模式定義

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

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

動機(Motivation)

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

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

Visitor模式代碼:

#include <iostream>

using namespace std;

class Visitor;

class Element

{

public:

virtual void Func1()?=?0;

virtua lvoid Func2(intdata)=0;

virtual void Func3(intdata)=0;

//...

virtual ~Element(){}

};

class ElementA?:public Element

{

public:

void Func1()?override{

//...

}

void Func2(intdata)?override{

//...

}

};

class ElementB?:public Element

{

public:

void Func1()?override{

//***

}

void Func2(intdata)?override?{

//***

}

};

結(jié)構(gòu)



要點總結(jié)

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

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

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

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

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

典型模式

Interpreter

23.Interpreter解析器

模式定義

給定一個語言,定義它的文法的一種表示搂捧,并定義一種解釋器驮俗,這個解釋器使用該表示來解釋語言中的句子。

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

動機(Motivation)

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

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

Interpret模式代碼:

#include <iostream>

#include <map>

#include <stack>

using namespace std;

class Expression?{

public:

virtual intinterpreter(map?var)=0;

virtual ~Expression(){}

};

//變量表達(dá)式

class VarExpression:public Expression?{

charkey;

public:

VarExpression(constchar&?key)

{

this->key?=?key;

}

intinterpreter(map?var)?override?{

returnvar[key];

}

};

//符號表達(dá)式

classS ymbolExpression?:public Expression?{

//?運算符左右兩個參數(shù)

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

}

intinterpreter(map?var)?override?{

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

}

};

//減法運算

class SubExpression?:public SymbolExpression?{

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:

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

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

}

}

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

return expression;

}

void release(Expression*?expression){

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

}

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

int result=expression->interpreter(var);

cout<

release(expression);

return0;

}

結(jié)構(gòu)



要點總結(jié)

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市仓技,隨后出現(xiàn)的幾起案子鸵贬,更是在濱河造成了極大的恐慌,老刑警劉巖脖捻,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阔逼,死亡現(xiàn)場離奇詭異,居然都是意外死亡地沮,警方通過查閱死者的電腦和手機嗜浮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摩疑,“玉大人危融,你說我怎么就攤上這事±状” “怎么了吉殃?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長楷怒。 經(jīng)常有香客問我蛋勺,道長,這世上最難降的妖魔是什么鸠删? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任抱完,我火速辦了婚禮,結(jié)果婚禮上刃泡,老公的妹妹穿的比我還像新娘巧娱。我一直安慰自己碉怔,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布禁添。 她就那樣靜靜地躺著眨层,像睡著了一般。 火紅的嫁衣襯著肌膚如雪上荡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天馒闷,我揣著相機與錄音酪捡,去河邊找鬼。 笑死纳账,一個胖子當(dāng)著我的面吹牛逛薇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疏虫,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼永罚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了卧秘?” 一聲冷哼從身側(cè)響起呢袱,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎翅敌,沒想到半個月后羞福,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡蚯涮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年治专,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遭顶。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡张峰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出棒旗,到底是詐尸還是另有隱情喘批,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布嗦哆,位于F島的核電站谤祖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏老速。R本人自食惡果不足惜粥喜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望橘券。 院中可真熱鬧额湘,春花似錦卿吐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至毯焕,卻和暖如春衍腥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纳猫。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工婆咸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芜辕。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓尚骄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親侵续。 傳聞我的和親對象是個殘疾皇子倔丈,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 23種設(shè)計模式 “對象性能”模式 面向?qū)ο蠛芎玫慕鉀Q了“抽象”的問題,但是必不可免地要付出一定的代價状蜗。對于通常情況...
    孫浩_9bfd閱讀 525評論 0 0
  • 23種設(shè)計模式 “對象性能”模式 面向?qū)ο蠛芎玫慕鉀Q了“抽象”的問題需五,但是必不可免地要付出一定的代價。對于通常情況...
    孫浩_9bfd閱讀 388評論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理诗舰,服務(wù)發(fā)現(xiàn)警儒,斷路器,智...
    卡卡羅2017閱讀 134,667評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法眶根,類相關(guān)的語法蜀铲,內(nèi)部類的語法,繼承相關(guān)的語法属百,異常的語法记劝,線程的語...
    子非魚_t_閱讀 31,643評論 18 399
  • 面向?qū)ο蟮牧笤瓌t 單一職責(zé)原則 所謂職責(zé)是指類變化的原因。如果一個類有多于一個的動機被改變族扰,那么這個類就具有多于...
    JxMY閱讀 943評論 1 3