码迷,mamicode.com
首页 > 其他好文 > 详细

Object Factories(对象工厂)

时间:2015-02-13 16:01:52      阅读:165      评论:0      收藏:0      [点我收藏+]

标签:

1,为什么需要对象工厂?

 我们创建一个对象时,必须给出具体型别,new A也好,new B也罢。A,B都是对象的型别。我们向来反对写hardcode,但却在创建对象时必须hardcode。

 如果需要根据用户的输入信息,或是网络反馈信息,或是文本文件信息来创建对象时,应该怎么办呢?

 最初我们想法可能是这样的,伪代码

switch(Info)
{
  case a: return new A;
  case b: return new B;
  ...
  default:...
}

这就是大家都熟悉简单工厂模式,他虽然简单,但蕴含一个基本原则:根据value(a,b...)查找type(A,B...),利用type创建value(A*,B*...)

利用对象工厂,我们创建对象时得到了解脱,无需写hardcode,不必给出类型,我们可以提供数字,字符串,以及特定格式信息来创建一个对象。

好的,这就是对象工厂存在的意义!

2.简单工厂不够完美吗?

是的,不够完美。理由有三:

  • 它使用了switch语句,因而带有switch语句相应缺点,这是面向对象极力消除的东西(switch本身就是一种hardcode,所以面向对象:多态)
  • 工厂类需要收集其生产的所有类型信息,编译依存性很强哦
  • 想要添加一个新类型,需要修改工厂代码,而且通常不只一处。(引入头文件,添加常量,修改switch等)

3.我们的目标是什么?

  • 工厂不使用switch语句
  • 工厂可伸缩,可以动态添加或删除所生产的产品
  • 可以对工厂提供构造对象的原料,且数量可变
  • 泛化工厂,可以无需编码支持不同类型抽象产品
  • 减少内存管理复杂度
  • 尽量做到类型安全

4.看看实现,这才是重点:(在我不断完善实现的过程中,我发现自己是为了模式而模式,所以以下代码仅供参考

 1 struct package
 2 {
 3     void * funcSet;
 4     void * func;
 5     size_t index;
 6     string sig;
 7 };
 8  
 9 template <typename ...> class TypeList {};
10  
11 template <typename AbstractProduct ,typename IdentifierType = string> class FactoryImpl
12 {
13 public:
14     template <typename... Arg> bool Register(const IdentifierType& id,const function<unique_ptr<AbstractProduct>(Arg...)>& creator)
15     {
16  
17         static vector<function<unique_ptr<AbstractProduct>(Arg...)>> vf;
18         typename AssocMap::const_iterator i =associations_.find(id);
19         if(i!= associations_.end()) return false;
20         vf.push_back(creator);
21         return associations_.insert(typename AssocMap::value_type(id,package {&vf,&vf.back(),vf.size()-1,string(typeid(TypeList<Arg...>).name())})).second;
22  
23     }
24  
25     template <typename ... Arg >
26     bool UnRegister(const IdentifierType& id)
27     {
28         typename AssocMap::const_iterator i =associations_.find(id);
29         if(i != associations_.end())
30         {
31             assert(
32                 ((i->second).sig).compare(typeid(TypeList<Arg...>).name())==0
33             );
34             auto vf=static_cast<vector<function<unique_ptr<AbstractProduct>(Arg...)>>*>((i->second).funcSet);
35             vf->erase(vf->begin()+(i->second).index);
36         }
37  
38         return associations_.erase(id)==1;
39     }
40  
41     template <typename... Arg> unique_ptr<AbstractProduct> Createobject(const IdentifierType& id,Arg&&... args)
42     {
43         typename AssocMap::const_iterator i =associations_.find(id);
44  
45         if(i != associations_.end())
46         {
47             assert(((i->second).sig).compare(typeid(TypeList<Arg...>).name())==0);
48             auto funp=static_cast<function<unique_ptr<AbstractProduct>(Arg...)>* >((i->second).func);
49             return (*funp)(std::forward<Arg>(args)...);
50         }
51         assert(false);
52     }
53  
54  
55 private:
56     typedef std::unordered_map<IdentifierType,package> AssocMap;
57     AssocMap associations_;
58  
59 };

 

 代码释疑:

  •  17行,在干什么?是这样的,客户传过来的function对象很可能在调用时已经释放了,如果我们不保存一个function对象,仅仅将其function对象指针转变为void*,而之后我们在把void*转换为function对象指针,调用时如果function对象已经释放了,那么必然出现内存错误。
  •  恰恰因为17行原因,一切变得非常棘手。最难实现的是UnRegister函数,这也是这段代码最大的败笔。UnRegister强迫客户显式提供正确的型别,否则不能工作。为什么一定要提供型别?因为型别擦除后,编译器已无法知道它真的型别,需要明确进行强制转换后,才能在vector中删除function对象。不然,内存泄露直至进程退出。
  • 所有函数都加入assert,防止客户出错,提供不正确参数将导致进程终止。
  • 代码中使用std::function和unique_ptr, std::function为不同形式的可调用体包装为相同型别,并保持统一的调用。unique_ptr则是为了方便内存管理。
  • 1-7package结构体中 func指向用户提供function对象,funcSet指向Register函数中static vector,index为其在func指针在funcSet中位置,sig可以理解为函数签名

因为没有在类模板使用可变参数,所以实现起来很是费劲,我现在倒觉得可以使用类模板带可变参数实现。这样类模板实例化的类,虽然只支持注册和调用参数固定调用体,但具有编译期类型检查的好处,Register和UnRegister均很容易实现。用户需要时,根据需要实例化模板即可。

template <typename AbstractProduct ,typename IdentifierType ,typename... Arg  > class Factory
{

public:
    bool Register(const IdentifierType& id, std::function<unique_ptr<AbstractProduct> (Arg...)> creator)
    {
        return associations_.insert(typename AssocMap::value_type(id,creator)).second;
    }
    bool UnRegister(const IdentifierType& id)
    {
        return associations_.erase(id)==1;
    }
    unique_ptr<AbstractProduct> Createobject(const IdentifierType& id,Arg&&... args)
    {
        typename AssocMap::const_iterator i =associations_.find(id);
        if(i != associations_.end())
        {
            return (i->second)(std::forward<Arg>(args)...);
        }
        assert(false);
    }

private:
    typedef std::unordered_map<IdentifierType,std::function<unique_ptr<AbstractProduct> (Arg...)> > AssocMap;
    AssocMap associations_;

};

需要注意一下,这代码因为使用变长模板参数,所以无法使用默认模板参数。

 

使用第一种实现方式,你只需要一个对象,即可搞定所有需求(UnRegister比较闹心)。具有运行期类型检查。

使用第二种实现方式,你要在不同场合下,实例化出类,并且创建出对象只支持注册某种调用格式调用体。而且具有编译期类型检查。

 

虽然在第一种方式上,下了很多功夫,但是我还是不推荐这种做法,除非真的有万不得已需要,非要把代码设计成这样。

春节前,最后一篇文章,转载请标明出处,谢谢。

 

Object Factories(对象工厂)

标签:

原文地址:http://www.cnblogs.com/tangzhenqiang/p/4288623.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!