自注册工厂模式

C++自注册工厂

假设我们现在需要实现一个绘制类,不同的图形有不同的绘制方式,我们可以大致写出以下代码:

1.普通的工厂模式,大家想必非常熟悉,伪代码如下:

class Sharp
{
public:
    virtual ~Sharp()
    {
    }

    virtual void paint() = 0;
};

//圆形
class Circle : public Sharp
{
public:
    virtual void paint() override
    {
        qDebug() << "paint Circle";
    }
};

//矩形
class Rectangle : public Sharp
{
public:
    virtual void paint() override
    {
        qDebug() << "paint Rectangle";
    }
};

class SharpFactory
{
public:
    static SharpFactory* getInstance()
    {
        static SharpFactory ins;
        return &ins;
    }

    void registerSharp(int type, Sharp* ins)
    {
        if (!m_sharpMap.contains(type))
        {
            m_sharpMap.insert(type, ins);
        }
    }

    Sharp* getSharp(int type)
    {
        return m_sharpMap.value(type, nullptr);
    }

private:
    QHash<int, Sharp*> m_sharpMap;
};

这是一个最简单的工厂模式,我们要如何使用这个工厂模式呢? 如下:

	//1.先创建图形类对象
    Sharp* clrcle = new Circle();
    Sharp* rect = new Rectangle();
//    Sharp* customSharp = new customSharp();
    
	//2.将类对象注册到工厂中去
    SharpFactory::getInstance()->registerSharp(0, clrcle);
    SharpFactory::getInstance()->registerSharp(1, rect);

	//3.使用时,根据类型到工厂中取具体的对象
    Sharp* ins = SharpFactory::getInstance()->getSharp(0);
    if (ins != nullptr)
    {
        ins->paint();
    }

以上就是这个工厂模式的使用方式,我们先思考这个工厂有什么特点:

  1. 新增一种图形时,只需要新增一个类,继承自Sharp类,并重新实现virtual void paint() override;函数即可,无需对现有代码做任何改动
  2. 新增一种图形(如椭圆,ellipse)时,需要在程序中的某处,手动 Sharp* ell = new ellipse();,并需要调用SharpFactory::getInstance()->registerSharp(2, ell);将其注册到工厂中去; 如果我忘记了写这两行代码,或者我仅仅只构造了椭圆对象,但是忘了将其注册到工厂中去,那么在我想获取椭圆对象时,就无法通过工厂类的Sharp* ell = SharpFactory::getInstance()->getSharp(2);获取到椭圆类对象
  3. 我在程序中某处手动 new 出来的这些 Sharp类对象,需要在何处进行释放?
    1.遵循谁创建就由谁释放的原则;由程序员取手动管理
    2.让工厂类取统一管理,在工厂类的析构函数中全部释放,譬如:
~SharpFactory()
{
   for (QHash<int, Sharp*>::const_iterator iter = m_sharpMap.begin(); iter != m_sharpMap.end(); ++iter)
   {
   	Sharp* item = iter.value();
   	if (item != nullptr)
   	{
   		delete item;
   		item = nullptr;
   	}
   }
}

在这里,我个人认为使用第二种方式,将这些对象的生命周期全部交给工厂类去管理,会更合适
这里一定会有同学说,既然是我创建的对象,那么应该由我释放,为什么要交给工厂类去释放呢?

针对这个问题,我们思考一下,如果以上代码中的Shape类和SharpFactory工厂类是由框架的开发者写的,而具体的图形类,是由应用程序的开发者去写,框架的开发者更希望应用程序的开发者只关心自己的业务逻辑,而不需要去关心其他的因素,其他的处理框架可以帮应用程序的开发者处理好,这时候,我们希望应用程序的开发者将自己继承Sharp类写的具体图形类对象,注册到工厂中去,而又可以将new的细节隐藏,这样的话,对于应用程序开发者来说,他没有去手动new这个对象,自然他不会去释放这个对象

那我们如何将new的细节隐藏起来,不需要应用程序开发者手动创建呢?
其实我们可以通过模板类,参考一下代码:

template<class CustomSharp>
class SharpRegister
{
public:
    SharpRegister(int type)
    {
    //        std::function<Sharp* ()> func = []() -> Sharp* { return new CustomSharp;};
        SharpFactory::getInstance()->registerSharp(type, []() -> Sharp* { return new CustomSharp;}());
    }
};

