對象工廠(2)---一個泛化的工廠類

在個人上一篇博客 對象工廠(1)---和萬惡的 switch 說再見中,咱們經過使用函數指針索引的方法,爲咱們的工廠類代碼中消除了 switch 語句。本篇博客的目標是將實現一個泛化的工廠類,實現代碼複用。

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

  1. Abstract 基類(文中的 Shape),經過操做這個基類的指針,整個系統實現了運行期多態
  2. Concrete 子類(文中的 Line 等),每一個攜帶真正邏輯代碼的具體類。
  3. Identifier(文中的 int),Factory 類須要從 Identifier 映射到正確的具體類建立函數。
  4. Creator(文中的 CreateLine 等),用於真正產生具體類的各個函數。
若是咱們使用模板編程,將上面的概念做爲模板參數,那麼一個泛型工廠類便呼之欲出了。但是,且慢,讓咱們分析一下這4個角色是否 Factory 類都須要瞭解呢?答案顯然不是,Concrete 子類對 Factory 徹底是透明的,Factory 在建立對象的時候,不須要也不該該關心到底有哪些具體子類(這個正是咱們上篇博客所解決的問題)。

如今給出泛型工廠類的基本代碼:程序員

template
<
  class AbstractProduct,
  typename IdentifierType,
  typename ProductCreator = AbstractProduct *(*)(),
>
class Factory {
public:
  bool Register(const IdentifierType &id, ProductCreator creator) {
    return associations_.insert(typename AssocMap::value_type(id, creator)).second;
  }
  bool Unregister(const IdentifierType &id) {
    return associations_.erase(id) == 1;
  }
  AbstractProduct *CreateObject(const IdentifierType &id) const {
    typename AssocMap::const_iterator i = associations_.find(id);
    if (i == associations_.end()) {
      // 嗚呼,出現錯誤了,這裏應該給客戶端自有處理這種狀況的自由
    }
    return (i->second)();
  }
private:
  typedef std::map<IdentifierType, ProductCreator> AssocMap;
  AssocMap associations_;
};
上面的代碼爲 ProductCreator 提供了一個默認的類型,一個函數指針,語法乍看有點怪異,可是若是這樣寫想必有點經驗的 c/c++ 程序員均可以輕易認出:
typedef AbstractProduct *(*CreateFn)();
上面的代碼爲不接受任何參數,返回 AbstractProduct 指針的函數指針定義了一個 CreateFn 別名。

接下來讓咱們完善一下這個設計,如代碼中的紅字指出,若是一個工廠類沒法識別的 type 出現了,咱們改如何處理呢?典型的處理方法有:編程

  1. 拋出異常
  2. 返回一個空指針
  3. 提供一個默認指針
每個解決方法在不一樣的場景下都是一個合適的處理方式,好比若是你須要儘早發現這種意料以外的狀況,那麼拋出異常是個不錯的選擇,若是你在開發一個網絡服務器,那麼顯然若是直接拋出異常可能會倒置服務器 down 掉,那這時返回一個空指針,並記錄錯誤日誌多是一個更好的選擇。因此咱們必須給予客戶端指定如何處理的權限。

這裏使用的實現模式成爲 Policy 模式(詳見《moder c++ design》一書),這種模式將程序中一些能夠有多種實現方法的地方區分出來,並抽象爲一個 Policy,經過模板參數傳入,使用相似組裝的方法,將多個 Policy 組裝成一個實用的 class,最大程度實現代碼和組件的複用(其實我的感受模板編程更像是面對接口編程,只是接口不是明確寫出的,而是隱藏在代碼中的)。segmentfault

爲了解決上面的問題,咱們引入一個 FactoryErrorPolicy,這個 Policy 負責提供一個接口,接口用於處理未知的 type,也就是說其多是這樣子:服務器

template<class AbstractProduct, typename IdentifierType>
class FactoryErrorPolicy {
protected:
  (static) (static) AbstractProduct *OnUnknownType(const IdentifierType &id);
};
注意代碼中被括起來的 static 關鍵字,這並非什麼晦澀的語法,而是我想在此說明,由於咱們使用的是模板編程,它是面向語法的,不是面向標記的,無論你是 static 與否,只要能夠正常運行就知足咱們的接口。這個我會在以後再加以解釋,如今咱們先完全完善咱們的 Factory 類:
template
<
  class AbstractProduct,
  typename IdentifierType,
  typename ProductCreator = AbstractProduct *(*)(),
  template<class, class> class FactoryErrorPolicy = DefaultFactoryError
>
class Factory
  : public FactoryErrorPolicy<AbstractProduct, IdentifierType> {
public:
  bool Register(const IdentifierType &id, ProductCreator creator) {
    return associations_.insert(typename AssocMap::value_type(id, creator)).second;
  }
  bool Unregister(const IdentifierType &id) {
    return associations_.erase(id) == 1;
  }
  AbstractProduct *CreateObject(const IdentifierType &id) const {
    typename AssocMap::const_iterator i = associations_.find(id);
    if (i == associations_.end()) {
      return this->OnUnknownType(id);
    }
    return (i->second)();
  }
private:
  typedef std::map<IdentifierType, ProductCreator> AssocMap;
  AssocMap associations_;
};
注意新引入的兩行代碼,這裏咱們爲模板參數增長了一個 Policy 類,並在代碼中隱式的要求
FactoryErrorPolicy<AbstractProduct, IdentifierType>
類能夠提供一個 OnUnknownType() 函數接口,其能夠接受 int 做爲參數(它的簽名能夠是接受 double, char, 任何能夠經過int隱式轉化的類型)調用就能夠了,至因而不是 static 咱們也不關心,甚至它的聲明能夠是這樣:
void *OnUnknownType(double d, int i = 0, string="");
這是徹底能夠的,在模板編程中,咱們只須要 this->OnUnknownType() 這句語句能夠經過編譯就能夠了,這是模板編程和麪向對象最大的不一樣點之一,我的認爲其靈活性遠超面向對象編程。還要注意的是 Policy 模式每每經過定義的模板類 public 繼承 Policy 類來實現。

咱們甚至還爲這個 Policy 提供了一個默認的 class 來減小客戶端的編碼成本:網絡

template<class AbstractProduct, typename IdentifierType>
class DefaultFactoryError {
protected:
  static AbstractProduct *OnUnknownType(const IdentifierType &id,
                                        int i = 0,
                                        std::map<int, int> *p=NULL) {
    return NULL;
  }
};
爲了佐證我上面的論點,我特地選擇了一個很奇怪的實現,至此咱們完整實現了一個泛型工廠類, 上一篇博客中能夠很容易的複用這個類,只須要用:
typedef Factory<Shape, int> ShapeFactory;
替代以前的 ShapeFactory 實現,而且適當修改 Factory 類的調用處(由於接口名字有所改變,好比 CreateShape 改成 CreateObject),就能夠直接使用現成的 Factory 類。

一個通用、客戶端能夠輕易從新定義出現 UnKnown Type 時的行爲的泛型工廠類到此就徹底實現了。再次感慨一下 c++ 模板的強大。函數

固然,上面的泛型類任然有其缺陷,好比在構造的時候沒法傳入參數,這對於不少沒有默認構造函數的對象來講是沒法接受的,一個可能的改進方法是在 CreateObject 函數添加一個 void * 參數。具體對參數的使用邏輯從新落在每一個 Creator 中。this

AbstractPtr CreateObject(const Identifier &id, void *arg) const {
        typename Creators::const_iterator it = creators_.find(id);
        if (it == creators_.end()) {
            return this->OnUnknownType(id);
        }
        return (it->second)(arg);
    }
固然這樣會致使不少不須要參數的類在註冊 Creator 的時候必須使用一個可用一個 void *作參數的 Creator,增長了一點程序員的負擔。筆者暫時沒法想到更好的解決方法,若是有同窗能夠指教的話歡迎在下面留言。

鑑於自身水平有限,文章中有不免有些錯誤,歡迎你們指出。也但願你們能夠積極留言,與筆者一塊兒討論編程的那些事。編碼

相關文章
相關標籤/搜索