物件工廠設計模式

2022-02-08 23:39:20 字數 4785 閱讀 3225

如果你在你的某個系統中增加了乙個子類,你要建立這個子類的物件,但又不想改變任何原有**,有可能麼?

答案是肯定的,用「物件工廠」設計模式。

物件工廠(object factory)是gof 23種設計模式之外的模式,它既不是抽象工廠(abstract factory),也不是工廠方法(factory method),儘管可能跟它們有些淵源。我第一次看到介紹「物件工廠」的書是《c++設計新思維(modern c++ design)》,但我第一次看到物件工廠的**,卻比看到這書早,但我當時不知道它叫「物件工廠」。

)第8章詳細講解了我們為什麼會需要物件工廠,如何實現並泛化它等內容。本文並不想重複這些內容,而是想通過乙個小例子,將使用物件工廠和不使用物件工廠的情況進行對比,來說明物件工廠會帶來哪些好處。

還是物件導向教科書上那個經典的shape的例子。基於多型,你用c++編寫了一套關於形狀的系統,shape是基類。可能你已經有了line、rectangle等子類。在你的客戶程式裡,通過傳入形狀的型別標識(假設我們用字串來標識型別,當然用整型來標識也可以)來建立具體的(concrete)shape。你的**看起來可能是這樣。

shape 

*createshapebyid(

const

std::

string

&strshapeid)

else

if(strshapeid =="

rectangle")

return

pshape;

}這像是gof《設計模式》裡所說的引數化工廠方法。但這裡違反了物件導向的最重要的規則:

1.它基於型別標記執行了if-else語句(當用整型標識而換為switch語句時同理),這正是物件導向程式竭力消除的東西。

2.它在乙個原始碼檔案中收集所有關於shape子類的相關資訊,這也是我們應該竭力避免的。客戶**檔案都因此必須包含其標頭檔案,造成編譯依存性和維護上的瓶頸。

3.它難以擴充。現在你需要增加ellipse子類,如果沒有使用物件工廠模式,除了增加ellipse本身的**,你至少還要增加以下**:

a)在你的客戶**檔案裡,增加

#include 

"ellipse.h"b)

在createshapebyid中加入以下**:

else

if(strshapeid =="

ellipse")

c)如果你用整型定義型別標識,你還要定義

ellipse

形狀的型別標識。比如:

#define

ellipse 3

現在讓我們來改改,用物件工廠來實現。泛化的物件工廠的**如下:

template

<

class

abstractproduct,

class

identifiertype,

typename productcreator 

=abstractproduct*(

*)()

>

class

factory

;factory(factory

&factory);

factory

&operator=(

const

factory

&factory);

public

:bool

register(

const

identifiertype

&id , productcreator creator)

bool

unregister(

const

identifiertype

&id)

abstractproduct 

*createobject(

const

identifiertype

&id)

return

null;

}static

factory

*instance()

return

pfactory;

}private

:typedef std::map

<

identifiertype, productcreator

>

assocmap;

assocmap associations_;

};簡單說說其工作機理。更詳細的、深入的內容還是請看《c++設計新思維》。

1.此物件工廠泛化了3樣東西:

a)抽象產品(abstract product)。對應本例,就是shape。

b)產品型別識別符號(product type identifier)。對應本例,我們用字串標識,就是std::string。

c)產品生產者(product creator)。對應本例,我們將用預設的(也是最簡單的)原型,也就是無引數、返回值為抽象產品指標的函式。

2.它使用std::map作為產品型別識別符號與產品生產者的對映的儲存結構。

3.register負責向map中註冊乙個產品型別識別符號與產品生產者的對映,unregister則負責登出。

4.createobject是物件工廠的核心,它會根據傳入的產品型別識別符號,找到對應的產品生產者,並呼叫它,建立出具體產品(concrete product)。

5.instance是實現了物件工廠的單件模式。這裡用的是「meyers singleton」的乙個變種。當然這裡不是討論singleton的地方。

有了物件工廠,我們再在shape.h裡定義乙個用來註冊shape具體類的模板類,這裡有真正的形狀的生產者(create函式)。**如下:

template 

<

class

derivedshape

>

class

registershapeclass