我们使用一个模板类,这个类的构造函数中调用 new操作符来创建一个Sharp类型的指针,并注册到工厂类中,在使用时候,我们仅需要创建SharpRegister类的对象即可,比如我们可以使用一下代码来代替上面的创建类对象:

    SharpRegister<Circle> circleSharpRegister(0);
    SharpRegister<Rectangle> rectSharpRegister(1);

当然,这两个模板类对象(circleSharpRegister, rectSharpRegister),在我们的程序中,除了将CircleRectangle这两个类对象注册到工厂中,就没有任何其它作用,所以我们可以更一步简化注册的流程,我们甚至可以用宏定义来实现注册,例如:

#define CREATE_SHARP_NAME(T) reg_sharp_##T##_       //生成变量名
#define REGISTER_SHARP(type, T) static SharpRegister<T> CREATE_SHARP_NAME(T)(type);

REGISTER_SHARP宏 是用来创建一个静态的 SharpRegister模板类对象,我们为了让这些静态类对象的对象名不重复,所以才有 CREATE_SHARP_NAME宏,用来生成对象名
这样改造之后,如果新增一个椭圆类ellipse,应用程序的开发者仅需要在ellipse类的实现文件中写上一行:
REGISTER_SHARP(3, ellipse); 即可,就能实现将ellipse类对象注册到工厂中


提一嘴

当前这个工厂中,存储的是sharp类的子类对象,每次根据type获取时,拿到的都是同一个对象,如果我希望每次根据type都能拿到一个全新的sharp子类对象,只需要在注册时,将创建类对象的函数放到容器中即可,而不是直接将创建好的类对象放入到容器中,使用时,

//工厂类容器修改
QHash<int, std::function<Sharp* ()>> m_sharpMap;

//获取一个全新的类对象
Sharp* getSharp(int type)
{
	std::function<Sharp* ()> func = m_sharpMap.value(type, nullptr);
	if (func != nullptr)
	{
		return func();
	}
    return nullptr;
}

//注册模板类
template<class CustomSharp>
class SharpRegister
{
public:
    SharpRegister(int type)
    {
        std::function<Sharp* ()> func = []() -> Sharp* { return new CustomSharp;};
        SharpFactory::getInstance()->registerSharp(type, func);
    }
};

思考:

经过以上的改造,我们这个工厂可以通过宏定义来实现自动注册,看似已经非常完美,但是这里其实还有几个缺点

  1. 使用REGISTER_SHARP会创建静态对象,静态对象的初始在在main函数之前,这样也许会增加程序启动的时长
  2. 如果我希望仅有一个Sharp类,有很多种画法(函数),而不是希望扩展画法时候,是通过新增类来实现,比如:
class Sharp
{
public:
   virtual ~Sharp()
   {
   }

   virtual void paintCircle()
   {
       qDebug() << "paint clrcle";
   }

   virtual void paintRect()
   {
       qDebug() << "paint rect";
   }

   virtual void paintEllipse()
   {
       
   }
};

这种时候我们需要将工厂稍微修改一下,工厂类的容器中存储的就不再是Sharp*类型,而应该是函数指针类型,指向绘制函数,我们稍微将这个工厂类和注册类改造一下:

#define CREATE_SHARP_NAME(T) reg_sharp_##T##_       //生成变量名
#define REGISTER_SHARP(type, func) static SharpRegister<T> CREATE_SHARP_NAME(type) (type, func);

using PaintFunc = std::function<void ()>; 
class SharpFactory
{
public:
   static SharpFactory* getInstance()
   {
       static ClassFactory ins;
       return &ins;
   }

   void registerPaintFunc(int type, PaintFunc paintFunc)
   {
       if (!m_sharpMap.contains(type))
       {
           m_sharpMap.insert(type, paintFunc);
       }
   }

   PaintFunc getPaintFunc(int type)
   {
       return m_sharpMap.value(type, nullptr);
   }

private:
   QHash<int, PaintFunc> m_sharpMap;
};

class SharpRegister
{
public:
   SharpRegister(int typ, PaintFunc func)
   {
       SharpFactory::getInstance()->registerPaintFunc(type, func);
   }
};

可以看到,注册类不再是模板类;在使用处,需要构造Sharp或其子类的对象 m_sharp,并且需要这个宏,将类对象的函数指针注册到工厂中去,并且m_sharp对象的名称周期应该由构造者进行管理

       REGISTER_SHARP(0, std::bind(&Sharp::paintCircle, m_sharp));
       REGISTER_SHARP(1, std::bind(&Sharp::paintRect, m_sharp));
       REGISTER_SHARP(2, std::bind(&Sharp::paintEllipse, m_sharp));
  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值