本周內(nèi)容
(1)單件模式
(2)享元模式
(3)狀態(tài)模式
(4)備忘錄
(5)組合模式
(6)迭代器
(7)職責(zé)鏈
(8)命令模式
(9)訪問器
(10)解析器
(11)設(shè)計(jì)模式總結(jié)
“對(duì)象性能”模式
- 面向?qū)ο蠛芎玫亟鉀Q了“抽象”的問題,但是必不可免地要付出一定的代價(jià)气笙。對(duì)于通常情況來講设捐,面向?qū)ο蟮某杀敬蠖伎梢院雎圆挥?jì)坟奥。但是某些情況,面向?qū)ο笏鶐淼某杀颈仨氈?jǐn)慎處理侦鹏。
- 典型模式:Singleton湾盒、Flyweight
一 單件模式
動(dòng)機(jī)
- 在軟件系統(tǒng)中,經(jīng)常有這些一類特殊的類痰憎,必須保證它們?cè)谙到y(tǒng)中只存在一個(gè)實(shí)例,才能確保它們的邏輯正確性攀涵、以及良好的效率铣耘。
- 如何繞過常規(guī)的構(gòu)造器,提供一種機(jī)制來保證一個(gè)類只有一個(gè)實(shí)例以故?
- 這應(yīng)該是類設(shè)計(jì)者的責(zé)任蜗细,而不是使用者的責(zé)任。
模式定義:
保證一個(gè)類僅有一個(gè)實(shí)例怒详,并提供一個(gè)該實(shí)例的全局訪問點(diǎn)炉媒。————《設(shè)計(jì)模式》GoF
結(jié)構(gòu):
要點(diǎn)總結(jié)
- Singleton模式中的實(shí)例構(gòu)造器可以設(shè)置為protected以允許子類派生昆烁。
- Singleton模式一般不要支持拷貝構(gòu)造函數(shù)和Clone接口吊骤,因?yàn)檫@有可能導(dǎo)致多個(gè)對(duì)象實(shí)例,與Singleton模式的初衷違背静尼。
- 如何實(shí)現(xiàn)多線程環(huán)境下安全的Singleton白粉?注意對(duì)雙檢查鎖的正確實(shí)現(xiàn)。
二 享元模式
動(dòng)機(jī)
- 在軟件系統(tǒng)采用純粹對(duì)象方案的問題在于大量細(xì)粒度的對(duì)象會(huì)很快充斥在系統(tǒng)中鼠渺,從而帶來很高的運(yùn)行時(shí)代價(jià)——主要指內(nèi)存需求方面的代價(jià)鸭巴。
- 如何在避免大量細(xì)粒度對(duì)象問題的同時(shí),讓外部客戶程序仍然能夠透明地使用面向?qū)ο蟮姆绞絹磉M(jìn)行操作拦盹?
模式定義:
運(yùn)行共享技術(shù)有效地支持大量細(xì)粒度的對(duì)象鹃祖。——《設(shè)計(jì)模式》GoF
結(jié)構(gòu):
要點(diǎn)總結(jié):
- 面向?qū)ο蠛芎玫亟鉀Q了抽象性的問題普舆,但是作為一個(gè)運(yùn)行在機(jī)器中的程序?qū)嶓w恬口,我們需要考慮對(duì)象的代價(jià)問題。Flyweight主要解決面向?qū)ο蟮拇鷥r(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)變化”模式為這一問題提供了一種解決方案太援。
- 典型模式:State闽晦、Memento
三 狀態(tài)模式
動(dòng)機(jī)
- 在軟件構(gòu)建過程中,某些對(duì)象的狀態(tài)如果改變提岔,其行為也會(huì)隨之而發(fā)生變化仙蛉,比如文檔處于只讀狀態(tài),其支持的行為和讀寫狀態(tài)支持的行為就可能完全不同碱蒙。
- 如何在運(yùn)行時(shí)根據(jù)對(duì)象的狀態(tài)來透明地更改對(duì)象的行為荠瘪?而不會(huì)為對(duì)象操作和狀態(tài)轉(zhuǎn)化之間引入緊耦合?
模式定義:
允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為赛惩。從而使對(duì)象看起來似乎修改了其行為哀墓。————《設(shè)計(jì)模式》GoF
結(jié)構(gòu):
要點(diǎn)
- 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ì)象開銷。
四 備忘錄
動(dòng)機(jī)
- 在軟件構(gòu)建過程中念逞,某些對(duì)象的狀態(tài)在轉(zhuǎn)換過程中困食,可能由于某種需要,要求程序能夠回溯到對(duì)象之前處于某個(gè)點(diǎn)時(shí)的狀態(tài)翎承。如果使用一些公有接口來讓其他對(duì)象得到對(duì)象的狀態(tài)硕盹,便會(huì)暴露對(duì)象的細(xì)節(jié)實(shí)現(xiàn)。
- 如何實(shí)現(xiàn)對(duì)象狀態(tài)的良好保存與恢復(fù)叨咖?但同時(shí)又不會(huì)因此而破壞對(duì)象本身的封裝性瘩例。
模式定義:
在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài)甸各,并在該對(duì)象之外保存這個(gè)狀態(tài)垛贤。這樣以后就可以將該對(duì)象恢復(fù)到原先保存的狀態(tài)∪で悖————《設(shè)計(jì)模式》GoF
結(jié)構(gòu):
要點(diǎn)總結(jié):
- 備忘錄(Memento)存儲(chǔ)原發(fā)器(Originator)對(duì)象的內(nèi)部狀態(tài)聘惦,在需要時(shí)恢復(fù)原發(fā)器狀態(tài)。
- Memento模式的核心是信息隱藏儒恋,即Originator需要內(nèi)外接隱藏信息善绎,保持其封裝性黔漂。但同時(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 Resposibility
五 組合模式
動(dòng)機(jī)
- 在軟件在某些情況下撤卢,客戶代碼過多地依賴于對(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ì)象容器到推?
模式定義:
將對(duì)象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。Composite使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性(穩(wěn)定)惕澎±虿猓——《設(shè)計(jì)模式》GoF
結(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ì)象有頻繁的遍歷需求闲询,可使用緩存技巧來改善效率久免。
六 迭代器
動(dòng)機(jī)
- 在軟件構(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ì)象”為“應(yīng)對(duì)變化中的集合對(duì)象”提供了一種優(yōu)雅的方式。
模式定義:
提供一種方法順序訪問一個(gè)聚合對(duì)象中的各個(gè)元素御蒲,而又不暴露(穩(wěn)定)該對(duì)象的內(nèi)部表示衣赶。——《設(shè)計(jì)模式》GoF
結(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)致問題遵馆。
七 職責(zé)鏈
動(dòng)機(jī)
- 在軟件構(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í)決定來處理需求,從而使兩者解耦峻黍。
模式定義:
使多個(gè)對(duì)象都有機(jī)會(huì)會(huì)處理請(qǐng)求复隆,從而避免請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對(duì)象連成一條鏈姆涩,并沿著這條鏈傳遞請(qǐng)求挽拂,直到有一個(gè)對(duì)象處理它為止」嵌觯——《設(shè)計(jì)模式》GoF
結(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ìn)行解耦梅尤,從而支持組件行為的變化柜思,實(shí)現(xiàn)兩者之間的松耦合。
- 典型模式:Command巷燥、Visitor
八 命令模式
動(dòng)機(jī)
- 在軟件構(gòu)建過程中赡盘,“行為請(qǐng)求者”與“行為實(shí)現(xiàn)者”通常呈現(xiàn)一種“松耦合”。但在某些場(chǎng)合——比如需要對(duì)行為進(jìn)行“記錄矾湃、撤銷/重(undo/redo)、事務(wù)”等處理堕澄,這種無法抵御變化的緊耦合是不合適的邀跃。
- 在這種情況下,如何將“行為請(qǐng)求者”與“行為實(shí)現(xiàn)者”解耦蛙紫?將一組行為抽象為對(duì)象拍屑,可以實(shí)現(xiàn)二者之間的松耦合。
模式定義:
將一個(gè)請(qǐng)求(行為)封裝為一個(gè)對(duì)象坑傅,從而使你可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化僵驰;對(duì)請(qǐng)求排隊(duì)或記錄請(qǐng)求日志,以及支持可撤銷的操作唁毒∷廛睿——《設(shè)計(jì)模式》GoF
結(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è)“復(fù)合命令”MacroCommand诺核。
- Command模式與C++中的函數(shù)對(duì)象有些類似。但兩者定義行為接口的規(guī)范有所區(qū)別:Command以面向?qū)ο笾械摹敖涌?實(shí)現(xiàn)”來定義行為接口規(guī)范久信,更嚴(yán)格窖杀,但有性能損失;C++函數(shù)對(duì)象以函數(shù)簽名來定義行為接口規(guī)范裙士,更靈活入客,性能更高。
九 訪問器
動(dòng)機(jī)
- 在軟件構(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)添加新的操作咖驮,從而避免上述問題边器?
模式定義:
表示一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作。使得可以在不改變(穩(wěn)定)各元素的類的前提下定義(擴(kuò)展)作用于這些元素的新操作(變化)托修⊥桑——《設(shè)計(jì)模式》GoF
結(jié)構(gòu):
要點(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類的改變际长。因此Vistor模式適用于“Element”類層次結(jié)構(gòu)穩(wěn)定,而其中的操作卻經(jīng)常面臨頻繁改動(dòng)兴泥。
“領(lǐng)域規(guī)則”模式
- 在特定領(lǐng)域中工育,某些變化雖然頻繁,但可以抽象為某種規(guī)則搓彻。這時(shí)候如绸,結(jié)合特定領(lǐng)域,將問題抽象為語法規(guī)則旭贬,從而給出在該領(lǐng)域下的一般性解決方案怔接。
- 典型模式:Interpreter
十 解析器
動(dòng)機(jī)
- 在軟件構(gòu)建過程中,如果某一特定領(lǐng)域的問題比較復(fù)雜稀轨,類似的結(jié)構(gòu)不斷重復(fù)出現(xiàn)蜕提,如果使用普遍的編程方式來實(shí)現(xiàn)將面臨非常頻繁的變化。
- 在這種情況下靶端,將特定領(lǐng)域的問題表達(dá)為某種語法規(guī)則下的句子谎势,然后構(gòu)建一個(gè)解釋器來解釋這樣的句子,從而達(dá)到解決問題的目的杨名。
模式定義:
給定一個(gè)語言脏榆,定義它的文法的一種表示,并定義一種解釋器台谍,這個(gè)解釋器使用該表示來解釋語言中的句子须喂。——《設(shè)計(jì)模式》GoF
結(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ù)雜的文法表示Interperter模式會(huì)產(chǎn)生比較大的類層次結(jié)構(gòu)卒废,需要求助于語法分析生成器這樣的標(biāo)準(zhǔn)工具沛厨。
十一 設(shè)計(jì)模式總結(jié)
-
什么時(shí)候不用模式
- 代碼可讀性很差時(shí)
- 需求理解還很淺時(shí)
- 變化沒有顯現(xiàn)時(shí)
- 不是系統(tǒng)的關(guān)鍵依賴點(diǎn)
- 項(xiàng)目沒有復(fù)用價(jià)值時(shí)
- 項(xiàng)目將要發(fā)布時(shí)
-
經(jīng)驗(yàn)之談
- 不要為模式而模式
- 關(guān)注抽象類&接口
- 理清變化點(diǎn)和穩(wěn)定點(diǎn)
- 審視依賴關(guān)系
- 要有Framework和Application的區(qū)隔思維
- 良好的設(shè)計(jì)是演化的結(jié)果
-
設(shè)計(jì)模式成長(zhǎng)之路
- “手中無劍,心中無劍”:見模式而不知
- “手中有劍摔认,心中無劍”:可以識(shí)別模式逆皮,作為應(yīng)用開發(fā)人員使用模式
- “手中有劍,心中有劍”:作為框架開發(fā)人員為應(yīng)用設(shè)計(jì)某些模式
- “手中無劍参袱,心中有劍”:忘掉模式电谣,只有原則