Pluggable Object Factory 可插入式對象工廠

     ITK 中使用的其實是種稱爲 pluggable object factory 的設計模式,它可以在運行時進行動態替換,而且可以不修改已有代碼的情況下添加處理新格式的圖像文件類,等還有一些其它優點。它由兩種模式組合而成:1.Abstract Factory 抽象工廠;2. Prototype 原型模式。還可能與單件模式(Singleton)組合。

簡單的對象工廠可能如下所示:

   1:   
   2:  //Simple Factory
   3:  Circle 
   4:  {
   5:      double radius;
   6:  }
   7:   
   8:  class Shape 
   9:  {
  10:  public:
  11:      static Shape* newShape(istream&);
  12:  };
  13:  Shape* Shape::newShape(istream& params)
  14:  {
  15:      string className;
  16:      params >> className;
  17:      if (className == "Circle")
  18:          return new Circle(params);
  19:      else if (className == "Triangle") 
  20:          return new Triangle(params);
  21:      else
  22:          //…
  23:  }
  24:   

Shape 是一個用來創建不同形狀的對象工廠,Circle 是一種具體的形狀。對象工廠 Shape 通過判斷傳入的參數,創建不同的對象實例。簡單對象工廠的問題在於:1.基類 Shape 必須知道從它繼承的每一個具體子類。2.當需要添加一種新的形狀時,就得修改工廠方法 newShape()。這就違背了面向對象設計的“依賴倒置”原則(Dependency Inversion Principle),即高層模塊不應該依賴於低層模塊。同時還違背了“開-閉”原則(Opened-Closed),即 Open to extension,Closed to Modification。Pluggable Object Factory 模式可以解決這些問題。

Pluggable Object Factory,或者稱爲 PROTOTYPE–ABSTRACT FACTORY,可以將該模式歸爲創建型一類。

     使用一個關聯式容器如 map,關聯容器中的每一個元素都是一個對象工廠,該對象工廠知道如何從一個抽象類層次結構中創建具體的子類對象實例。關聯容器中的 key 則是用來從容器中查找合適的對象工廠。

Prototype Factory Compound Pattern:使用工廠方法,我們創建一個抽象類 ShapeMaker,它有一個靜態的公有方法 newShape。一個具體的 CircleMaker 知道如何創建 circle 對象,它繼承自 ShapeMaker。

   1:  //
   2:  class ShapeMaker
   3:  {
   4:  public:
   5:      static Shape* newShape(istream&);
   6:  protected:
   7:      typedef map<string,ShapeMaker*> MakerMap;
   8:      virtual Shape* makeShape(istream&) const=0;
   9:      static MakerMap registry;
  10:  };
  11:  Shape* ShapeMaker::newShape(istream& is) {
  12:      string className;
  13:      is >> className;
  14:      ShapeMaker* maker = 
  15:          (*registry.find(className)).second;
  16:      return maker->makeShape(is);
  17:  }
  18:  //
  19:  class CircleMaker : public ShapeMaker 
  20:  {
  21:  private:
  22:      Shape* makeShape(istream& params) const 
  23:      { 
  24:          return new Circle(params); 
  25:      }
  26:  };
  27:   
 
     同樣,我們可以創建新的具體工廠類,如 TriangleMaker、RectangleMaker 等。每一個具體的工廠的實例都被保存於一個關聯容器中。基類 ShapeMaker 通過一個 key 關聯具體的工廠子類,如上面代碼所示這裏的 key 使用的是類名 className。 newShape(className) 通過在關聯容器中查找對應於 className 的具體的對象工廠來創建具體的對象實例。這樣,我們便可以不修改工廠方法而添加新的對象工廠。
Registering Factories Using Prototype:Prototype 原型模式提供了在關聯容器中動態註冊對象工廠的方法。每一個具體的對象工廠都維護一個自身的靜態對象實例變量,該靜態對象實例的唯一目的就是在基類的靜態關聯容器中註冊自己。
   1:   
   2:  //
   3:  class CircleMaker : public ShapeMaker
   4:  {
   5:  private:
   6:      CircleMaker() : ShapeMaker("Circle") {}
   7:      static const CircleMaker registerThis;
   8:  };
   9:  //其中:
  10:  ShapeMaker::ShapeMaker(string className) 
  11:  {
  12:      //在基類的靜態關聯容器中註冊自己
  13:      registry.insert( make_pair(className, this) );
  14:  }
  15:  //使用:
  16:  fstream params("shapes.txt");
  17:  Shape* shape = ShapeMaker::newShape(params);
  18:   
 

Plugging in with Prototype Factory:Factory Method 工廠方法與 Prototype 原型模式結合,允許工程擴展 ShapeMaker可以創建對象的 Shape 類型。ShapeMaker 類以及與其協作的其它類表示了 Prototype Factory 模式。Factory Method 工廠方法提供創建對象實例的方法,Prototype 原型模式提供在運行時動態註冊對象工廠的方法。

1.GENERIC FACTORIES 泛化工廠:從上面的 Prototype Factory 模式可以看出,算法與數據類型是相互獨立的,所以我們使用模板實現 Prototype Factory 組合模式:

   1:   
   2:  //Generic 工廠
   3:  template<class Object>
   4:  class Maker 
   5:  {
   6:  public:
   7:      virtual ~Maker();
   8:      static Object* newObject(istream&);
   9:  protected:
  10:      Maker(const string& className);
  11:      virtual Object* makeObject(istream&) const=0;
  12:  private:
  13:      typedef Maker
  14:      typedef map<string,MakerPtr> MakerMap;    
  15:      static MakerMap registry;
  16:  };
  17:   

這樣,ShapeMaker 便可如下重寫:

   1:  //
   2:  template<class Shape>
   3:  class ShapeMaker : public Maker
   4:  {
   5:  protected:
   6:      ShapeMaker(const string& className)
   7:          : Maker(className) {}
   8:  };
   9:  class CircleMaker : public ShapeMaker 
  10:  { 
  11:  private:
  12:      CircleMaker () : ShapeMaker ("Circle") {}
  13:      Shape* makeObject(istream& params) const { 
  14:          return new Circle(params); }
  15:      static const CircleMaker registerThis;
  16:  };
  17:   

還可以很容易地創建其它對象工廠的抽象類層次,如下:

   1:  //
   2:  template<class CoordSys>
   3:  class CoordSysMaker : public Maker
   4:  {
   5:      //…
   6:  };
   7:  class XyzMaker : public CoordSysMaker
   8:  {
   9:      //…
  10:  };
  11:  class RasterMaker : public CoordSysMaker
  12:  {
  13:      //…
  14:  };
  15:   

Making Objects from Files:有時我們可能不使用 C++ 的 istream 流作爲 newObject() 的參數,而想使用 FILE*,我們可以將 Maker 的模板參數如下修改即可:

   1:  //
   2:  template <class Object, class Params> 
   3:  class Maker 
   4:  {
   5:  public:
   6:      static Object* newObject(Params);
   7:  protected:
   8:      typedef Maker
   9:      virtual Object* makeObject(Params) const=0;
  10:  };
  11:  //
  12:  class ShapeMaker 
  13:      : public Maker
  14:  { 
  15:      //...
  16:  };
  17:  class LegacyShapeMaker
  18:      : public Maker
  19:  { 
  20:      //… 
  21:  };
  22:   

Making Objects from Aggregate Classes:我們還可以從一個包含某個具體子類數據成員的聚集類構造對象:

   1:  //
   2:  class GeomModel
   3:  {
   4:      //...
   5:  private:
   6:      const char*    sensorName;
   7:      FILE*          geomFile;
   8:      const char*    outputDir;
   9:  };
  10:  //結構體,用於傳遞構造 GeomModel 所需的參數
  11:  struct GeomModelParams 
  12:  {
  13:      const char*    sensorName;
  14:      FILE*          geomFile;
  15:      const char*    outputDir;
  16:  };
  17:  class GeomModelMaker 
  18:      : public Maker
  19:  { 
  20:      //… 
  21:  };
  22:   
 

2.Chain of Factories:如果我們要創建的對象的類型不是流的第一個元素,那麼上面的工廠就會失效。我們可以通過將職責鏈模式(Chain of Responsibility)組合到我們 Pluggable 工廠模式來解決這個問題。職責鏈模式允許我們順序迭代一個 Makers 鏈表,讓 makers 決定哪一個 maker 可以創建該對象。在職責鏈上通用的查找機制可能如下:

   1:  //
   2:  Object* Maker
   3:  ::newObject(Params params) 
   4:  {
   5:      Object* object = 0;
   6:      for (const_iterator iter = registry.begin(); 
   7:           !object && iter != registry.end(); ++iter )
   8:      {
   9:          MakerPtr maker = (*iter).second;
  10:          object = maker->makeObject(params);
  11:      }
  12:      return object;
  13:  }
  14:   

3.PRIORITIZING FACTORIES,優先工廠:前面我們一直都在使用關聯容器 map 存儲 key 與 對象工廠之間的連繫,我們知道 map 中的 key 是唯一的,也就是說我們前面假定了 key(params) 與 對象工廠(maker)之間存在 1-1 的關係。但實際中經常可能會出現不只有一個對象工廠可以創建某個對象,這時我們只需要將 map 改成 multimap,並在創建對象時設置一定的優先策略即可。其中,ITK 中的對象工廠使用的便是 multimap,一個對象可能會有多個工廠可以創建。

我們創建一個新類 PriorityMaker 表示優化查找策略,它從對象工廠列表中查找可以創建指定對象優先級最高的對象工廠:

   1:  //Priority Factory
   2:  template <class Object>
   3:  class PriorityMaker 
   4:      : public Maker
   5:  {
   6:  public:
   7:      static Object* newObject(istream& params) 
   8:      {
   9:          string className;
  10:          params >> className;
  11:   
  12:          //定位第一個可以創建 className 對象的對象工廠
  13:          const_iterator iter = registry.lower_bound(className);
  14:   
  15:          //繼續在 multimap 中查找,直至某個對象工廠不能創建 className 對象實例
  16:          //並進行簡單的比較,選擇優先級最高的對象工廠
  17:          const_iterator tmp = iter;    
  18:          while (    ++tmp != registry.end() && 
  19:                  (*tmp).first == className) 
  20:          {
  21:              if (*(*iter).second < *(*tmp).second)
  22:                  iter = tmp; 
  23:          }
  24:   
  25:          //最後使用選擇到的對象工廠創建對象實例
  26:          MakerPtr maker = (*iter).second;
  27:          return maker->makeObject(params);
  28:      }
  29:  }
  30:   
     ITK 中的策略是:在 multimap 中查找可以創建 className 的對象工廠,然後選擇第一個對象工廠進行創建。ITK 中還提供了一個方法,該方法會使用所有可以創建 className 的對象工廠,依次創建出所有的對象實例。

4.SINGLETON FACTORIES,單件工廠:當我們只有一個選擇的時候,會需要單件工廠,就如同單件對象一樣。單件工廠的查找策略非常簡單,因爲在某一時間只有一個對象工廠會被註冊。如果發生不止一個對象工廠被註冊的錯誤,我們原則是:使用最後一個註冊的對象工廠。

   1:  //Singleton Factory
   2:  template <class Object>
   3:  class SingletonMaker : public Maker
   4:  {
   5:  public:
   6:      static Object* newObject() 
   7:      {
   8:          //rbegin:如果出現多個工廠註冊的錯誤,我們選擇最後一個
   9:          MakerPtr maker = 
  10:              (*registry.rbegin()).second; 
  11:          return maker->makeObject();
  12:      }
  13:  protected:
  14:      SingletonMaker() 
  15:          : Maker
  16:  };
  17:   

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章