GeekBand C++ Week11 Notes

Week11 Notes

“對象性能模式”

單間模式

面向?qū)ο蠛芎玫亟鉀Q了抽象的問題,但是不可避免要付出一定的代價,在某些情況下,抽象帶來的代價需要謹(jǐn)慎處理,比如虛函數(shù)和繼承

虛函數(shù)帶來的代價還是很多的,某些倍乘效應(yīng)

典型的模式有Singleton和Flyweight

在某些特殊的場景下,面向?qū)ο髱淼某杀居袉栴}

經(jīng)常有一些特殊的類,必須保證在系統(tǒng)中只存在一個實(shí)例,才能確保邏輯的正確,以及具有良好的效率,如何繞過常規(guī)的構(gòu)造器使一個類只有一個實(shí)例呢,這是設(shè)計者的責(zé)任,不是使用者的責(zé)任

怎么實(shí)現(xiàn)呢?

Class Singleton{

Private:

? ? Singleton();

? ? Singleton(constSigleton& other);

Public:

? ? StaticSingleton* getInstance();

? ? StaticSingleton* m_instance;

};

//線程非安全版本

Singleton* Singleton::m_instance = nullptr;

Singleton* Singleton::getInstace(){

? ? If(m_instace== nullptr){

? ? ? ? M_instance = new Singleton();

? ? }

? ? returnm_instance;

}

//線程安全版本。但鎖的代價過高

Singleton* Singleton::getInstance(){

? ? Locklock;

? ? If(m_instance== nullptr){

? ? ? ? M_instance= new Singleton();

? ? }

? ? returnm_instance;

}

這是線程安全版本,鎖的代價在哪里?假設(shè)對象已經(jīng)被創(chuàng)建出來,m_instance已經(jīng)不是null了,這是后兩個線程都是在讀,此時不需要加鎖。

當(dāng)高并發(fā)的時候(外部服務(wù)器)十萬人同時在線,在這種場景下,這個鎖的代價可能也是很高的。

//雙檢查鎖,但由于內(nèi)存讀寫reorder不安全

Singleton* Singleton::getIncetance(){

? ? If(m_instance== nullptr){

? ? ? ? Locklock;

? ? ? ? If(m_instance == nullptr)

? ? ? ? M_instance = new Singleton();

? ? }

? ? returnm_instance;

}

鎖前檢查,正確但代價過高

鎖后檢查,為了保證正確

內(nèi)存讀寫reorder的情況會導(dǎo)致雙檢查鎖失效

在指令層的時候和我們的假設(shè)有可能不一樣

new的這一步先分配內(nèi)存,再調(diào)用構(gòu)造器,一般對分配的那塊內(nèi)存初始化,第三步是做完的這些東西的指針值給m_instance,但這三步是我們假想的順序,在指令層,這三步有可能被reorder,先分配內(nèi)存,然后把內(nèi)存地址給m_instance,最后再調(diào)用構(gòu)造器。這是編譯器優(yōu)化的時候有可能會這樣干,每一種語言都會有這種情況。怎么解決這個問題?

//C++ 11版本以后的跨平臺實(shí)現(xiàn)(volatile)

std::atomicSingleton::m_instance;

std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance(){

? ? Singleton*tmp = m_instance.load(std::memory_order_relaxed);

? ? Std::atomic_thread_fence(std::memory_order_acquire);

? ? If(tmp== nullptr){

? ? ? ? Std::lock_guardlock(m_mutex);

? ? ? ? Tmp= m_instance.load(std::memory_order_relaxed);

? ? ? ? If(tmp== nullptr){

? ? ? ? ? ? Tmp= new Singleton;

? ? ? ? ? ? Std::atomic_thread_fence(std::memory_order_relaxed);

? ? ? ? ? ? M_instance.store(tmp,std::memory_order_relaxed);

? ? ? ? }

? ? }

? ? return tmp;

}

作用是保證一個類僅有一個實(shí)例,實(shí)例構(gòu)造器可以設(shè)置為protected以允許子類派生,一般不要支持拷貝構(gòu)造函數(shù)和clone接口,因?yàn)檫@樣有可能導(dǎo)致多個對象實(shí)例。

享元模式

Flyweight

在軟件系統(tǒng)中采用純粹對象方案的問題在于大量細(xì)粒度的對象會很快充斥在系統(tǒng)中,從而帶來很高的運(yùn)行時代價。使用共享的技術(shù)可以有效地支持大量細(xì)粒度的對象

class Font{

private:

? ? //uniqueobject key

? ? stringkey;

public:

? ? Font(conststring& key){…}

};

class FontFactory{

private:

? ? map fontPool;

public:

? ? Font*GetFont(const string& key){

? ? ? ? Map::iterator item = fontPool.find(key);

? ? ? ? If(item!=footPool.end()){

? ? ? ? ? ? ReturnfontPool[key];

? ? ? ? }

? ? ? ? else{

? ? ? ? ? ? Font* font = new Font(key);

? ? ? ? ? ? fontPool[key] = font;

? ? ? ? ? ? return font;

? ? ? ? } ?

? ? }

void clear(){…}

}

對象池的一種設(shè)計方式,有就返回,沒有就創(chuàng)建一個。實(shí)際中數(shù)據(jù)結(jié)構(gòu)也不同,接口也可能不同。通過一個共享的方案來支持大量細(xì)粒度的對象。出去的對象最好是只讀的,否則出去以后被更改了。Flyweight一般只解決對象的代價問題。對象的數(shù)量太大導(dǎo)致對象的開銷太大。對象上10萬,大概是5M的水平

幾十個對象不要用flyweight,里面使用的數(shù)據(jù)結(jié)構(gòu)也一樣需要開銷

狀態(tài)模式

“狀態(tài)變化模式”某些對象的狀態(tài)經(jīng)常面臨變化,如何對這些變化進(jìn)行有效的管理,同時又維持高層模塊的穩(wěn)定?某些對象的狀態(tài)如果改變,其行為也會發(fā)生變化,比如文檔處于只讀狀態(tài)。

Enum NetworkState{

? ? Network_Open

? ? Network_Close,

? ? NetworkConnect,

};

//類似于狀態(tài)機(jī)

class NetworkProcessor{

? ? NetworkStatestate;

Public:

? ? VoidOperation1(){

? ? ? ? If(state== Network_Open){

? ? ? ? ? ? State= Network_Close;

? ? ? ? }

? ? ? ? else_if …

? ? }

? ? voidoperation2(){…}

}

那我們這樣寫有什么問題?

這和之前的strategy很像,if_else在你的代碼中出現(xiàn)很多,以后如果添加了一種新的狀態(tài)wait,那之前的代碼要怎么更改,需求的變化讓你的代碼不斷地在變化,按照strategy給我們的經(jīng)驗(yàn),先提抽象基類

class NetworkState{

public:

? ? Network*pNext;

? ? Virtualvoid Operation1() = 0;

? ? Virtualvoid Operation2() = 0;

? ? Virtual~NetworkState(){}

};

class OpenState:public NetworkState{

? ? staticNetworkState* m_instance;

public:

? ? staticNetworkState* getInstance(){

? ? ? ? if(m_instance== nulptr){

? ? ? ? ? ? m_instance= new OpenState();

? ? ? ? }

? ? ? ? return m_instance;

? ? }

? ? voidoperation1(){

? ? ? ? //…

? ? ? ? pNext = CloseState::getInstance; ?

? ? }

};

class NetworkProcessor{

? ? NetworkState*pState;

Public:

? ? NetworkProcessor(NetworkState*pState){

? ? ? ? This->pState= pState;

? ? }

? ? voidOperation1(){

? ? ? ? pState->Operation1();

? ? ? ? pState= pState->pNext;

? ? }

};

