天天看點

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:      
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         * MakerPtr;       
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
      
       * MakerPtr;
            
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
       
        void> 
             
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
       
        void>(
        "SingletonMaker"){}
             
16:  };      
17: