C++設(shè)計(jì)模式第三周筆記

對象性能:

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

典型模式:Singleton、Flyweight

單件模式

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

在如那件系統(tǒng)中夺饲,經(jīng)常有這些一些特殊的類奸汇,必須保證它們在系統(tǒng)中只存在一個(gè)實(shí)例,才能確保它們的邏輯正確性往声、以及良好的效率擂找。

如何繞過常規(guī)的構(gòu)造器,保證一個(gè)類只有一個(gè)實(shí)例浩销。

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

寫法

常規(guī)寫法撼嗓,線程不安全

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;

}

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

Singleton* Singleton::getInstance(){

if(m_instance == nullptr){

Lock lock;

if(m_instance == nullptr){

m_instance = new Singleton;

}

}

return m_instance;

}

在new的過程中且警,理論上是如下的三個(gè)步驟:

分配內(nèi)存粉捻,執(zhí)行構(gòu)造函數(shù),將指針賦值給m_instance

但是如果發(fā)生reorder斑芜,有可能顛倒順序:

分配內(nèi)存肩刃,將指針賦值給m_instance,執(zhí)行構(gòu)造函數(shù)

雙檢查鎖,在lock的前后判斷m_instance是否為空盈包。因?yàn)榭赡芏鄠€(gè)線程都走進(jìn)到m_instance==nullptr分支沸呐,所以之后每個(gè)線程在獲得鎖之后要再次判斷m_instance==nullptr,來確保m_instance不會(huì)被重復(fù)實(shí)例化呢燥。

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

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

Singleton模式一般不要支持拷貝構(gòu)造函數(shù)和Clone接口,這會(huì)導(dǎo)致多個(gè)對象叛氨,違背了Singleton模式的初衷呼渣。

多線程安全的Singleton,應(yīng)該注意雙檢查鎖的正確實(shí)現(xiàn)寞埠。

享元模式Flyweight

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

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

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

模式定義:運(yùn)用共享技術(shù)(解決效率問題的常用手段)有效地支持大量細(xì)粒度的對象。

GetFlyweight(key) 通過這個(gè)方法得到對象饭冬,函數(shù)通過key進(jìn)行判斷使鹅,如果存在,直接返回伍伤,如果不存在并徘,則創(chuàng)建遣钳,并加入pool中扰魂,再返回。

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

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

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

對象數(shù)量太大從而導(dǎo)致對象內(nèi)存開銷加大————什么樣的數(shù)量才算大愿棋?這需要我們根據(jù)具體的應(yīng)用情況去評估科展。

狀態(tài)變化

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

典型模式:State 狀態(tài)模式琅攘、Memento備忘錄模式

狀態(tài)模式

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

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

模式定義:

允許一個(gè)對象在內(nèi)部狀態(tài)改變時(shí)改變它的行為剧辐,從而使對象看起來似乎修改了其行為解恰。

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

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

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

如果Stat對象沒有實(shí)例變量,那么各個(gè)上下文可以共享同一個(gè)State對象参萄,從容節(jié)省對象開銷卫枝。(與策略模式相似)

備忘錄模式Memento

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

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

模式定義:

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

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

備忘錄(Memento)存儲(chǔ)原發(fā)器(Originator)對象的內(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ù)膶ο笮蛄谢С址ǎ虼送捎眯矢吣记俊⒂秩菀渍_實(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 Resposibility

組合模式Composite

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

客戶代碼過多地依賴于對象容器復(fù)雜的內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)命斧,對象容器內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)(而非抽象接口)的變化將引起客戶代碼的頻繁變化田晚,帶來了代碼的維護(hù)性、擴(kuò)展性等弊端国葬。

模式定義:

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

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

Composite模式采用樹形結(jié)構(gòu)來實(shí)現(xiàn)普遍存在的對象容器汇四,從而將一對多的關(guān)系轉(zhuǎn)換成一對一的關(guān)系接奈,使得客戶程序可以一致地處理對象和對象容器,無需關(guān)心處理的是單個(gè)的對象通孽,還是組合的對象容器序宦。

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

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

迭代器Iterator

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

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

模式定義:

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

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

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

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

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

職責(zé)鏈Chain Of Resposibility

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

一個(gè)請求可能被多個(gè)對象處理,但是每個(gè)請求在運(yùn)行時(shí)只能有一個(gè)接受者测垛,如果顯示指定捏膨,將必不可少地帶來請求發(fā)送者與接受者的緊耦合。

