alex

alex

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  7 随笔 :: 6 文章 :: 7 评论 :: 0 Trackbacks

abstract factory.感觉是个很玄的名字,即使在我写学习笔记的时候,我也因为缺少现实中的实践而对其了解不深。^^希望.... 多指正
Name:
   abstract factory
Addressed Problem:
   提供对同1种相关对象的创建。比如在程序里面,希望根据用户配置来设置UI方案,比如方案1和2,对应的UI也存在2套不同的实现类,比如对于ScrollBar,有CScrollBar1和CScrollBar2,都继承自ScrollBar。也许在程序里面有很多这样的类。这样你在应用UI实例化类时就必须要选择比如
IScrollBar* pScrollBar = NULL;
if (_use_ui_1)
   pScrollBar = new CScrollBar1;
else
   pScrollBar = new CScrollBar2;
也许你这样感觉没多大关系,但想象下,在程序的多处都存在这样的选择,为什么我不选择一种less error phone的方法呢?比如
pScrollBar = Create(...);
这样一种简单的方式呢?而且上面的写法也引进了1个避短,也就是在产生pScrollBar时,涉及到了具体的实现类,这在面向对象这种program to interface这样的语言中是很忌讳的事情。比如,在加种UI方案,也许你会再写个else,这样等于自己把自己往火堆里面推。你是一个勤奋的程序员,但不是个优秀的程序员^^.也许有人会说,我可以利用prototype这样的方式来啊。这样,在程序的外面配置一下每个类的prototype就可以了,或许也可以用object factory啊,都可以(^^似乎是哦,我也没学过这2个的说)解决上面的问题,一个通过DoClone类似的,1个通过ID或其他的,其实在应用上面的2种时,自然的导出了abstract factory.为什么呢,因为上面的2种,你都需要设置多个的原型或ID。比如也许在CUIManager的构造里面写贼大的if
if (_use_ui_1)
{
    ...//设置UI_1下所有的prototype
}
else
{
    ...//设置UI_2下所有的prototype
}
也许在将来的莫天,在加上UI_3,则。再写个if.也许大家可能会想,我在应用ui时,我就知道了所要创建的对象,为什么我不把上面宝装一下改成:
if (_use_ui_1)
 //设置factory_1
else
 //设置factory_2
然后在抽象出来的IFactory里面提供创建这些UI Class的方法,比如,CreateScrollBase.等等,这样就有了abstract factory 的雏形。
基础实现
/*
 测试abstract factory模式
*/

class IScrollWindow
{
public:
 virtual void DrawScroll(void) = 0;
};

class IListWindow
{
public:
 virtual void DrawList(void) = 0;
};

class ICreateWindowFactory
{
public:
 virtual IScrollWindow* CreateScrollWindow(void) = 0;

 virtual IListWindow* CreateListWindow(void) = 0;
};

class CBlueWindowFactory:
 public ICreateWindowFactory
{
 class CBlueScrollWindow:
  public IScrollWindow
 {
 public:
  virtual void DrawScroll(void)
  {
   std::cout << "draw blue scroll" << std::endl;
  }
 };

 class CBlueListWindow:
  public IListWindow
 {
 public:
  virtual void DrawList(void)
  {
   std::cout << "draw blue list" << std::endl;
  }
 };
public:
 virtual IScrollWindow* CreateScrollWindow(void)
 {
  return new CBlueScrollWindow;
 }

 virtual IListWindow* CreateListWindow(void)
 {
  return new CBlueListWindow;
 }
};

class CRedWindowFactory:
 public ICreateWindowFactory
{
 class CRedScrollWindow:
  public IScrollWindow
 {
 public:
  virtual void DrawScroll(void)
  {
   std::cout << "draw red scroll" << std::endl;
  }
 };

 class CRedListWindow:
  public IListWindow
 {
 public:
  virtual void DrawList(void)
  {
   std::cout << "draw red list" << std::endl;
  }
 };
public:
 virtual IScrollWindow* CreateScrollWindow(void)
 {
  return new CRedScrollWindow;
 }

 virtual IListWindow* CreateListWindow(void)
 {
  return new CRedListWindow;
 }
};
在需要用的地方,就可以这样:
ICreateWindowFactory* pCreateWindow1 = new CBlueWindowFactory;

 pCreateWindow1->CreateListWindow()->DrawList();
 pCreateWindow1->CreateScrollWindow()->DrawScroll();
假如需要用不同的工厂,则更换不会影响到调用处的代码。因为掉用工厂的地方是面向接口的。其实abstract factory的理念应该是比较简单的(^^瞎猜的).基本讲完了什么是抽象类工厂,他要解决的一些问题以及怎么解决和1个小而乱的demo代码段。下面来看下我们怎么泛化这个类工厂,这个会涉及到loki里面的具体实现,大家要加满油啊,因为泛化类工厂是一件不容易的事情啊。

