设计模式|工厂设计模式-Factory Method(C++语言描述)

概述
我们在谈设计模式的时候,需要结合具体的场景来谈。没有万能的设计模式可以适应每一个业务场景,我们只有结合实际业务场景,抓住场景中的「变」和「不变」的主体才能更好地运用设计模式来设计出优良的代码结构。
工厂设计模式属于让我们灵活创建对象的模式。用于应对这样的场景:通过一个类,在不改变它的源码的情况下可以通过它暴露的接口构造出我们想要的对象。
通过模拟一个小场景并逐步重构来理解工厂设计模式。
需求
我们模拟一下小米公司的产品发布。
需求:有一个主体(小米公司),可以通过它对外的接口不断发布我们想要的产品。
简单实现:

#ifndef _MI_PRIMARY_H_ #define _MI_PRIMARY_H_ #include using std::cout; using std::endl; enum MiProductTypes { Phone = 1, NoteBook }; class MiPhone{/*具体产品*/}; class MiNoteBook{/*具体产品*/}; class MiPrimary { public: MiPrimary(){} ~MiPrimary(){} void* productAnnounce(MiProductTypes type) { void * pRet = nullptr; switch (type) { case Phone: pRet = new MiPhone; cout << "MiPhone Announced!" << endl; break; case NoteBook: pRet = new MiNoteBook; cout << "MiNoteBook Announced!" << endl; break; default: break; }return pRet; } }; #endif

注意: 这里为了简便把各个产品与MiPrimary类写在一个文件了,实际上是分开在不同文件中的。这里的核心逻辑是定义枚举,根据传入的类型到对应分支创造对象。
我们如何使用这个类:
#include #include "MiPrimary.h" using std::cout; using std::endl; int main() { MiPrimary mi; mi.productAnnounce(Phone); return 0; }

我们先实例化一个MiPrimary对象,然后传入事先定义好的枚举类型,就可以得到对应的类。
缺陷: 缺陷是结合场景来讲的,这里的场景是小米公司后面还要扩展业务,目前只有手机、笔记本电脑这两种产品。后面可能还有其他的3C产品需要发布,那时候我们又需要在这里新增MiProductTypes的成员,以及在switch下面新增case 分支。
优势: 当然,优势就是代码简单。逻辑清晰易懂。
思考: 如果说我们的MiPrimary类写好了就不想再变动了,以后新增产品的时候也不用改MiPrimary代码就可以直接发布,那我们该怎么设计?
工厂设计模式
针对这个场景,前面设计的缺陷在于MiPrimary编译时依赖于具体的某个产品的实现。工厂设计模式通过抽象一个工厂基类,让MiPrimary依赖这个工厂基类,把new操作留给工厂基类,来避免对具体产品的依赖。
工厂设计模式要点:
  • 工厂基类
  • 产品基类
具体产品继承产品基类,还需要一个与具体工厂,继承自工厂基类,通过具体工厂new 具体产品。
UML关系如下用MiCompany类代替MiPrimary类,加以区分:
设计模式|工厂设计模式-Factory Method(C++语言描述)
文章图片

我们在设计系统的时候主要抓住系统的变与不变:在上面这个UML图中,变的部分我们用蓝色线图框出,不变的,稳定的部分我们用红色线图框出。
下面是具体的代码实现:
不变的主体类MiCompany:
//file name:MiCompany.h #ifndef _MICOMPANY_H_ #define _MICOMPANY_H_ #include #include "IFactory.h"class MiCompany { public: MiCompany() { m_pFactory = nullptr; } ~MiCompany(){} void setFactory(IFactory* pFactory) { m_pFactory = pFactory; }void productAnnounce()//若指针传出,内存交由外部处理 { if(m_pFactory) { m_pFactory->getProduct()->doSomeWork(); } }private: IFactory* m_pFactory; }; #endif

工厂基类IFactory:
//file name:IFactory.h #ifndef _IFACTORY_H_ #define _IFACTORY_H_ #include "IProduct.h"class IFactory { public: virtual IProduct* getProduct() = 0; }; #endif

产品基类IProduct:
//file name:IProduct.h #ifndef _PRODUCT_H_ #define _PRODUCT_H_ #include using std::cout; using std::endl; class IProduct { public: IProduct(){} virtual ~IProduct(){} virtual void doSomeWork() = 0; }; #endif

具体产品工厂MiPhoneFactory:
//file name:MiPhoneFactory.h #ifndef _MIPHONE_FACTORY_H_ #define _MIPHONE_FACTORY_H_ #include "IFactory.h" #include "MiPhone.h"class MiPhoneFactory : public IFactory { public: MiPhoneFactory() { m_pMiPhone = nullptr; } ~MiPhoneFactory() { delete m_pMiPhone; m_pMiPhone = nullptr; } IProduct* getProduct()//根据需求也可做成单例 { if(m_pMiPhone == nullptr) { m_pMiPhone = new MiPhone; } return m_pMiPhone; } private: MiPhone* m_pMiPhone; }; #endif

具体产品MiNoteBook类:
//file name:MiNoteBook.h #ifndef _MI_NOTEBOOK_H_ #define _MI_NOTEBOOK_H_ #include "IProduct.h"class MiNoteBook : public IProduct { public:MiNoteBook() { cout << "MiNoteBook Created!" <

具体产品工厂类MiNoteBookFactory.h:
//filename:MiNoteBookFactory.h #ifndef _MI_NOTEBOOK_FACTORY_H_ #define _MI_NOTEBOOK_FACTORY_H_ #include "IFactory.h" #include "MiNoteBook.h" class MiNoteBookFactory : public IFactory { public: MiNoteBookFactory() { m_pMiNoteBook = nullptr; } virtual IProduct* getProduct()//这里根据需要可以做成一个单例 { if(m_pMiNoteBook) { m_pMiNoteBook = new MiNoteBook; } return m_pMiNoteBook; }private: MiNoteBook* m_pMiNoteBook; }; #endif

使用案例:
//file name: main.cc #include using std::cout; using std::endl; #include "MiCompany.h" #include "MiPhoneFactory.h" #include "MiNoteBookFactory.h" int main() { MiCompany mi; MiPhoneFactory phoneFactory; mi.setFactory(&phoneFactory); mi.productAnnounce(); MiNoteBookFactory notebookFactory; mi.setFactory(¬ebookFactory); mi.productAnnounce(); return 0; }

总结: 【设计模式|工厂设计模式-Factory Method(C++语言描述)】我们在MiCompany确定的情况下,要增加新的产品发布的话,我们除了要新增产品以外,还要新增一个针对这个产品的工厂搭配使用,这样才符合目前这个工厂模式的框架。那还有没有更酷的解决方案呢?
通常来说,代码的骚操作越多,越难以理解。我们后续会继续改进这个设计。

    推荐阅读