允許一個對象在其內(nèi)部狀態(tài)改變的時候改變它的行為,從而是對象看起來似乎修改了其行為。和strategy非常像。為不同的狀態(tài)引入不同的對象使得狀態(tài)轉(zhuǎn)換變得更加明確,如果state對象沒有實(shí)例變量,那么各個上下文可以共享一個state對象,從而節(jié)省對象開銷。(Singleton設(shè)計模式)

備忘錄

Memento

屬于“狀態(tài)變化”模式,某些對象在狀態(tài)轉(zhuǎn)換過程中,要求程序能夠回溯到對象之前處于某個點(diǎn)時的狀態(tài),如果使用一些公共接口來讓其他對象得到對象的狀態(tài),會暴露對象細(xì)節(jié)實(shí)現(xiàn)

在不破壞封裝性的前提下,捕獲一個對象的內(nèi)部狀態(tài),并在對象外部保存這個狀態(tài)。

Class Memento{

? ? Stringstate;

Public;

? ? Memento(conststring& s): state(s){}

? ? StringgetState() const{return state;}

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

?}

class Originator{

? ? stringstate;

public:

? ? Originator(){}

? ? MementocreateMomento(){

? ? ? ? Mementom(state);

? ? ? ? Returnm;

? ? }

? ? voidsetMomento(const Memento& m){

? ? ? ? state = m.getState();

? ? }

};

int main(){

? ? Originatororiginator;

? ? //存儲到備忘錄

? ? Mementomem = originator.reateMomento();//捕獲對象狀態(tài)

? ? //改變originator狀態(tài) ??

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

? ? originator.setMomento(memento);?

}

不破壞originator的封裝性,

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

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

由于現(xiàn)代語言運(yùn)行時都具有相當(dāng)?shù)膶ο笮蛄谢С?,因此往往采用效率較高又容易正確實(shí)現(xiàn)的序列化方案來實(shí)現(xiàn)momento模式

實(shí)現(xiàn)的是一個類似于深拷貝的事情,內(nèi)存快照的方式,復(fù)雜對象不好實(shí)現(xiàn),一個對象里面沒有指針,很簡單,但如果指針里面再有指針,指針里面再有指針,這樣具體實(shí)現(xiàn)的時候不太好實(shí)現(xiàn),所以我們一般用序列化,

組合模式

Composite

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

iterator,chain ofresponsibility

組件在內(nèi)部具有特定的數(shù)據(jù)結(jié)構(gòu),如果讓客戶程序依賴這些特定的數(shù)據(jù)結(jié)構(gòu),會破壞組件的復(fù)用。

需要將客戶代碼和復(fù)雜的對象容器結(jié)構(gòu)解耦。

將對象組合成樹性結(jié)構(gòu)以表示部分和張體的層次結(jié)構(gòu),composite使得用戶對單個對象和組合對象的使用具有一致性。

Class component{

Public:

? ? Virtualvoid process() = 0;

? ? Virtual~component(){}

};

class Composite: public component{

? ? stringname;

? ? listelements;

public:

? ? composite(conststring& s): name(s){}

? ? voidadd(component* element){ ?

? ? ? ? elements.push_back(element);

? ? }

? ? voidremove(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)用

? ? }

};

class Leaf:public component{

? ? stringname;

public:

? ? Leaf(strings): name(s) {}

? ? Voidprocess(){

? ? ? ? //processcurrent node

? ? }

};

void Invoke(component& c){

? ? c.process();

}

int main(){

? ? compositeroot(“root”);?

? ? compositetreeNode1(“treeNode1”);

? ? compositetreeNode2(“treeNode2”)

? ? …

? ? Leafleaf1(“l(fā)eft1”);

? ? Leafleaf2(“l(fā)eft2”);

? ? Root.add(&treeNode1);

? ? treeNode1.add(&treeNode2);

? ? treeNode2.add(&leaf1);?

? ? root.add(&treeNode2);?

}

composite模式采用樹形結(jié)構(gòu)來實(shí)現(xiàn)普遍存在的對象容器,將一對多的關(guān)系轉(zhuǎn)化為一對一的關(guān)系,使得客戶代碼可以一致地處理對象和容器,不需要關(guān)心處理的是單個的兌現(xiàn)還是組合的對象容器,將客戶代碼與復(fù)雜的對象容器結(jié)構(gòu)解耦是composite的核心思想,解耦以后客戶代碼將與純粹的抽象接口而不是對象容器的內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)發(fā)生以來,更能應(yīng)對變化。

迭代器模式

iterator

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

提供一種方法順序訪問一個聚合對象中的各個元素,不暴露該對象的內(nèi)部表示。

職責(zé)鏈

chain of responsibility

一個請求可能被多個對象處理,但每個請求在運(yùn)行的時候只能有一個接受者2,如果顯示指定會有緊耦合

讓請求的需求者自動來判斷

命令模式

command

“行為變化”模式

組件行為的變化經(jīng)常導(dǎo)致組件本身劇烈的變化

運(yùn)行時綁定,一串虛函數(shù)的調(diào)用

如何將行為請求者與行為實(shí)現(xiàn)者解耦?

將請求封裝成一個對象,從而可以用不同的請求對客戶進(jìn)行參數(shù)化,對請求排隊(duì)或記錄請求日志。

一旦把行為作為對象后可能獲得的好處有redo, undo, stack入棧出棧,將行為抽象為對象

實(shí)現(xiàn)command接口的具體命令對象ConcreteCommand有時候需要可能會保存一些額外的狀態(tài)信息。使用composite模式封裝為一個復(fù)合指令

使用接口和實(shí)現(xiàn)來定義行為接口規(guī)范。

訪問器

類層次結(jié)構(gòu)中要增加新的行為方法,在基類中更改會給子類帶來負(fù)擔(dān),所以要透明的為類層次結(jié)構(gòu)上動態(tài)地增加。(運(yùn)行時增加)

class element{

? ? virtualaccept(Visitor &visitor)

}

class visitot{

public:

? ? voidvisitElementA(ElementA& element)

}

訪問器的缺點(diǎn):

有幾個子類visit里就有幾個visitconcreteelemntA

所以需要知道有幾個子類

所以visitor模式需要在類的層次結(jié)構(gòu)需要穩(wěn)定

但其中的操作經(jīng)常面臨頻繁改動

解析器

問題比較復(fù)雜,如果使用

a+b-c+d把規(guī)則提取出來,

class Expression{

public:

? ? virtualint interpreter(map var) = 0;

? ? virtual~Expression(){}

};

變量表達(dá)式:

VarExpression:public Expression{

? ? ?Char key

Public:

? ? VarExpression(constchar& key){

? ? This->key= key;

? ? } ?

? ? int interpreter(map var) override{

? ? ? ? return var[key];

? ? }

};

符號表達(dá)式:

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

? ? Intinterpreter(map var) override{

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

? ? }

};

Expression* analyse(string expStr){

? ? StackexpStack;

? ? Expression*left = nullptr;

? ? ?Expression*right = nullptr;

? ? For(int I = 0; I< expStr.size(); i++){

? ? ? ? Switch(expStr[i]){

? ? ? ? ? ? Case’+’:

? ? ? ? ? ? Case’-’:

? ? ? ? ? ? Default:

? ? ? ? }

? ? }

}

給定一個語言,定義它的文法的一種表示,并定義一個解釋器,

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

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

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

總結(jié)

管理變化,提高復(fù)用!

分解和抽象

八大原則

重構(gòu)技巧:

靜態(tài)轉(zhuǎn)為動態(tài),早綁定轉(zhuǎn)為晚綁定,編譯時依賴轉(zhuǎn)為運(yùn)行時依賴,繼承轉(zhuǎn)為組合

直接組合一個對象和繼承在C++內(nèi)存上是一樣的,這樣B就不能變化了,但是如果A中組合B的指針,B就具有靈活性,可以改變,指向B的子類

關(guān)注抽象類和接口,理清變化點(diǎn)和穩(wěn)定點(diǎn),審視依賴關(guān)系,要有framework和application的區(qū)隔思維,良好的設(shè)計是演化的結(jié)果。

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

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

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