GoF的設計模式一共23個,可以分為3大類:創建型、結構型和行為型,這篇文章主要討論創建型。
創建型的設計模式包括:簡單工廠(Simple Factory)、工廠方法(Factory Method)、抽象工廠(Abstract Factory)、單例(Singleton)、構造者(Builder)和原型(Prototype),我們分別來討論。
我們首先來看工廠系列的3個設計模式,它們都主要是針對軟件設計中的“開放-封閉”原則,即程序應該對擴展開放,對修改封閉。特別是當我們的程序采用XML+反射的方式來創建對象時,工廠模式的威力就完全展現出來了,這時我們可以通過維護配置文件的方式,來控制程序的邏輯。
1)簡單工廠,當我們的程序在實例化對象時,如果輸入條件不一樣,產生的對象也不一樣,那么我們可以考慮使用簡單工廠對不同的實例進行統一封裝, UML結構如下:
優點:封裝了具體對象的實例化過程,Client端和具體對象解耦,同時ProductManager可以作成靜態類或者Singleton對象,然后可以使用HashMap緩存具體對象(前提是對象沒有時間依賴性),降低創建對象的次數。
缺點:當增添一種新類型的對象時,需要修改Productmanager的代碼(如果不采用XML)
2)工廠方法,它是針對簡單工廠的改進版,添加了對ProductManager的抽象,UML結構如下:
優點:結構更加靈活,對於某種類型的對象來說,會有一個特定的對象工廠指向它,這樣當我們需要添加一種新類型的產品時,只需要添加兩個類,一個是具體產品類,一個是新產品的工廠類。這樣更加靈活。
缺點:結構開始變得復雜,而且最終還是需要Client端來確定究竟使用哪一個Factory(當然這個信息可以保存在上下文或者配置文件中)。
3)抽象工廠,這個是最復雜的工廠模式,它用來生成一個產品線上的所有產品,我們假設一個產品線上包括多個產品,不同的產品線上的產品個數是一樣的,這樣我們需要一個針對產品線的抽象,並且很顯然不同產品線上的產品是不可能混到一起的。對應的UML結構圖如下:
上圖表明,一個產品線上的產品由IProduct1和IProduct2組成,客戶端在獲取產品時,這兩個產品應該是同時返回的,因此對於IProductManager來說,它需要同時生成這兩個對象。
優點:對創建產品家族的行為高度抽象,添加一個產品線的邏輯比較清晰。
缺點:當我們對產品線上的產品進行增加和刪除時,對應的操作比較麻煩,所有的產品工廠都需要進行修改。
4)單例,這是比較好理解的一個模式,從字面上說,就是程序在運行的過程中,希望在任意時刻,都只保留某個對象的唯一實例。對應的UML結構圖如下:
單例的實現方式一般包括幾步:1)私有的指向自身的字段;2)私有構造函數;3)公開對私有字段進行實例化的方法。也有幾種針對具體語言進行的改善,例如針對多線程采用double lock機制,采用常量方式定義私有字段、使用內嵌類來實例化字段等。
我們也可以對單例進行一些適當的擴展,例如我們將對象的個數由1個變為N個,這就成了對象池。
通常工廠模式中會使用到單例模式,特別是對於簡單工廠來說。
5)構造者,對於一些復雜對象來說,它可以分成多個不同的部分,在實例化時,不同部分之間實例化的順序,有時會有嚴格的限制,這時我們就可以使用構造者模式了。對應的UML結構圖如下:
我們定義了IBuilder接口來實例化對應的不同部分,同時有一個方法來返回對象的實例。而Constructor類的Construct方法會按照業務邏輯依次調用實例化部分對象的方法,即BuildPartA、BuildPartB,這里的調用順序,完全由業務邏輯來控制,最后可以調用GetProduct方法取得完整的對象實例。
我們有時也會對上圖進行修改,例如將GetProduct放到Constructor中,或者將Construct方法放入到GetProduct(取消Constructor)中。即使有這些變形,但是基本的思想是不變的。
6)原型,我們在程序運行過程中,當需要有新的實例對象時,有時並不希望是從頭創建一個對象,而是希望新的實例的狀態和某個已存在的實例保持一致,這就是原型模式發揮作用的地方。對應的UML結構圖如下:
在.NET中,已經定義了IClonable接口來實現原型模式。需要注意在實現時,會有深拷貝和淺拷貝的區別,深拷貝會同時拷貝堆棧和堆上的內容,而淺拷貝只會拷貝堆棧上的內容。