模式定義:

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

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

Chain Of Responsibility 模式的應(yīng)用場合在于一個(gè)請求可能有多個(gè)接受者,但是最后真正的接受者只有一個(gè)眉尸,這個(gè)時(shí)候請求發(fā)送者與接受者的耦合有可能出現(xiàn)變化脆弱的癥狀久又,職責(zé)鏈的目的就是講二者解耦,從而更好地應(yīng)對變化效五。

應(yīng)用了Chain Of Responsibility模式后地消,對象的責(zé)任分派將更具靈活定,我們可以在運(yùn)行時(shí)添加/修改請求的處理職責(zé)畏妖。

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

行為變化

在組件的構(gòu)建過程中,組件行為的變化經(jīng)常導(dǎo)致組件本身劇烈的變化迅细,行為變化模式將組件的行為和組件本身進(jìn)行解耦巫橄,從而支持組件的行為變化,實(shí)現(xiàn)兩者之間的松耦合茵典。

典型模式:Command湘换、Visitor

命令模式Command

動(dòng)機(jī):行為請求者與行為實(shí)現(xiàn)者通常呈現(xiàn)一種緊耦合。但在某些場合----比如需要對行為進(jìn)行記錄统阿、撤銷/重做彩倚、事務(wù)等處理,這種無法抵御變化的緊耦合使不合適的扶平。

模式定義:

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

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

Command模式的根本弟弟在于將行為請求者與行為實(shí)現(xiàn)者解耦岸夯,在面向?qū)ο笳Z言中,常見的手段是將行為抽象為對象们妥。

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

Command模式與C++中的函數(shù)對象有些類似破镰,但兩者定義行為接口的規(guī)范有所區(qū)別:Command以面向?qū)ο笾械慕涌?-實(shí)現(xiàn)來定義行為接口說明,更嚴(yán)格压储,但有性能損失鲜漩;C++函數(shù)對象以函數(shù)名來定義行為接口規(guī)范,更靈活集惋,性能更高孕似。

訪問器Visitor

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

模式定義:

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

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

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

所謂雙重分發(fā)即Visitor模式中間包括了兩個(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īng)常面臨頻繁改動(dòng)。

領(lǐng)域規(guī)則

在特定領(lǐng)域中柳琢,某些變化雖然頻繁绍妨,但可以抽象為某種規(guī)則。這時(shí)候染厅,結(jié)合特定領(lǐng)域痘绎,將問題抽象為語法規(guī)則,從而給出在該領(lǐng)域下的一般性解決方案肖粮。

典型模式:Interpreter

解析器Interpreter

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

模式定義:給定一個(gè)語言,定義它的文法的一種表示魂那,并定義一種解釋器蛾号,這個(gè)監(jiān)視器使用該表示來解釋語言中的句子。

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

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市所灸,隨后出現(xiàn)的幾起案子丽惶,更是在濱河造成了極大的恐慌,老刑警劉巖爬立,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钾唬,死亡現(xiàn)場離奇詭異,居然都是意外死亡懦尝,警方通過查閱死者的電腦和手機(jī)知纷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陵霉,“玉大人琅轧,你說我怎么就攤上這事∮荒樱” “怎么了乍桂?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長效床。 經(jīng)常有香客問我睹酌,道長,這世上最難降的妖魔是什么剩檀? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任憋沿,我火速辦了婚禮,結(jié)果婚禮上沪猴,老公的妹妹穿的比我還像新娘辐啄。我一直安慰自己采章,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布壶辜。 她就那樣靜靜地躺著悯舟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪砸民。 梳的紋絲不亂的頭發(fā)上抵怎,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音岭参,去河邊找鬼反惕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛冗荸,可吹牛的內(nèi)容都是我干的承璃。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼蚌本,長吁一口氣:“原來是場噩夢啊……” “哼盔粹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起程癌,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤舷嗡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后嵌莉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體进萄,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年锐峭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了中鼠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡沿癞,死狀恐怖援雇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情椎扬,我是刑警寧澤惫搏,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站蚕涤,受9級(jí)特大地震影響筐赔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜揖铜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一茴丰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦较沪、人聲如沸鳞绕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至萄焦,卻和暖如春控轿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拂封。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工茬射, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冒签。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓在抛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親萧恕。 傳聞我的和親對象是個(gè)殘疾皇子刚梭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

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