1. 程式人生 > >設計模式深入淺出(二)物件建立——Builder,原型,單例

設計模式深入淺出(二)物件建立——Builder,原型,單例

Builder

Builder模式,顧名思義,建造者。

這個模式讓我想到了前一段時間的裝修。現在的裝修工程一般是這樣配置的:有一個專案經理,全權由他負責及排程手下的泥工,電工,木工,油漆工的工作。整個裝修階段,泥工,電工等工種會輪流(反覆)進場或中間有些交叉,比如,房間鋪設電線,需要電工在牆上,地上先開槽,再鋪線,當電工做完電線鋪設後,需要將開槽回填,這時候需要泥工進場。而當泥工鋪好瓷磚等之後,又需要電工重新進場安裝開關,照明等。

而這一切的工種排程及施工,則有專案經理安排,作為業主的我,只需要與專案經理溝通。也就是整個裝修工程,對我而言有了統一的介面(專案經理),我不需要關心具體到底是泥工,電工還是油漆工在施工(當然,如果現實中肯定不能這樣,不然裝修到最後你撞牆也晚了:( )。

恩,到這裡我們進行一下抽象。專案經理,負責工作的協調,我們抽象為Director(導演)。泥工,電工,木工,油漆工,我們將他們分配到一個施工隊,叫Builder。Builder向外暴露一系列介面,用來生產產品的某一步驟(或階段產品),如NiGong(泥工),DianGong(電工)。

這樣,整個裝修工程就抽象為:專案經理(Director)指導排程他的施工隊(Builder)的不同工種(Builder的不同介面),最終生產一個裝修好的房子(Product)。 UML圖如下所示:
這裡寫圖片描述

OK,讓我們再想一下,現在的裝修風格各種各樣:有地中海,美式,北歐,中式等等。那我們假設一個施工隊只能會一種裝修風格,那就需要美式施工隊,中式施工隊等。為了滿足業主的不同風格要求,專案經理手下有多種施工隊,在每次裝修工程中,專案經理會根據業主的選擇,調動相應的施工隊進行施工。UML圖為:
這裡寫圖片描述

這裡我們對builder通過繼承(或協議)進行了擴充套件,使其支援多種不同的裝修風格。而在Director的裝修方法中,為了能夠靈活改變builder,我們添加了一個builder型別引數,用來接收實際執行build操作的builder。

這裡我們看到,儘管目前支援了各種裝修風格,但是對於業主來說,他還是隻需呼叫Director的裝修方法,並將自己需要的builder作為引數傳入。而在專案經理這裡,他的工種排程流程及方式是不用改的(先有電工開槽做電線,再由泥工回填,做瓷磚,再有木工進場做傢俱……),因為不管傳入的是何種builder,在專案經理這,都是按照其父類builder進行處理的。

好了,這就是builder模式。總結一下,該模式需要兩個參與者:Director和Builder。Director負責排程Builder, 而Builder進行具體的build操作。當需要更改最終產品型別時,只需要更換Builder即可。這裡說一下,最終的產品,不需要有共同的父類Product,因為不同Builder最終生產出的產品,可能沒有任何的共性
Builder模式UML:
這裡寫圖片描述

下面是《設計模式》一書中,Builder模式呼叫的時序圖。
《TUTUTUTT》

可以看到,這裡面Client是直接向Builder要結果的(GetResult)。和我們的例子不太一樣。這樣做的好處是,Director的職責更為單一,只是負責協調。另外一個重要的原因是,Builder模式的最終產品,可能不能夠抽象為統一的父類,也就是說Director不能夠提供統一的介面來返回產品,因此,Client直接向Builder索要結果。

Builder模式的主要效果:

  1. 將生產過程與實現分離,讓我們更靈活更換實現,來獲得不同的產品。
  2. 由於有了Director排程,使生產過程更為精細和可控。
  3. 封裝了生產流程與生產實踐,使使用者只關心最終產品,而對產品的生產過程無需關心。

原型

原型模式沒什麼好說的,在OC中,就是我們常用的NSObject copy。

那麼我們為什麼使用原型模式呢?個人感覺是使得兩個物件間徹底的解耦。

copy的物件與被copy的物件間是一種映象關係,他們兩者只在copy的瞬間有聯絡,當copy後,兩者之間的改動互不影響。這裡 涉及到淺拷貝與深拷貝的問題,就不再展開描述。

說一下我們平常程式設計中的例子:

對於有mutable型別的變數來說,當我們把他們宣告為property的時候,應當用copy,這樣當該屬性被賦值時,會自動拷貝一份。如:

@interface NXStudent : NSObject
@property(nonatomic, copy) NSString *name;
@end

這是為什麼呢?

因為當我們對NXStudent物件的name賦值時,可能賦值的是個NSString 型別,但也可能是NSMutableString型別。當是NSMutableString型別時,那麼當在NXStduent物件外部更改string值時,NXStudent物件的name也會一起被改變。這顯然與屬性的定義相違背。

而copy後,NXStudent的name與外部傳入的stirng徹底解耦,無論外部再如何更改,copy 過來的name均不會被影響。

單例

單例的實現方法太多了,在OC中的標準實現,我也不需要寫了吧(dispatch_once)?大家應該倒著寫都能夠寫出來。

這裡只提一個關鍵詞:單例模式的執行緒安全。不瞭解的可以自行百度。

在Cocoa Touch框架中,單例模式也是比比皆是,如UIApplication,就是一個典型的單例。

個人感覺單例模式的應用應該剋制,除非是明確的表明,當前程式中需要這麼一個全域性的唯一物件,否則不應該考慮單例。因為單例模式雖然簡單,但是若要用得好,還是要仔細一些。

這主要是考慮到

  1. 單例物件的釋放時機。單例物件何時釋放?由於他是全域性唯一的,因此單例的生命週期往往是和程式一樣長的,如果單例持有的資源特別重的話,要考慮好資源釋放的問題。
  2. 單例的中間狀態。由於單例是全域性性的,在執行某些單例方法時,他可能會產生些中間狀態,那麼這些中間狀態是否應該在再次執行單例方法時重置?
  3. 單例的執行緒安全。