玩技术,Geeker
一个原创技术文章分享网站

C++设计模式——工厂方法模式

问题描述

之前讲到了C++设计模式——简单工厂模式,由于简单工厂模式的局限性,比如:工厂现在能生产ProductA、ProductB和ProductC三种产品了,此时,需要增加生产ProductD产品;那么,首先是不是需要在产品枚举类型中添加新的产品类型标识,然后,修改Factory类中的switch结构代码。是的,这种对代码的修改,对原有代码的改动量较大,易产生编码上的错误(虽然很简单,如果工程大了,出错也是在所难免的!!!)。这种对代码的修改是最原始,最野蛮的修改,本质上不能称之为对代码的扩展。同时,由于对已经存在的函数进行了修改,那么以前进行过的测试,都将是无效的,所有的测试,都将需要重新进行,所有的代码都需要进行重新覆盖。这种,增加成本,不能提高效率的事情,在公司是绝对不允许的(除非昏庸的PM)。出于种种原因,简单工厂模式,在实际项目中使用的较少。那么该怎么办?怎么办呢?需要对原有代码影响降到最小,同时能对原有功能进行扩展。

UML类图

那么今天介绍的工厂方法模式,就隆重登场了。它只是对简单工厂模式的扩展,在GOF的介绍中,它们是合并在一起的,而我则是单独分开进行讲解的,就是为了区分二者的利弊,便于大家在实际项目中进行更好的把握与应用。工厂方法模式是在简单工厂模式的基础上,对“工厂”添加了一个抽象层。将工厂共同的动作抽象出来,作为抽象类,而具体的行为由子类本身去实现,让子类去决定生产什么样的产品。

果冻想 | 一个原创文章分享网站

如图,FactoryA专心负责生产ProductA,FactoryB专心负责生产ProductB,FactoryA和FactoryB之间没有关系;如果到了后期,如果需要生产ProductC时,我们则可以创建一个FactoryC工厂类,该类专心负责生产ProductC类产品。由于FactoryA、FactoryB和FactoryC之间没有关系,当加入FactoryC加入时,对FactoryA和FactoryB的工作没有产生任何影响,那么对代码进行测试时,只需要单独对FactoryC和ProductC进行单元测试,而FactoryA和FactoryB则不用进行测试,则可省去大量无趣无味的测试工作。

适用场合

工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

  1. 在设计的初期,就考虑到产品在后期会进行扩展的情况下,可以使用工厂方法模式;
  2. 产品结构较复杂的情况下,可以使用工厂方法模式;

由于使用设计模式是在详细设计时,就需要进行定夺的,所以,需要权衡多方面的因素,而不能为了使用设计模式而使用设计模式。

代码实现

/*
** FileName     : FactoryMethodPatternDemo
** Author       : Jelly Young
** Date         : 2013/11/18
** Description  : More information, please go to http://www.jellythink.com
*/

#include <iostream>
using namespace std;

class Product
{
public:
	virtual void Show() = 0;
};

class ProductA : public Product
{
public:
	void Show()
	{
		cout<< "I'm ProductA"<<endl;
	}
};

class ProductB : public Product
{
public:
	void Show()
	{
		cout<< "I'm ProductB"<<endl;
	}
};

class Factory
{
public:
	virtual Product *CreateProduct() = 0;
};

class FactoryA : public Factory
{
public:
	Product *CreateProduct()
	{
		return new ProductA ();
	}
};

class FactoryB : public Factory
{
public:
	Product *CreateProduct()
	{
		return new ProductB ();
	}
};

int main(int argc , char *argv [])
{
	Factory *factoryA = new FactoryA ();
	Product *productA = factoryA->CreateProduct();
	productA->Show();

	Factory *factoryB = new FactoryB ();
	Product *productB = factoryB->CreateProduct();
	productB->Show();

	if (factoryA != NULL)
	{
		delete factoryA;
		factoryA = NULL;
	}

	if (productA != NULL)
	{
		delete productA;
		productA = NULL;
	}

	if (factoryB != NULL)
	{
		delete factoryB;
		factoryB = NULL;
	}

	if (productB != NULL)
	{
		delete productB;
		productB = NULL;
	}
	return 0;
}