registershapeclass(

const

std::

string

&strshapeid)

};再定義乙個將類名轉換為字串的巨集:

#define

classnametostring(x) #x

好了,有了物件工廠,createshapebyid就變成這樣:

shape 

*createshapebyid(

const

std::

string

&strshapeid)

首先,這個函式短多了,而且不會隨著子類的增加而膨脹,但這不是關鍵。這裡面沒有對具體shape型別的引用。當我們需要增加ellipse子類,只需在ellipse類自己的**裡加上下面這句(向工廠註冊自己),而不需要改變任何原有**!

registershapeclass

<

ellipse

>

registerellipse(classnametostring(ellipse));

這看起來有些奇異,但更奇異的是,不僅從原有**中我們看不到任何引用新子類的**,而且連linker都會認為新子類沒有被引用,而將新子類的obj排除在link之外。當然,你也許會認為link的時候使用/opt:noref選項可以避免這個問題。但現實是, visual c++(從vc6到vc9)的/opt:noref選項都有乙個問題(參見

):即使用此選項,仍然不能將新子類的obj檔案link進去。解決的辦法也是在這個**裡看到的,加入類似下面這樣一句,以使linker強行將registershapeclass連線進去

:#pragma

comment(linker, "/include:??0?$registershapeclass@vellipe@@@@qae@abv?$basic_string@du?$char_traits@d@std@@v?$allocator@d@2@@std@@@z")

在我曾開發過的醫學影象處理系統中,需要用到物件工廠的地方,至少有3種:

1.由使用者輸入型別,系統動態生成對應的物件實體。比如:使用者選擇不同的測量工具(measurement,包括:distance,angle等),還有下達各種影象操作的命令(command,比如:zoom,pan,rotate等)。

2.序列化。比如上條所說的measurement,我們能夠儲存下來,並能夠在某個時刻恢復(restore)。儲存時,用measurement的名稱來標識測量工具的型別並序列化,恢復時,根據這個型別標識動態生成物件,並反序列化。

3.同步。我們稱其為會議模式(conference mode),比如乙個客戶端上畫出的measurement,其它客戶端上能同步看到,我們使用xml並進行流(std::stringstream)的輸入和輸出,來傳輸和同步資料和狀態。當資料在其它客戶端流入的時候,與反序列化相似,根據型別標識動態生成物件。

物件工廠使得客戶不再需要(或較少)改變原有系統,但卻很容易擴充套件系統。所以說:物件工廠是對oo開閉原則(ocp,對變更關閉,對擴充套件開放)非常好的闡釋。

這裡再多說一句:由於.net的反射(reflection)機制,使我們不用自己再去建造物件工廠就可以動態地生成物件。用c#寫出來的**應該類似於這樣:

string

strshapeid ="

ellipse";

type type 

=type.gettype(strshapeid);

shape shape 

=(shape)activator.createinstance(type, 

newobject );

物件導向設計模式(工廠模式)

1 單一職責原則 其實就是 高內聚,低耦合 每個類應該只有乙個職責,對外只能提供一種功能,而引起類變化的原因應該只有乙個。2 開閉原則 核心思想就是 對擴充套件開放,對修改關閉 3 裡式替換原則 核心思想 在任何父類出現的地方都可以用子類來替代。也就是說在同乙個繼承體系中的物件應該有共同的行為特徵。...

物件建立型設計模式 抽象工廠模式

本文章根據劉偉 sunny 的設計模式一書記錄的筆記,感謝作者的知識分享。抽象工廠模式 abstract factory pattern 提供乙個建立一系列相關或相互依賴物件的介面,而無須指定它們具體的類。抽象工廠模式又稱為kit模式,它是一種物件建立型模式。在本例項中我們對 進行了大量簡化,實際使...

物件導向設計原則 設計模式之簡單工廠 工廠方法

在實際的開發中,我們要想更深入的了解物件導向思想,就必須熟悉前人總結過的物件導向的思想的設計原則 1.單一職責原則 2.開閉原則 3.黎克特制替換原則 4.依賴注入原則 5.介面分離原則 6.迪公尺特原則 概述 設計模式 design pattern 是一套被反覆使用 多數人知曉的 經過分類編目的 ...