泛化_1
首先,(^^这部分我也不是很懂)要泛化的是abstract factory的接口,就象上面的CreateScrollWindow和CreateListWindow,在泛化时需要的信息是要创建的同1组对象的相关接口比如IScrollBar,IList等等,在loki里面,要为1个类泛化1组接口,可以通过GenScatterHierarchy来将unit应用到typelist里的每1个类型,并将该类从unit<type>派生,从而得到1组接口。GenScatterHierarchy做了什么呢,他产生了啥呢?具体的可以看morden c++里面的实现。通过GenScatterHierarchy我们得到了我们要得1组接口。下面是loki里面对这个得相关实现
template <class T>
class AbstractFactoryUnit
{
public:
    virtual T* DoCreate(Type2Type<T>) = 0;
    virtual ~AbstractFactoryUnit() {}
};

可以看到,上面定义了2个函数,而这个类就是我上面说得调用GenScatterHierarchy时,具现化时对typelist得每个类型应用得template类,而最后产生得也将是类似AbstractFactoryUnit<IScrollBar>的类,我们具体的抽象工厂从这些派生。至于pure dctor这个大家应该都知道啥作用。下面来看Abstract Factory 的泛化:

template
<
    class TList,
    template <class> class Unit = AbstractFactoryUnit
>
class AbstractFactory : public GenScatterHierarchy<TList, Unit>
{
public:
     typedef TList ProductList;
       
     template <class T> T* Create()
     {
        Unit<T>& unit = *this;
        return unit.DoCreate(Type2Type<T>());
     }
 };
可以看到这个即由GenScatterHierarchy来得到了我们想要的东西。提供了Create的模板函数,使得我们可以象这样factory.Create<IScrollBar>()的方便形势来调用。ProductList是对于抽象工厂要创建的类型的重命名。方便后面在产生实际的类型时,获取对应的类型信息,对于DoCreate的参数,大家应该都明白这是重载用的,那用在哪里呢?下面会介绍。

泛化_2
在辛苦介绍完泛化抽象工厂的接口后,我们可以通过类似的方式来定义1个abstract factory的接口
Loki::AbstractFactory<LOKI_TYPELIST_2(IScrollBar,IListWindow)>
下面我们来介绍最后的,我们怎么来提供抽象工厂的实现,首先是对象的创建,loki里面提供了默认的创建的方法,当然我们可以修改或用特化的版本来做选择。
template <class ConcreteProduct, class Base>
class OpNewFactoryUnit : public Base
{
     typedef typename Base::ProductList BaseProductList;
   
protected:
     typedef typename BaseProductList::Tail ProductList;
   
public:
     typedef typename BaseProductList::Head AbstractProduct;
     ConcreteProduct* DoCreate(Type2Type<AbstractProduct>)
     {
          return new ConcreteProduct;
     }
};
可以看到DoCreate是我们的核心部分,里面调用了new来创建对象。而这里也让我们看到这应该是上面创建对象的重载。而Type2Type的作用正是在这里体现作用,因为c++无法通过函数返回值来重载不同的对象。也许你会看到上面的一些类型定义,包括OpNewFactoryUnit的2个模板参数,第2个模板参数是实现GenLinearHierarchy必备的GenLinearHierarchy和上面的GenScatterHierarchy的核心思想一样,都是通过具现化来实现的,不过GenLinearHierarchy产生的是线性的继承体系,中间夹杂着比如OpNewFactoryUnit<IScrollBar,GenLinearHierarchy<...> >这样的形势。下面来看下抽象工程的具体实现的泛化,联系起来就能对上面的理解了
template
<
    class AbstractFact,
    template <class, class> class Creator = OpNewFactoryUnit,
    class TList = typename AbstractFact::ProductList
>
class ConcreteFactory
     : public GenLinearHierarchy<
     typename TL::Reverse<TList>::Result, Creator, AbstractFact>
{
public:
    typedef typename AbstractFact::ProductList ProductList;
    typedef TList ConcreteProductList;
};
可以看到ConcreteFactory由GenLinearHierarchy来驱动产生我们想要的,本来这个贴图比较明朗点,但我懒的画,哈哈。从GenLinearHierarchy的参数来看,第1个是具体的实现类的typelist,比如LOKI_TYPELIST2(CScrollBar_1,CListWindow_1),至于为什么要对类型做reverse操作,因为在类的基础体系产生后,typelist的第1个元素,在继承体系是由下往上的,而于上面由OpNewFactoryUnit等定义的ProductList的Head定义的自上往下的是相反的,所以这里应用了reverse操作。Creator就是上面的OpNewFactoryUnit或你自定义的元素。并在具现化是,应用typelist的每个类型。AbstractFact是继承体现的最顶端,这个应该很明显,就是上面定义的AbstractFactory,这样啥都明确了,具体的接口,对接口的函数的重载都已经泛化完成。下面是(^^睡死了,睡觉去了,简单从便,上面均未调试哦).至于为什么会把第3个参数默认为抽象类工厂接口的ProductList,这个和loki用基于prototype的Creator有关系。。(^^俺是菜鸟,就只能到这里了)
对abstract factory的看法,优点在上面的已经说过了,确定在,我们要添加1种对象的创建时,都要去修改接口的定义,当然后面的泛化也为我们解决了些问题,但泛化对于参数的传递不怎么好用,可以通过提供新的OpNewFactoryUnit来适当解决。
                                                    agerlis.2007.2.10 0:22
posted on 2007-02-11 15:53 agerlis 阅读(372) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理