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* 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:   

 

0
0
猜你在找
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
快速回复 TOP
    个人资料
    Pluggable Object Factory 可插入式对象工厂_第1张图片
    zhangcunli
    • 访问:80807次
    • 积分:1433
    • 等级:
    • 排名:第17778名
    • 原创:56篇
    • 转载:1篇
    • 译文:1篇
    • 评论:39条
    文章分类
  • C++(21)
  • DesignPattern(32)
  • ITK(18)
  • 其它(5)
  • 随感(4)
  • 霹雳相关(4)
  • linux(1)
  • Golang(0)
    文章存档
  • 2012年05月(1)
  • 2011年10月(1)
  • 2010年01月(5)
  • 2009年12月(19)
  • 2009年11月(7)
  • 2009年10月(11)
  • 2009年09月(12)
  • 2008年09月(2)
    阅读排行
  • ITK 编译安装(7696)
  • 初学者编译Linux内核,最简单ko模块(7352)
  • ITK 配准框架示例(6090)
  • ITK 编程步骤示例(3155)
  • ITK 数据源(3120)
  • 霹雳狂刀---回忆录(2458)
  • SVN 初步(2268)
  • ITK Viola-Wells 互信息测度配准示例(2215)
  • ITK 中的 ImageIO 图像输入输出结构框架(2043)
  • Multi-Resolution 多分辨策略示例(1798)
    评论排行
  • ITK 数据源(11)
  • ITK 配准框架示例(9)
  • ITK 编译安装(4)
  • Multi-Resolution 多分辨策略示例(3)
  • 使用 CL 编译器选项查看 C++ 类内存布局(3)
  • 霹雳狂刀---回忆录(3)
  • Command 命令模式(2)
  • Linux 下配置 ITK(1)
  • ITK Viola-Wells 互信息测度配准示例(1)
  • 初学者编译Linux内核,最简单ko模块(1)
    推荐文章
    • *Android自定义ViewGroup打造各种风格的SlidingMenu
    • * Android 6.0 运行时权限处理完全解析
    • * 数据库性能优化之SQL语句优化
    • *Animation动画详解(七)——ObjectAnimator基本使用
    • * Chromium网页URL加载过程分析
    • * 大数据三种典型云服务模式
    最新评论
  • 使用 CL 编译器选项查看 C++ 类内存布局

    jackap: 图片看不到了,可以修复下吗?

  • ITK 配准框架中的 Subject/Observer 模式及优化过程模拟演示-4

    aaaa_1111_: 楼主,您好!请问ITK中的三维配准有什么要求吗?DeformableRegistration8中优化...

  • ITK 数据源

    jamesLBJ: 楼主你好,请问ITK怎么读.mhd和.raw格式的?还是只能用vtk来读取?

  • Linux 下配置 ITK

    lj695242104: cmake 在ubuntu下gui?您说的按箭头操作根本就没出现啊!求解?

  • ITK 配准框架示例

    u014376282: ITKIO不存在啊

  • 霹雳布袋戏所有剧集

    u011683778: 怎么没有 霹雳震寰宇之龙战八荒?

  • 霹雳狂刀---回忆录

    u011472090: 我也是山西人,小时候没看完,现在网上的资源不太给力,模糊,没有国语的

  • ITK 编译安装

    TonyShengTan: 正在编译中,不过cmake'提示Performing Test CXX_HAS_WARNING-Wf...

  • 初学者编译Linux内核,最简单ko模块

    thfeathers: 不得在任何场合表达与公司有关的信息,给你个一级违规

  • ITK 配准框架示例

    gfhhgf: 这是一个itk自带的例子吧

你可能感兴趣的:(Pluggable Object Factory 可插入式对象工厂)