C++設(shè)計(jì)模式-第三篇
本章內(nèi)容:
1 備忘錄模式
2 狀態(tài)模式
3 組合模式
4 迭代器模式
5 職責(zé)模式
6 命令模式
7 訪問器模式
8 解析器模式
1 備忘錄模式(Memento Method模式)
** “狀態(tài)變化”模式:**
在組件構(gòu)建過程中,某些對象的狀態(tài)經(jīng)常面臨變化,如何對這些變化進(jìn)行有效的管理?同時(shí)又維護(hù)高層模塊的穩(wěn)定?“狀態(tài)變化”模式為這一問題提供了一種解決方案。
典型模式:
1). Memento Method
2). State動機(jī)(Motivation)
在軟件構(gòu)建過程中,某些對象的狀態(tài)在轉(zhuǎn)換過程中,可能由于某種需要,要求程序能夠回溯到對象之前處于某個(gè)點(diǎn)時(shí)的狀態(tài)。如果使用一些公有接口來讓其他對象得到對象的狀態(tài),便會暴露對象的細(xì)節(jié)實(shí)現(xiàn)。
如何實(shí)現(xiàn)對象狀態(tài)的良好保存與恢復(fù)?但同時(shí)又不會因此而破壞對象本身的封裝性。
模式定義
在不破壞封裝性的前提下,捕獲一個(gè)對象的內(nèi)部狀態(tài),并在該對象之外保存這個(gè)狀態(tài)。這樣以后可以將該對象恢復(fù)到原先保存的狀態(tài)。
-
結(jié)構(gòu)(Structure)
備忘錄模式結(jié)構(gòu)圖 要點(diǎn)總結(jié)
備忘錄(
Memento
)存儲原發(fā)器(Originator
)對象的內(nèi)部狀態(tài),在需要時(shí)恢復(fù)原發(fā)器狀態(tài)。Memento
模式的核心是信息隱藏,即Originator
需要向外界隱藏信息,保持其封裝性。但同時(shí)又需要將狀態(tài)保持到外界(Memento
)。由于現(xiàn)代語言運(yùn)行時(shí),(如java、c#等)都具有相當(dāng)?shù)膶ο笮蛄谢С郑虼送捎眯瘦^高,又較容易正確實(shí)現(xiàn)的序列化方案來實(shí)現(xiàn)
Memento
模式。
2 狀態(tài)模式(State模式)
** “狀態(tài)變化”模式:**
在組件構(gòu)建過程中,某些對象的狀態(tài)經(jīng)常面臨變化,如何對這些變化進(jìn)行有效的管理?同時(shí)又維護(hù)高層模塊的穩(wěn)定?“狀態(tài)變化”模式為這一問題提供了一種解決方案。
典型模式:
1). Memento Method
2). State動機(jī)(Motivation)
在軟件構(gòu)建過程中,某些對象的狀態(tài)如果改變,其行為也會隨之而發(fā)生變化,比如文檔處于只讀狀態(tài),其支持的行為和讀寫狀態(tài)支持的行為可能完全不同。
如何在運(yùn)行時(shí)根據(jù)對象的狀態(tài)來透明地更改對象的行為?而不會為對象操作和狀態(tài)轉(zhuǎn)化之間引入緊耦合?
模式定義
允許一個(gè)對象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為。從而使對象看起來似乎修改了其行為。
-
結(jié)構(gòu)(Structure)
狀態(tài)模式結(jié)構(gòu)圖 要點(diǎn)總結(jié)
State
模式將所有與一個(gè)特定狀態(tài)相關(guān)的行為都放入一個(gè)State
的子類對象中,在對象狀態(tài)切換時(shí),切換相應(yīng)的對象;但同時(shí)維持State
的接口,這樣實(shí)現(xiàn)了具體操作與狀態(tài)轉(zhuǎn)換之間的解耦。為不同的狀態(tài)引入不同的對象使得狀態(tài)轉(zhuǎn)換變得更加明確,而且可以保證不會出現(xiàn)狀態(tài)不一致的情況,因?yàn)檗D(zhuǎn)換時(shí)原子性的--即要么徹底轉(zhuǎn)換過來,要么不轉(zhuǎn)換。
如果
State
對象沒有實(shí)例化變量,那么各個(gè)上下文可以共享同一個(gè)State
對象,從而節(jié)省對象開銷。
3 組合模式(Composite模式)
** “數(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)的訪問,是一種行之有效的解決方案。
典型模式:
1). Composite
2). Iterator
3). Chain of Responsibility動機(jī)(Motivation)
在軟件某些情況下,客戶代碼過多地依賴于對象容器復(fù)雜的內(nèi)部實(shí)現(xiàn)結(jié)構(gòu),對象容器內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)(而非抽象接口)的變化將引起客戶代碼的頻繁變化,帶來了代碼的維護(hù)性、擴(kuò)展性等弊端。
如何將“客戶代碼與復(fù)雜的對象容器結(jié)構(gòu)”解耦?讓對象容器自己實(shí)現(xiàn)自身的復(fù)雜結(jié)構(gòu),從而使得客戶代碼就像處理簡單對象一樣來處理復(fù)雜的對象容器?
模式定義
將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。
Composite
使得用戶對單個(gè)對象和組合對的使用具有一致性(穩(wěn)定)。-
結(jié)構(gòu)(Structure)
組合模式結(jié)構(gòu)圖 要點(diǎn)總結(jié)
Composite
模式采用樹形結(jié)構(gòu)實(shí)現(xiàn)普遍存在的對象容器,從而將“一對多”的關(guān)系轉(zhuǎn)化為“一對一”的關(guān)系,使得客戶代碼可以一致地(復(fù)用)處理對象和對象容器,無需關(guān)心處理的是單個(gè)對象,還是組合的對象容器。將“客戶代碼與復(fù)雜的對象容器結(jié)構(gòu)”解耦是
Composite
的核心思想,解耦之后,客戶代碼將與純粹的抽象接口--而非對象容器的內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)--發(fā)生依賴,從而更能“應(yīng)對變化”。Composite
模式在具體的實(shí)現(xiàn)中,可以讓父對象中的子對象反向追溯;如果父對象有頻繁的遍歷要求,可使用緩存技巧來改善效率。
4 迭代器模式(Iterator模式)
** “數(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)的訪問,是一種行之有效的解決方案。
典型模式:
1). Composite
2). Iterator
3). Chain of Responsibility動機(jī)(Motivation)
在軟件構(gòu)建過程中,集合對象內(nèi)部結(jié)構(gòu)常常變化各異。但對于這些集合對象,我們希望在不暴露其內(nèi)部結(jié)構(gòu)的同時(shí),可以讓外部客戶代碼透明地訪問其中包含的元素;同時(shí)這種“透明遍歷”也為“同一種算法在多種集合對象上進(jìn)行操作”提供了可能。
使用面向?qū)ο蠹夹g(shù)將這種遍歷機(jī)制抽象為“迭代器對象”為“應(yīng)對變化中的集合對象”提供了一種優(yōu)雅的方式。
模式定義
提供一種方法順序訪問一個(gè)聚合對象中的各個(gè)元素,而又不暴露(穩(wěn)定)該對象的內(nèi)部表示。
-
結(jié)構(gòu)(Structure)
迭代器模式結(jié)構(gòu)圖 要點(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),會導(dǎo)致問題。
5 職責(zé)鏈模式(Chain of Responsibility模式)
** “數(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)的訪問,是一種行之有效的解決方案。
典型模式:
1). Composite
2). Iterator
3). chain of Responsibility動機(jī)(Motivation)
在軟件構(gòu)建過程中,一個(gè)請求可能被多個(gè)對象處理,但是每個(gè)請求在運(yùn)行時(shí)只能有一個(gè)接受者,如果顯式指定,將必不可少地帶來請求發(fā)送者與接受者的緊耦合。
如何使請求的發(fā)送者不需要指定具體的接受者?讓請求的接受者自己在運(yùn)行時(shí)決定來處理請求,從而使兩者解耦。
模式定義
使多個(gè)對象都有機(jī)會處理請求,從而避免請求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對象連成一條鏈,并沿著這條鏈傳遞請求,直到有一個(gè)對象處理它為止。
-
結(jié)構(gòu)(Structure)
職責(zé)鏈模式結(jié)構(gòu)圖 要點(diǎn)總結(jié)
Chain of Responsibility
模式的應(yīng)用場合在于“一個(gè)請求可能有多個(gè)接受者,但是最后真正的接受者只有一個(gè)”,這時(shí)候請求發(fā)送者與接受者的耦合有可能出現(xiàn)“變化脆弱”的癥狀,職責(zé)鏈的目的就是將二者解耦,從而更好地應(yīng)對變化。應(yīng)用了
Chain of Responsibility
模式后,對象的職責(zé)分派將更具靈活性。我們可以在運(yùn)行時(shí)動態(tài)添加/修改請求的處理職責(zé)。如果請求傳遞到職責(zé)鏈的末尾仍得不到處理,應(yīng)該有一個(gè)合理的缺省機(jī)制。這也是每一個(gè)接受對象的責(zé)任,而不是發(fā)出請求的對象的責(zé)任。
6 命令模式(Command模式)
** “行為變化”模式:**
在組件的構(gòu)建過程中,組件行為的變化經(jīng)常導(dǎo)致組件本身劇烈的變化。“行為變化”模式將組件的行為和組件本身進(jìn)行解耦,從而支持組件行為的變化,實(shí)現(xiàn)兩者之間的松耦合。
典型模式:
1). Command
2). Visitor動機(jī)(Motivation)
在軟件構(gòu)建過程中,“行為請求者”與“行為實(shí)現(xiàn)者”通常呈現(xiàn)一種“緊耦合”。但在某些場合——比如需要對行為進(jìn)行“記錄、撤銷/重(undo/redo)、事務(wù)”等處理,這種無法抵御變化的緊耦合是不合適的。
在這種情況下,如何將“行為請求者”與“行為實(shí)現(xiàn)者” 解耦?將一組行為抽象為對象,可以實(shí)現(xiàn)二者之間的松耦合。
模式定義
將一個(gè)請求(行為)封裝為一個(gè)對象,從而使你可用不同的請求對客戶進(jìn)行參數(shù)化;對請求排隊(duì)或記錄請求日志,以及支持可撤銷的操作。
-
結(jié)構(gòu)(Structure)
命令模式結(jié)構(gòu)圖 要點(diǎn)總結(jié)
Command
模式的根本目的在于將“行為請求者”與“行為實(shí)現(xiàn)者” 解耦,在面向?qū)ο笳Z言中,常見的實(shí)現(xiàn)手段是“將行為抽象為對象”。實(shí)現(xiàn)
Command
接口的具體命令對象ConcreteCommand
有時(shí)候根據(jù)需要可能會保存一些額外的狀態(tài)信息。通過使用Composite
模式,可以將多個(gè)“命令”封裝為一個(gè)“復(fù)合命令”MacroCommand
。Command
模式與C++
中的函數(shù)對象有些類似。但兩者定義行為接口的規(guī)范有所區(qū)別:Command
以面向?qū)ο笾械摹敖涌?實(shí)現(xiàn)”來定義行為接口規(guī)范,更嚴(yán)格,但有性能損失;C++
函數(shù)對象以函數(shù)簽名來定義行為接口規(guī)范,更靈活,性能更高。
7 訪問器模式(Visitor模式)
** “行為變化”模式:**
在組件的構(gòu)建過程中,組件行為的變化經(jīng)常導(dǎo)致組件本身劇烈的變化。“行為變化”模式將組件的行為和組件本身進(jìn)行解耦,從而支持組件行為的變化,實(shí)現(xiàn)兩者之間的松耦合。
典型模式:
1). Command
2). Visitor動機(jī)(Motivation)
在軟件構(gòu)建過程中,由于需求的改變,某些類層次結(jié)構(gòu)中常常需要增加新的行為(方法),如果直接在基類中做這樣的更改,將會給子類帶來很繁重的變更負(fù)擔(dān),甚至破壞原有設(shè)計(jì)。
如何在不更改類層次結(jié)構(gòu)的前提下,在運(yùn)行時(shí)根據(jù)需要透明地為類層次結(jié)構(gòu)上的各個(gè)類動態(tài)添加新的操作,從而避免上述問題?
模式定義
表示一個(gè)作用于某對象結(jié)構(gòu)中的各元素的操作。使得可以在不改變(穩(wěn)定)各元素的類的前提下定義(擴(kuò)展)作用于這些元素的新操作(變化)。
-
結(jié)構(gòu)(Structure)
訪問器模式結(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è)類動態(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
子類),會導(dǎo)致Visitor
類的改變。因此Visitor
模式適用于“Element
類層次結(jié)構(gòu)穩(wěn)定,而其中的操作卻經(jīng)常面臨頻繁改動”。
8 解析器模式(Interpreter模式)
** “領(lǐng)域規(guī)則”模式:**
在特定領(lǐng)域中,某些變化雖然頻繁,但可以抽象為某種規(guī)則。這時(shí)候,結(jié)合特定領(lǐng)域,將問題抽象為語法規(guī)則,從而給出在該領(lǐng)域下的一般性解決方案。
典型模式:
1). Interpreter動機(jī)(Motivation)
在軟件構(gòu)建過程中,如果某一特定領(lǐng)域的問題比較復(fù)雜,類似的結(jié)構(gòu)不斷重復(fù)出現(xiàn),如果使用普通的編程方式來實(shí)現(xiàn)將面臨非常頻繁的變化。
在這種情況下,將特定領(lǐng)域的問題表達(dá)為某種語法規(guī)則下的句子,然后構(gòu)建一個(gè)解釋器來解釋這樣的句子,從而達(dá)到解決問題的目的。
模式定義
給定一個(gè)語言,定義它文法的一種表示,并定義一種解釋器,這個(gè)解釋器使用該表示來解釋語言中的句子。
-
結(jié)構(gòu)(Structure)
解析器模式結(jié)構(gòu)圖 要點(diǎn)總結(jié)
Interpreter
模式的應(yīng)用場合是Interpreter
模式應(yīng)用中的難點(diǎn),只有滿足“業(yè)務(wù)規(guī)則頻繁變化,且類似的結(jié)構(gòu)不斷重復(fù)出現(xiàn),并且容易抽象為語法規(guī)則的問題”才適合使用Interpreter
模式。使用
Interpreter
模式來表示文法規(guī)則,從而可以使用面向?qū)ο蠹记蓙矸奖愕亍皵U(kuò)展”文法。Interpreter
模式比較適合簡單的文法表示,對于復(fù)雜的文法表示,Interpreter
模式會產(chǎn)生比較大的類層次結(jié)構(gòu),需要求助于語法分析生成器這樣的標(biāo)準(zhǔn)工具。