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

Object Factories(对象工厂)

时间:2015-02-13 16:42:37      阅读:121      评论:0      收藏:0      [点我收藏+]

标签:c++   工厂模式   模板   泛型编程   设计模式   

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.看看实现,这才是重点:(在我不断完善实现的过程中,我发现自己是为了模式而模式,所以以下代码仅供参考)

 
struct package
{
    void * funcSet;
    void * func;
    size_t index;
    string sig;
};
 
template <typename ...> class TypeList {};
 
template <typename AbstractProduct ,typename IdentifierType = string> class FactoryImpl
{
public:
    template <typename... Arg> bool Register(const IdentifierType& id,const function<unique_ptr<AbstractProduct>(Arg...)>& creator)
    {
 
        static vector<function<unique_ptr<AbstractProduct>(Arg...)>> vf;#17
        typename AssocMap::const_iterator i =associations_.find(id);
        if(i!= associations_.end()) return false;
        vf.push_back(creator);
        return associations_.insert(typename AssocMap::value_type(id,package {&vf,&vf.back(),vf.size()-1,string(typeid(TypeList<Arg...>).name())})).second;
 
    }
 
    template <typename ... Arg >
    bool UnRegister(const IdentifierType& id)
    {
        typename AssocMap::const_iterator i =associations_.find(id);
        if(i != associations_.end())
        {
            assert(
                ((i->second).sig).compare(typeid(TypeList<Arg...>).name())==0
            );
            auto vf=static_cast<vector<function<unique_ptr<AbstractProduct>(Arg...)>>*>((i->second).funcSet);
            vf->erase(vf->begin()+(i->second).index);
        }
 
        return associations_.erase(id)==1;
    }
 
    template <typename... Arg> unique_ptr<AbstractProduct> Createobject(const IdentifierType& id,Arg&&... args)
    {
        typename AssocMap::const_iterator i =associations_.find(id);
 
        if(i != associations_.end())
        {
            assert(((i->second).sig).compare(typeid(TypeList<Arg...>).name())==0);
            auto funp=static_cast<function<unique_ptr<AbstractProduct>(Arg...)>* >((i->second).func);
            return (*funp)(std::forward<Arg>(args)...);
        }
        assert(false);
    }
 
 
private:
    typedef std::unordered_map<IdentifierType,package> AssocMap;
    AssocMap associations_;
 
};

代码释疑:

     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(对象工厂)

标签:c++   工厂模式   模板   泛型编程   设计模式   

原文地址:http://blog.csdn.net/zjq2008wd/article/details/43794471

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