2013年11月18日 于大连、东软。

未经允许不得转载:果冻想 » C++设计模式——工厂方法模式

分享到:更多 ()

评论 58

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #0

    想问下这里在最后删除的时候对变量进行了NULL的判断,跟第一节课的不太一样,不明白这里有什么技巧或者习惯什么的吗?

    msccreater3年前 (2013-12-23)回复
    • 这是实际项目开发中一种比较严谨的做法,为了防止对已经释放过的内存再次进行释放而造成的APP Crash,比如:
      if (productB != NULL)
      {
      delete productB;
      productB = NULL;
      }
      这种一种比较好的编程方式;我们都知道对于指针,在实际开发中掌控它是比较麻烦的,为了减少不必要的麻烦,所以文章中的所有对内存的释放都进行了判断,同时,在每次初始化指针时,也是对应的将其初始化为NULL,释放之后,也将指针初始化为NULL,如:productB = NULL。这是一种编程风格,也是一种编程习惯。

      Jelly3年前 (2013-12-24)回复
  2. #0

    写得不错!支持楼主,希望持续更新!~

    cugbin3年前 (2013-12-26)回复
    • 谢谢你的关注,设计模式会持续更新的。希望有机会能和你一起探讨设计模式。

      果冻想3年前 (2013-12-26)回复
      • 嗯嗯,初学设计模式,会跟随着你学习!

        cugbin3年前 (2013-12-27)回复
        • 希望你也能和大家一起分享你的学习心得,分享你对每个设计模式的理解;就像一千个读者,就会有一千个哈姆雷特一样;每个人对设计模式的理解都是不一样的。我坚信,分享使人进步。

          果冻想3年前 (2013-12-27)回复
  3. #0

    从简单工厂模式到工厂模式,如果一个工厂对一个生产类,有种不如直接new生产类的感觉。。。虽然不用改原来的函数,但是,给我感觉有点不如简单工厂模式那么整洁。···· 我为什么会有这种想法。

    益可达3年前 (2014-04-05)回复
    • 谢谢你的回复。不管是哪种工厂模式,都有一个好处就是对象的创建过程被集中进行了管理,也就是说便于对象的创建,从这一点出发,希望你能考虑到。如果还有什么问题,希望你能继续和我讨论。

      果冻想3年前 (2014-04-07)回复
  4. #0

    原来是这样!工厂方法模式相对简单工厂模式来说,优点在于方便对象的扩展。也就是说简单工厂添加产品的时候,要去修改大家公共的东西。而方法工厂的话,添加新产品,只需要自己添加自己的,不需要改动到公共的部分。我是这样理解的。。。。不过按代码量来算,简单工厂写的是比工厂方法少。

    林翔进3年前 (2014-04-17)回复
    • 嗯。简单工厂方法使用if…else…或者switch…case,按照不同的分支,创建不同的对象;这样在以后添加新的产品的时候,就需要添加对应的分支;而工厂方法则不必,只用添加新的产品类即可,这样就出现了另一个问题,当产品很多时,对应的产品类的管理也会是个麻烦的事情。这个你考虑到了么???对于这方面,希望能有更多的机会和你交流。

      果冻想3年前 (2014-04-18)回复
    • 严重同意。

      qblace2年前 (2015-05-30)回复
    • 分析的很棒

      josan9个月前 (07-15)回复
  5. #0

    博主好,如果是需要根据用户的输入来选泽创建哪种producer,那么还是需要 if else if 这些的吧,这些代码,不就需要写到客户端(调用的地方)那里吗?这还是多多少少不符合开闭原则的?

    zgl3年前 (2014-05-30)回复
  6. #0

    博主写的很好啊~!学设计模式就看你的博客了。

    理性IT男3年前 (2014-08-26)回复
  7. #0

    每天学一个设计模式 多谢博主分享!

    0统神02年前 (2014-10-19)回复
    • 谢谢你的支持!你也可以提出你的见解和我分享。

      果冻想2年前 (2014-10-19)回复
  8. #0

    写的真清晰,谢谢分享

    You Belong With Me2年前 (2014-11-16)回复
    • 楼主,看了后能够清晰的了解意思,但是还是在何处该用什么模式的时候有点模糊,这该怎么解决

      You Belong With Me2年前 (2014-11-16)回复
      • 多阅读别人的源码,看看人家是怎么使用的设计模式。然后多总结。学习都是有这么个过程的。

        果冻想2年前 (2014-11-17)回复
        • 有没有优秀的C++源码推荐?

          HI2年前 (2015-05-25)回复
  9. #0

    有内存泄露,作为基类的析构函数必须是虚析构函数

    112年前 (2014-12-15)回复
    • 恩。这个知道的。上面的代码只是Demo,上面的代码应该没有内存泄露吧。

      果冻想2年前 (2014-12-15)回复
  10. #0

    从demo代码上看就是多了一层,反而增加了复杂性?

    zhouzhangjin2年前 (2015-01-18)回复
    • 增加了复杂性?这个怎么理解?倒是类的个数增加了。类的维护起来就会增加难度。

      果冻想2年前 (2015-01-18)回复
  11. #0

    是不是可以把delete 设计到各自类的析构函数里,main里面就不用考虑释放的问题了

    Codeser2年前 (2015-01-18)回复
  12. #0

    作为一名毕业的东软人,看到这样的博文,好兴奋,不知作者是学生还是老师

    pro_xk2年前 (2015-01-22)回复
  13. #0

    delete之前判断指针是否为空是无意义的

    foo2年前 (2015-01-30)回复
    • 那如果出现多次delete 怎么办?

      hou2年前 (2015-04-22)回复
      • delete NULL; 这条语句不会产生任何副作用,可以认为是一条空语句。

        熊家继2年前 (2015-05-10)回复
      • 是无意义的,delete会判断指针是否为空的

        bo2年前 (2015-05-26)回复
  14. #0

    博主绘UML图,用的什么工具呢

    爱求索2年前 (2015-04-16)回复
  15. #0

    請教博主,若這依照這模式的觀念配合 C++ 的 Template 機制,可以只寫出 Factory 類即可,若有需要再個別特例化。請問這樣做跟文章中的 Demo 功能上相比是否有差異存在?

    hao2年前 (2015-04-20)回复
    • 文章中的Demo只是设计模式的雏形,一个简单的框架,结合Template,需要具体情况具体分析,如果涉及到有不同个参数的构造函数时,Template就不会那么灵活了。至于使用Template机制,也见过使用的,个人对Template无好感,如果外加上特例化,可能把问题更复杂化了。

      果冻想2年前 (2015-04-20)回复
  16. #0

    感觉看你的文章学习设计模式比较易学,但是在使用场景上还是有比较模糊。博主是否有实际应用的开源代码分享呢?

    浩yan2年前 (2015-05-25)回复
    • 有很多的设计模式已经在实际的工作中使用了,有实际的使用案例,有的文章中也把我使用的一些例子写进去了。也准备也一些开源代码使用设计模式的例子,但是,时间是真心不够。

      果冻想2年前 (2015-05-25)回复
      • 大师加油啊

        qblace2年前 (2015-05-30)回复
        • 叫我大湿?使不得,使不得,但是加油一定要的。

          果冻想2年前 (2015-05-31)回复
  17. #0

    楼主的博客真的挺好的,正在学习。有一个想法,对工厂增加一个抽象层Factory,就是为了在客户端编码时尽量利用抽象层进行逻辑编码,最后用子类实体替换为具体实现,这样会有利于扩展。可以这样理解吗?将子类实体作为参数输入即可void display(Factory& FactoryObj){ FactoryObj.CreateProduct()->Show();}

    fengniumaxuwei2年前 (2015-06-04)回复
  18. #0

    楼主的博客真的挺好的,正在学习。有一个想法,对工厂增加一个抽象层Factory,就是为了在客户端编码时尽量利用抽象层进行逻辑编码,最后用子类实体替换为具体实现,这样会有利于扩展。可以这样理解吗?将子类实体作为参数输入即可void display(Factory& FactoryObj){FactoryObj.CreateProduct()->Show();}

    fengniumaxuwei2年前 (2015-06-04)回复
  19. #0

    你好,果冻!
    我想问下,现实开发中是不是使用模板配合static成员函数更简单点.类似这样
    template<typename T>
    class Factory
    {
    public:
    static T* create(){
    return new T();
    }

    };

    ashui2年前 (2015-08-19)回复
    • 请参见其他网友的评论,也有别的网友问了同样的问题。

      果冻想2年前 (2015-08-20)回复
  20. #0

    搜索设计模式看到了你的文章,顺便看了其他的文章,感觉博主有很强的学习规划,学习了,持续关注,希望有更高质量的文章,我目前主要做C++

    me_huhu1年前 (2015-11-06)回复
    • 目前还在基础学习,高质量的博客好难写,好费时间,好费脑细胞。谢谢支持。

      果冻想1年前 (2015-11-06)回复
  21. #0

    讲的清晰易懂,非常棒

    milton1年前 (2016-02-18)回复
  22. #0

    这个工厂只负责产品的生产,却没有产品的销毁。感觉可以在factory建个纯虚析构函数;之后在FactorA以及FtorB各建一个相应的虚析构函数

    josan9个月前 (07-15)回复
    • 恩。工厂只负责生产产品,不负责销毁。

      果冻想8个月前 (07-19)回复
  23. #0

    #include
    using namespace std;

    class Product
    {
    public:
    virtual void Show() = 0;
    };

    class ProductA : public Product
    {
    public:
    void Show()
    {
    cout << "I'm ProductA" << endl;
    }
    };

    class ProductB : public Product
    {
    public:
    void Show()
    {
    cout << "I'm ProductB" << endl;
    }
    };

    class Factory
    {
    public:
    virtual Product *CreateProduct() = 0;
    virtual void DestroyProduct(Product *) = 0;
    };

    class FactoryA : public Factory
    {
    public:
    Product *CreateProduct()
    {
    return new ProductA();
    }
    void DestroyProduct(Product *a)
    {
    cout << "ProdcutA is deleted.\n";
    delete a;
    }
    };

    class FactoryB : public Factory
    {
    public:
    Product *CreateProduct()
    {
    return new ProductB();
    }
    void DestroyProduct(Product *a)
    {
    cout <CreateProduct();
    productA->Show();

    Factory *factoryB = new FactoryB();
    Product *productB = factoryB->CreateProduct();
    productB->Show();

    if(factoryA != NULL)
    {
    factoryA->DestroyProduct(productA);
    delete factoryA;
    factoryA = NULL;
    }

    /*if(productA != NULL)
    {
    cout <DestroyProduct(productB);
    delete factoryB;
    factoryB = NULL;
    }

    //if(productB != NULL)
    //{
    // delete productB;
    // productB = NULL;
    //}

    cin.get();
    return 0;
    }

    不知道,我这个改进的怎么样?有没有什么需要改的,求讨论

    josan9个月前 (07-15)回复
    • 不好意思,回复晚了,看到了你很多的回复。我都一一进行了仔细的阅读。非常喜欢你这样的读者,能将自己的想法和大家分享,同时也非常感谢你提出的建议。非常感谢。

      果冻想8个月前 (07-19)回复
  24. #0

    这么写;我始终感觉不太好;感觉写得太初级了

    阿星8个月前 (07-27)回复
    • 恩,是有点初级,实际应用也是这样的啊。

      果冻想8个月前 (07-27)回复
  25. #0

    简单工厂模式 代码量略少,但违背开闭原则,并且稍微有点复杂
    工厂方法模式 代码量陡增 但逻辑清晰 测试量少

    vast7个月前 (08-17)回复

在这里玩技术,享受技术带来的疯狂

捐赠名单关于果冻