從2005年初聽(tīng)說(shuō)設計模式,到現在雖然已經(jīng)8年多了,但GoF的23種模式依然盛行,當然GoF提出這些模式的年代更加久遠(1995年)。
在工作的過(guò)程中,陸陸續續接觸了GoF的大部分模式,我記得在2008年的時(shí)候就想總結一 下設計模式(最近想做的兩件事情),最后因為各種原因也沒(méi)有完成。最近這段時(shí)間正好是職業(yè)空檔期,沒(méi)什么事兒做,就把之前看過(guò)的設計模式翻出來(lái)整理了一 下,于是就有了上面幾篇文章。
整理設計模式的過(guò)程,也是一個(gè)深刻理解面向對象設計的過(guò)程。通過(guò)對各個(gè)模式的回顧,讓我更能夠明白前輩們關(guān)于面向對象設計提出的各種“最佳實(shí)踐”,特別是S.O.L.I.D,我覺(jué)得在這里再說(shuō)一次,也不算矯情。
S:?jiǎn)我宦氊熢瓌t(Single Responsibility Principle, SRP),一個(gè)類(lèi)只能有一個(gè)原因使其發(fā)生改變,即一個(gè)類(lèi)只承擔一個(gè)職責。
O:開(kāi)放-封閉原則(Open-Close Principle, OCP),這里指我們的設計應該針對擴展開(kāi)放,針對修改關(guān)閉,即盡量以擴展的方式來(lái)維護系統。
L:里氏替換原則(Liskov Subsititution Principle, LSP),它表示我們可以在代碼中使用任意子類(lèi)來(lái)替代父類(lèi)并且程序不受影響,這樣可以保證我們使用“繼承”并沒(méi)有破壞父類(lèi)。
I:接口隔離原則(Interface Segregation Principle, ISP),客戶(hù)端不應該依賴(lài)于它不需要的接口,兩個(gè)類(lèi)之間的依賴(lài)應該建立在最小接口的基礎上。這條原則的目的是為了讓那些使用相同接口的類(lèi)只需要實(shí)現特定必要的一組方法,而不是大量沒(méi)用的方法。
D:依賴(lài)倒置原則(Dependence Inversion Principle, DIP),高層模塊不應該依賴(lài)于低層模塊,兩者應該都依賴(lài)于抽象;抽象不依賴(lài)于細節,而細節應該依賴(lài)于抽象。這里主要是提倡“面向接口”編程,而非“面向實(shí)現”編程。
設計模式,從本質(zhì)上講,是針對過(guò)去某種經(jīng)驗的總結。每種設計模式,都是為了在特定條件下去解決特定問(wèn)題,離開(kāi)這些前提去討論設計模式,是沒(méi)有意義的。
下面,我們快速回顧GoF的23種模式。
上面是對GoF23中模式的快速回顧,其中的理解未必很深刻很到位。對設計模式的學(xué)習是沒(méi)有止境的,而且它也只是面向對象分析與設計的冰山一偶
GoF的設計模式一共23個(gè),可以分為3大類(lèi):創(chuàng )建型、結構型和行為型,這篇文章主要討論創(chuàng )建型。
創(chuàng )建型的設計模式包括:簡(jiǎn)單工廠(chǎng)(Simple Factory)、工廠(chǎng)方法(Factory Method)、抽象工廠(chǎng)(Abstract Factory)、單例(Singleton)、構造者(Builder)和原型(Prototype),我們分別來(lái)討論。
我們首先來(lái)看工廠(chǎng)系列的3個(gè)設計模式,它們都主要是針對軟件設計中的“開(kāi)放-封閉”原則, 即程序應該對擴展開(kāi)放,對修改封閉。特別是當我們的程序采用XML+反射的方式來(lái)創(chuàng )建對象時(shí),工廠(chǎng)模式的威力就完全展現出來(lái)了,這時(shí)我們可以通過(guò)維護配置 文件的方式,來(lái)控制程序的邏輯。
1)簡(jiǎn)單工廠(chǎng),當我們的程序在實(shí)例化對象時(shí),如果輸入條件不一樣,產(chǎn)生的對象也不一樣,那么我們可以考慮使用簡(jiǎn)單工廠(chǎng)對不同的實(shí)例進(jìn)行統一封裝, UML結構如下:
優(yōu)點(diǎn):封裝了具體對象的實(shí)例化過(guò)程,Client端和具體對象解耦,同時(shí)ProductManager可以作成靜態(tài)類(lèi)或者Singleton對象,然后可以使用HashMap緩存具體對象(前提是對象沒(méi)有時(shí)間依賴(lài)性),降低創(chuàng )建對象的次數。
缺點(diǎn):當增添一種新類(lèi)型的對象時(shí),需要修改Productmanager的代碼(如果不采用XML)
2)工廠(chǎng)方法,它是針對簡(jiǎn)單工廠(chǎng)的改進(jìn)版,添加了對ProductManager的抽象,UML結構如下:
優(yōu)點(diǎn):結構更加靈活,對于某種類(lèi)型的對象來(lái)說(shuō),會(huì )有一個(gè)特定的對象工廠(chǎng)指向它,這樣當我們需要添加一種新類(lèi)型的產(chǎn)品時(shí),只需要添加兩個(gè)類(lèi),一個(gè)是具體產(chǎn)品類(lèi),一個(gè)是新產(chǎn)品的工廠(chǎng)類(lèi)。這樣更加靈活。
缺點(diǎn):結構開(kāi)始變得復雜,而且最終還是需要Client端來(lái)確定究竟使用哪一個(gè)Factory(當然這個(gè)信息可以保存在上下文或者配置文件中)。
3)抽象工廠(chǎng),這個(gè)是最復雜的工廠(chǎng)模式,它用來(lái)生成一個(gè)產(chǎn)品線(xiàn)上的所有產(chǎn)品,我們假設一個(gè)產(chǎn)品線(xiàn)上包括多個(gè)產(chǎn)品,不同的產(chǎn)品線(xiàn)上的產(chǎn)品個(gè)數是一樣的,這樣我們需要一個(gè)針對產(chǎn)品線(xiàn)的抽象,并且很顯然不同產(chǎn)品線(xiàn)上的產(chǎn)品是不可能混到一起的。對應的UML結構圖如下:
上圖表明,一個(gè)產(chǎn)品線(xiàn)上的產(chǎn)品由IProduct1和IProduct2組成,客戶(hù)端在獲取產(chǎn)品時(shí),這兩個(gè)產(chǎn)品應該是同時(shí)返回的,因此對于IProductManager來(lái)說(shuō),它需要同時(shí)生成這兩個(gè)對象。
優(yōu)點(diǎn):對創(chuàng )建產(chǎn)品家族的行為高度抽象,添加一個(gè)產(chǎn)品線(xiàn)的邏輯比較清晰。
缺點(diǎn):當我們對產(chǎn)品線(xiàn)上的產(chǎn)品進(jìn)行增加和刪除時(shí),對應的操作比較麻煩,所有的產(chǎn)品工廠(chǎng)都需要進(jìn)行修改。
4)單例,這是比較好理解的一個(gè)模式,從字面上說(shuō),就是程序在運行的過(guò)程中,希望在任意時(shí)刻,都只保留某個(gè)對象的唯一實(shí)例。對應的UML結構圖如下:
單例的實(shí)現方式一般包括幾步:1)私有的指向自身的字段;2)私有構造函數;3)公開(kāi)對私有字段進(jìn)行實(shí)例化的方法。也有幾種針對具體語(yǔ)言進(jìn)行的改善,例如針對多線(xiàn)程采用double lock機制,采用常量方式定義私有字段、使用內嵌類(lèi)來(lái)實(shí)例化字段等。
我們也可以對單例進(jìn)行一些適當的擴展,例如我們將對象的個(gè)數由1個(gè)變?yōu)镹個(gè),這就成了對象池。
通常工廠(chǎng)模式中會(huì )使用到單例模式,特別是對于簡(jiǎn)單工廠(chǎng)來(lái)說(shuō)。
5)構造者,對于一些復雜對象來(lái)說(shuō),它可以分成多個(gè)不同的部分,在實(shí)例化時(shí),不同部分之間實(shí)例化的順序,有時(shí)會(huì )有嚴格的限制,這時(shí)我們就可以使用構造者模式了。對應的UML結構圖如下:
我們定義了IBuilder接口來(lái)實(shí)例化對應的不同部分,同時(shí)有一個(gè)方法來(lái)返回對象的實(shí)例。而Constructor類(lèi)的Construct方 法會(huì )按照業(yè)務(wù)邏輯依次調用實(shí)例化部分對象的方法,即BuildPartA、BuildPartB,這里的調用順序,完全由業(yè)務(wù)邏輯來(lái)控制,最后可以調用 GetProduct方法取得完整的對象實(shí)例。
我們有時(shí)也會(huì )對上圖進(jìn)行修改,例如將GetProduct放到Constructor中,或者將Construct方法放入到GetProduct(取消Constructor)中。即使有這些變形,但是基本的思想是不變的。
6)原型,我們在程序運行過(guò)程中,當需要有新的實(shí)例對象時(shí),有時(shí)并不希望是從頭創(chuàng )建一個(gè)對象,而是希望新的實(shí)例的狀態(tài)和某個(gè)已存在的實(shí)例保持一致,這就是原型模式發(fā)揮作用的地方。對應的UML結構圖如下:
在.NET中,已經(jīng)定義了IClonable接口來(lái)實(shí)現原型模式。需要注意在實(shí)現時(shí),會(huì )有深拷貝和淺拷貝的區別,深拷貝會(huì )同時(shí)拷貝堆棧和堆上的內容,而淺拷貝只會(huì )拷貝堆棧上的內容。
在 這部分里,我們關(guān)注GoF里面的結構型模式,它主要是用于描述如何將類(lèi)組合在一起去構成更大的結構。結構型模式包括適配器(Adapter)、裝飾 (Decorator)、橋接器(Bridge)、享元(FlyWeight)、門(mén)面(Facade)、合成(Composite)以及代理 (Proxy)模式。
下面我們對上面提到的模式分別進(jìn)行描述。
1)適配器(Adapter)。當我們已經(jīng)開(kāi)發(fā)出一個(gè)模塊,有一套清晰的接口,并且模塊正在被某個(gè)功能使用(意味著(zhù)模塊接口改變的可能性不高),這是如果有另外一個(gè)功能也需要使用這個(gè)模塊的功能,但是對應的是一套完全不同的接口,這時(shí)適配器就可以發(fā)揮作用了。
適配器模式分為兩種,一種是對象適配器,一種是類(lèi)適配器,對象適配器的UML圖如下:
這里Adaptee1和Adaptee2指兩套不同的子系統,它們作為Adapter的屬性存在,可以使用IoC的方式指定。
類(lèi)適配器的UML圖如下:
同樣是兩個(gè)不同的子系統,但是這里我們創(chuàng )建了2個(gè)Adapter類(lèi)來(lái)分別指向兩個(gè)子系統。在這里我們可以在Client和ITarget之間,設置一個(gè)Adapter工廠(chǎng),來(lái)根據業(yè)務(wù)需求創(chuàng )建不同的Adpater實(shí)例。
2)裝飾(Decorator),假如我們已經(jīng)開(kāi)發(fā)了一套功能,然后根據需求,需要增加一些子功能,而且這些子功能是比較分散比較時(shí)可以增刪的,這時(shí)如果直接修改接口,那么會(huì )造成接口功能復雜并且不穩定,針對這種情況,我們可以使用裝飾模式。對應的UML圖如下:

上圖中,ConcreteComponent已經(jīng)實(shí)現了Component的基本功能,對于一些附加的功能,如果放在 ConcreteComponent中不合適的話(huà),我們可以像ConcreteDecoratorA一樣,創(chuàng )建一個(gè)基于Decorator的類(lèi),通過(guò) SetComponent方法將核心功能和輔助功能串在一起。
有時(shí),為了簡(jiǎn)單,我們也可以把ConcreteDecorator直接掛在Concretecomponent下面。
3)橋接器(Bridge), 面向對象提倡的幾個(gè)最佳實(shí)踐包括:1)封裝變化;2)面向接口編程;3)組合優(yōu)于繼承;4)類(lèi)的職責盡量單一。橋接器完美的體現了這些,通過(guò)創(chuàng )建型模式, 我們可以很好地達到面向接口編程的目標,也就是說(shuō)我們在程序中各變量的聲明類(lèi)型是接口類(lèi)型或者抽象類(lèi),而具體的實(shí)現類(lèi)型則由不同的設計模式使用不同方式指 定。這在接口或者抽象類(lèi)基本穩定的情況下,是很好地,但當接口需要發(fā)生變化時(shí),我們如何去處理?可以看看橋接器的UML圖:

通過(guò)這個(gè)圖,我們可以看出,Implementor接口的變化,對于Client來(lái)說(shuō),基本是沒(méi)有影響的。Abstraction會(huì )持有Implementor的一個(gè)實(shí)例。
4)享元(FlyWeight), 當我們系統中需要使用大量的小對象,但我們又不希望將所有的小對象都創(chuàng )建出來(lái)時(shí),可以考慮使用享元模式,它會(huì )抽取小對象中的公共部分,將其封裝為基類(lèi),然 后針對不同條件創(chuàng )建小對象,同時(shí)在對象池中維護這些小對象,客戶(hù)在需要使用小對象時(shí),首先在對象池中查找,如果存在,直接返回。對于小對象中“個(gè)性”的部 分,由調用小對象的客戶(hù)端進(jìn)行維護。對應的UML圖如下:
除了上述的簡(jiǎn)單享元,還存在一種復合享元,對應的UML圖如下:

圖中,CompositeConcreteComponent是不共享的,但是它里面包含很多簡(jiǎn)單的享元,這些享元是共享的,我們可以把它想象成一個(gè)特殊的“享元工廠(chǎng)”。
通常提到享元,最常見(jiàn)的例子就是文本編輯器中的26個(gè)字母,在.NET中,字符串常量也使用了享元模式。
在享元模式中,我們通常會(huì )將FlyWeightFactory設計為單例模式,否則享元就沒(méi)有意義了。
5)門(mén)面(Facade),如果我們的程序需要深入調用某個(gè)模塊的內部,但我們又不想和模塊過(guò)緊耦合,這時(shí)可以考慮使用門(mén)面模式,來(lái)對外部封裝內部子系統的實(shí)現。簡(jiǎn)單的門(mén)面可能和代理在某種程度上很相似。
門(mén)面模式?jīng)]有固定的UML圖,它是根據客戶(hù)端的實(shí)際需求以及子系統內部的接口來(lái)確定的。
6)合成(Composite),當我們的對象結構中存在“父子”關(guān)系時(shí),可以考慮使用合成模式。它分為兩種,一種是安全型的合成模式,UML圖如下:

這種類(lèi)型的合成模式,對于Component的增、刪、改,都在Composite中維護,Leaf根本不知道這些操作。另一種是透明型的合成模式,UML圖如下:

這種類(lèi)型的合成模式,自上而下所有的Component都會(huì )有增、刪、改的操作,只不過(guò)對于Leaf來(lái)說(shuō),這些操作時(shí)沒(méi)有意義的。
7)代理(Proxy),在編寫(xiě)程序時(shí),有時(shí)我們希望使用某個(gè)對象或者模塊的功能,但是因為種種原因,我們不能直接訪(fǎng)問(wèn),這時(shí)就可以考慮使用代理,對應的UML圖如下:

需要注意的是,在這里RealSubject只有一個(gè),如果有多個(gè),那么就是Adapter了。另外,代理也可以加入自己的一些邏輯處理,例如PreExecute和PostExecute。如果這里有多個(gè)Proxy,那么就是Decorator了。
上面就是對結構型設計模式的快速瀏覽,其中有很多UML圖看上去很相似,但深入去思考,每個(gè)模式的出發(fā)點(diǎn)、所要解決的問(wèn)題是不一樣的。
在這部分里,我們關(guān)注GoF設計模式中的行為型模式,它是用來(lái)在不同對象之間劃分職責和算法的抽象,行為模式不僅涉及到類(lèi)和對象,還涉及到類(lèi)與對象之間如何進(jìn)行關(guān)聯(lián)。
行為型模式包括:職責鏈(Chain of Responsibility)、命令(Command)、解釋器(Interperter)、迭代(Iterator)、中介者(Mediator)、備忘錄(Memento)、觀(guān)察者(Observer)、狀態(tài)(State)、策略(Strategy)、模板(Template)和訪(fǎng)問(wèn)者(Visitor)。我們主要討論其中的一部分模式,后續會(huì )有其他補充。
1) 職責鏈(Chain of Responsibility),如果完成一項業(yè)務(wù),需要很多步相關(guān)操作,但是如果將這些操作完全封裝到一個(gè)類(lèi)或者方法里面,又違背了單一職責的原則。這時(shí)我們可以考慮使用職責鏈模式,對應的UML圖如下:
我們可以創(chuàng )建很多個(gè)Handler的實(shí)現類(lèi),并通過(guò)設置Successor來(lái)將這些Handler“串”在一起。那么如何觸發(fā)所有的Handler呢?這里和Decorator有點(diǎn)兒類(lèi)似,我們可以通過(guò)調用 Successor.HandlerRequest來(lái)實(shí)現。這樣用戶(hù)只需要關(guān)心最開(kāi)始的Handler,而不必關(guān)心后面都還有哪些其他的Handler。
2)命令(Command),命令模式將發(fā)出命令和執行命令很好的區分開(kāi)來(lái),當我們執行某項業(yè)務(wù)時(shí),客戶(hù)端只需要構造一個(gè)請求,而不必關(guān)心業(yè)務(wù)實(shí)現的具體細節,即構造請求和業(yè)務(wù)實(shí)現是獨立的。對應的UML圖如下:

從圖中,我們可以看到,當Client端需要執行某項業(yè)務(wù)時(shí),它需要構造一個(gè)Invoker對象,它負責發(fā)出請求,會(huì )生成一個(gè)Command對象。同時(shí)我們看到有一個(gè)Receiver對象,它是用來(lái)實(shí)現具體業(yè)務(wù)的,我們在ConcreteCommand中,會(huì )引用這個(gè)對象,來(lái)完成具體業(yè)務(wù)。
3)觀(guān)察者(Observer),當我們的系統中,存在一個(gè)業(yè)務(wù)A,有其他多個(gè)業(yè)務(wù)都需要關(guān)注業(yè)務(wù)A,當它的狀態(tài)發(fā)生變化時(shí),其他業(yè)務(wù)都需要做出相應操作,這時(shí)我們可以使用觀(guān)察者模式。觀(guān)察者模式也稱(chēng)作訂閱模式,它會(huì )定義一個(gè)“主題”(業(yè)務(wù)A),一個(gè)抽象的“訂閱者”以及很多具體的“訂閱者”(其他業(yè)務(wù)),在“主題”中,會(huì )保留所有“訂閱者”的引用,同時(shí)可以對“訂閱者”進(jìn)行添加或者刪除,當“主題”的狀態(tài)發(fā)生變化時(shí),它會(huì )主動(dòng)“通知”所有“訂閱者”,從而“訂閱者”可以做出相應的操作。對應的UML圖如下:

我們可以看到ConcreteSubject中保留了多個(gè)Subscriber的引用(Subscribers),在NotifySubscriber方法中,它會(huì )依次調用每個(gè)Subscriber的Update方法,從而更新“訂閱者”的狀態(tài)。
4)訪(fǎng)問(wèn)者(Visitor),當我們有一個(gè)對象集合,集合中的元素類(lèi)型是不一樣的,但類(lèi)型是相對固定的,例如只有3種不同的類(lèi)型,但是可能有30個(gè)元素。如果我們希望對集合中的所有元素進(jìn)行某種操作,從接口的角度來(lái)看,由于類(lèi)型不一致,我們很難通過(guò)一個(gè)統一的接口來(lái)遍歷集合元素并對其進(jìn)行操作。這時(shí)我們可以考慮使用訪(fǎng)問(wèn)者模式,它將獲取某個(gè)元素和對元素進(jìn)行操作進(jìn)行了分離。對應的UML圖如下:

這里我們假設集合中只包括了2中不同的類(lèi)型,ObjectBuilder就是上面提到的集合,它包含多個(gè)不同的IElement元素,業(yè)務(wù)的核心實(shí)現是在VisitorA和VisitorB中,對于Element1的Accept 方法來(lái)說(shuō),它只是調用visitor.VisitElement1方法。
5)模板(Template),繼承是面向對象的一大核心,而模板方法就是對繼承的完美體現。對于某項業(yè)務(wù)來(lái)說(shuō),我們可以根據通用的流程,設計其方法骨架,針對不清晰或者不明確的地方,以抽象方法的方式來(lái)處理,然后根據不同的子業(yè)務(wù),創(chuàng )建不同的子類(lèi),在子類(lèi)中,實(shí)現那些抽象方法。對應的UML圖如下:

可以看出,對于子類(lèi)來(lái)說(shuō),它是不需要重寫(xiě)Operate方法的,而只需要實(shí)現父類(lèi)的抽象方法。對于客戶(hù)端來(lái)說(shuō),當它實(shí)例化某個(gè)子類(lèi)后,可以直接調用Operate方法來(lái)完成某項業(yè)務(wù)。
6)策略(Strategy),當我們的系統中,針對某項業(yè)務(wù)有多個(gè)算法時(shí),如何對這些算法進(jìn)行管理,我們可以考慮使用策略模式,它主要是針對一組可以提取相同接口的算法進(jìn)行管理。對應的UML圖如下:

這里需要注意的是,Strategy類(lèi)并不知道應該使用哪個(gè)具體的子類(lèi),這應該由Client指定。
7)解釋器(Interperter),如果我們的系統中有些特定的問(wèn)題反復出現,我們想要對這些問(wèn)題進(jìn)行抽象,那應該如何做?試想一下,當我們寫(xiě)完代碼后,是如何進(jìn)行編譯的?無(wú)論對C#還是 Java,它們的編譯器都會(huì )讀取我們所寫(xiě)的每一行代碼,并作出相應的解釋。我們可以部分認為,編譯器中存儲了任何組合的語(yǔ)句,類(lèi)似于C中的 typedef。解釋器做的就是類(lèi)似的事情,它將具有通用性的問(wèn)題進(jìn)行抽取,對其解決方案進(jìn)行綜合處理。對應的UML圖如下:

一般的執行過(guò)程是這樣的,Client讀取Context中的信息,根據某種原則將其劃分成多個(gè)部分,針對每一部分,構造相應的解釋器,并將Context信息傳入解釋器中進(jìn)行處理。這里的問(wèn)題是Client必須要清楚 Context細節和具體解釋器中間的關(guān)聯(lián)。我們可以在Client和Interpreter之間構造一個(gè)“解釋器工廠(chǎng)”,用來(lái)根據Context生成相應的解釋器實(shí)例,同樣,如果解釋器的執行過(guò)程和數據無(wú)關(guān),我們可以為“解釋器工廠(chǎng)”上追加“單例”模式,構造一個(gè)解釋器池。這些都是可以根據需求做的進(jìn)一步的優(yōu)化。
8)迭代(Iterator),前文提到的訪(fǎng)問(wèn)者(Visitor)模式,針對的是存儲在一起的不同類(lèi)型的對象集合,如何進(jìn)行遍歷處理,那么針對存儲在一起的相同類(lèi)型的對象集合,我們應該如何進(jìn)行遍歷呢?迭代模式可以幫我們做到,對應的UML圖如下:

在C#和Java中,我們都已經(jīng)在語(yǔ)言層級上實(shí)現了迭代,例如C#中的foreach,同時(shí).NET來(lái)設計了兩個(gè)接口來(lái)實(shí)現迭代:IEnumerator和IEnumerable。
9)中介者(Mediator),如果我們的系統中有多個(gè)對象,彼此之間都有聯(lián)系,那這是一個(gè)對象之間耦合很高的系統,我們應該如何優(yōu)化呢?我們可以建立一個(gè)知道所有對象的“對象”,在它內部維護其他對象之間的關(guān)聯(lián),這就是中介者模式,對應的UML圖如下:

這里,Mediator是知道所有IPerson的“底細”的,Client 可以直接與Mediator聯(lián)系,而不必關(guān)心具體的是PersonA還是PersonB,同樣,對于PersonA和PersonB來(lái)說(shuō),它們之間也沒(méi)有直接聯(lián)系,當兩者需要通信時(shí),之金額使用Mediator的Send方法。
這個(gè)模式不好的地方在于:1)所有的IPerson類(lèi)型都要有 Mediator引用,這樣才能和其他的Person通信;2)Mediator需要維護所有Person的實(shí)例,這樣它才能做出正確的判斷,將消息發(fā)給對應的Person,但當Person子類(lèi)過(guò)多時(shí),Mediator就變的較難維護,這時(shí),我們可以創(chuàng )建一套關(guān)于產(chǎn)生Person實(shí)例的“工廠(chǎng)”,會(huì )減輕 Mediator的負擔。
10)備忘錄(Memento),當我們的系統中存在這樣一種對象,它的屬性很多,在某些情況下,它的一部分屬性是需要進(jìn)行備份和恢復的,那應該如何做?談到備份和恢復,我們立刻想到可以使用原型模式,但那是針對所有屬性的,備忘錄模式可以很好地解決這里的問(wèn)題,對應的UML圖如下:

在這里,我們希望Originator的State2、State3是可以備份和恢復的,其他屬性是無(wú)關(guān)的。我們可以在希望備份Originator的地方,調用Creatememento方法,在希望恢復Originator部分屬性的地方,調用RestoreMemento方法,同時(shí)MementoManager對Memento進(jìn)行管理。
11)狀態(tài)(State),當我們的系統中的對象,需要根據傳入的不同參數,進(jìn)行不同的處理,而且傳入參數的種類(lèi)特別多,這時(shí)在方法內部會(huì )產(chǎn)生大量的if語(yǔ)句,來(lái)確定方法的執行分支。那么如何消除這些if語(yǔ)句呢?狀態(tài)模式可以幫我們做到,對應的UML圖如下:

這里,Client只與Context關(guān)聯(lián),在Context內部,會(huì )維護不同狀態(tài)之間的跳轉,簡(jiǎn)單來(lái)說(shuō),就是在HandleRequest內部判斷傳入的state值,如果符合業(yè)務(wù)邏輯,那么直接調用state的 HandleRequest方法;如果不符合,那么修改state值,然后調用相應state的HandleRequest方法。
原文鏈接:http://www.cnblogs.com/wing011203/archive/2013/05/02/3055299.html
【編輯推薦】
聯(lián)系客服