物件工廠 2 乙個泛化的工廠類

2021-09-14 00:45:02 字數 3868 閱讀 5435

物件工廠(1)---和萬惡的 switch 說再見中,我們通過使用函式指標索引的方法,為我們的工廠類**中消除了 switch 語句。本篇部落格的目標是將實現乙個泛化的工廠類,實現**復用。

下面讓我們先分析一下在物件工廠(1)---和萬惡的 switch 說再見中的工廠類的幾個主要角色:

abstract 基類(文中的 shape),通過操作這個基類的指標,整個系統實現了執行期多型

concrete 子類(文中的 line 等),每個攜帶真正邏輯**的具體類。

identifier(文中的 int),factory 類需要從 identifier 對映到正確的具體類建立函式。

creator(文中的 createline 等),用於真正產生具體類的各個函式。

如果我們使用模板程式設計,將上面的概念作為模板引數,那麼乙個泛型工廠類便呼之欲出了。可是,且慢,讓我們分析一下這4個角色是否 factory 類都需要了解呢?答案顯然不是,concrete 子類對 factory 完全是透明的,factory 在建立物件的時候,不需要也不應該關心到底有哪些具體子類(這個正是我們上篇部落格所解決的問題)。

現在給出泛型工廠類的基本**:

template

<

class abstractproduct,

typename identifiertype,

typename productcreator = abstractproduct *(*)(),

>

class factory

bool unregister(const identifiertype &id)

abstractproduct *createobject(const identifiertype &id) const

return (i->second)();

}private:

typedef std::mapassocmap;

assocmap associations_;

};

上面的**為 productcreator 提供了乙個預設的型別,乙個函式指標,語法乍看有點怪異,但是如果這樣寫想必有點經驗的 c/c++ 程式設計師都可以輕易認出:
typedef abstractproduct *(*createfn)();
上面的**為不接受任何引數,返回 abstractproduct 指標的函式指標定義了乙個 createfn 別名。

接下來讓我們完善一下這個設計,如**中的紅字指出,如果乙個工廠類無法識別的 type 出現了,我們改如何處理呢?典型的處理方法有:

丟擲異常

返回乙個空指標

提供乙個預設指標

每乙個解決方法在不同的場景下都是乙個合適的處理方式,比如如果你需要盡早發現這種意料之外的情況,那麼丟擲異常是個不錯的選擇,如果你在開發乙個網路伺服器,那麼顯然如果直接丟擲異常可能會倒置伺服器 down 掉,那這時返回乙個空指標,並記錄錯誤日誌可能是乙個更好的選擇。所以我們必須給予客戶端指定如何處理的許可權。

這裡使用的實現模式成為 policy 模式(詳見《moder c++ design》一書),這種模式將程式中一些可以有多種實現方法的地方區分出來,並抽象為乙個 policy,通過模板引數傳入,使用類似組裝的方法,將多個 policy 組裝成乙個實用的 class,最大程度實現**和元件的復用(其實個人感覺模板程式設計更像是面對介面程式設計,只是介面不是明確寫出的,而是隱藏在**中的)。

為了解決上面的問題,我們引入乙個 factoryerrorpolicy,這個 policy 負責提供乙個介面,介面用於處理未知的 type,也就是說其可能是這樣子:

templateclass factoryerrorpolicy ;
注意**中被括起來的 static 關鍵字,這並不是什麼晦澀的語法,而是我想在此說明,因為我們使用的是模板程式設計,它是面向語法的,不是面向標記的,不管你是 static 與否,只要可以正常執行就滿足我們的介面。這個我會在之後再加以解釋,現在我們先徹底完善我們的 factory 類:
template

<

class abstractproduct,

typename identifiertype,

typename productcreator = abstractproduct *(*)(),

templateclass factoryerrorpolicy = defaultfactoryerror

>

class factory

: public factoryerrorpolicy

bool unregister(const identifiertype &id)

abstractproduct *createobject(const identifiertype &id) const

return (i->second)();

}private:

typedef std::mapassocmap;

assocmap associations_;

};

注意新引入的兩行**,這裡我們為模板引數增加了乙個 policy 類,並在**中隱式的要求
factoryerrorpolicy
類可以提供乙個 onunknowntype() 函式介面,其可以接受 int 作為引數(它的簽名可以是接受 double, char, 任何可以通過int隱式轉化的型別)呼叫就可以了,至於是不是 static 我們也不關心,甚至它的宣告可以是這樣:
void *onunknowntype(double d, int i = 0, string="");
這是完全可以的,在模板程式設計中,我們只需要 this->onunknowntype() 這句語句可以通過編譯就可以了,這是模板程式設計和物件導向最大的不同點之一,個人認為其靈活性遠超物件導向程式設計。還要注意的是 policy 模式往往通過定義的模板類 public 繼承 policy 類來實現。

我們甚至還為這個 policy 提供了乙個預設的 class 來減少客戶端的編碼成本:

templateclass defaultfactoryerror 

};

為了佐證我上面的論點,我特意選擇了乙個很奇怪的實現,至此我們完整實現了乙個泛型工廠類,
typedef factoryshapefactory;
替代之前的 shapefactory 實現,並且適當修改 factory 類的呼叫處(因為介面名字有所改變,比如 createshape 改為 createobject),就可以直接使用現成的 factory 類。

乙個通用、客戶端可以輕易重新定義出現 unknown type 時的行為的泛型工廠類到此就完全實現了。再次感慨一下 c++ 模板的強大。

當然,上面的泛型類任然有其缺陷,比如在構造的時候無法傳入引數,這對於很多沒有預設建構函式的物件來說是無法接受的,乙個可能的改進方法是在 createobject 函式新增乙個 void * 引數。具體對引數的使用邏輯重新落在每個 creator 中。

abstractptr createobject(const identifier &id, void *arg) const 

return (it->second)(arg);

}

乙個抽象工廠的例子

最近在學習設計模式,首先在工廠模式中,遇到了抽象工廠模式,所以自己設計了個例子,大概說明了下道理,如下 package abstractfactory abstract class makeca ctory abstract class bmw abstract class linken class...

乙個簡單的抽象工廠模式

抽象工廠模式引出了產品族的概念 product family 給出下邊的例子 乙個生產廠 富士康 能生產蘋果的或者是谷歌的手機和膝上型電腦。蘋果和谷歌是兩個不同的產品族。手機和膝上型電腦是兩個不同的產品型別。生產乙個產品首先選擇它的產品組,然後確定產品型別就能生產出來。具體 手機 abstract ...

僅僅乙個工廠設計模式的例子

public inte ce iworkfactory public class studentwork implements work public class studentworkfactory implements iworkfactory public class teacherwork ...