工厂模式

工厂模式

概念

工厂模式是创建型模式中的一种,其目的是:隐藏各个对象创建的具体实现细节,仅通过抽象接口进行模块之间的调用;

工厂模式的优点是:隔离了两个模块之间的源码依赖关系,防止了两个模块越过自己的边界而侵入对方,从而两个模块可以独立开发、独立编译、独立部署,互不影响;

若整个软件代码能够确定永远是单体程序且不关心编译时间,那么工厂模式的作用并不会特别明显,

工厂模式的实现关键是,创建纯虚接口,并通过多态,将创建对象的工作延迟到子类执行,由子类确定实例化的对象;

工厂模式大致有:简单工厂、工厂方法、抽象工厂三种,现分述如下;

简单工厂

简单工厂模式,顾名思义,是工厂模式中较为简单的一个,没有其他多余的东西,朴素地展示了工厂模式中最精髓的观点:隐藏类创建实现细节,实现类使用者与提供者的源码隔离;

为了说明简单工厂模式,使用生产自行车的场景来说明,自行车有美利达、捷安特与喜德盛三个产品类,使用工厂类来创建这三种产品,示例代码如下所示;

产品类代码由:产品纯虚接口类、继承了纯虚接口的三种品牌的自行车产品类所组成,具体如下:

//  ----------------------------------------------------------------------------
/**
 * @file  Bicycle.h
 */
class Bicycle {
public:
	virtual ~Bicycle() {}
	virtual void show() = 0;
};


//  ----------------------------------------------------------------------------
/**
 * @file  MeridaBicycle.h 
 */
#include "Bicycle.h"
class MeridaBicycle : public Bicycle {
public:
	void show() { std::cout << "product : MeridaBicycle." << endl; }
};


//  ----------------------------------------------------------------------------
/**
 * @file  GiantBicycle.h 
 */
#include "Bicycle.h"
class GiantBicycle : public Bicycle {
public:
	void show() { std::cout << "product : GiantBicycle." << std::endl; }
};
 

//  ----------------------------------------------------------------------------
/**
 * @file  XdsBicycle.h 
 */
#include "Bicycle.h"
class XdsBicycle : public Bicycle {
public:
	void show() { std::cout << "product : XdsBicycle." << std::endl; }
};

工厂类代码的头文件中是不包含产品类的代码的,只有实现文件中才包含产品类代码,具体如下:

//  ----------------------------------------------------------------------------
/**
 * @file  factory.h
 */
class Bicycle;

enum ProductType {
    Merida,
    Giant,
    Xds
};

class Factory {
public:
	Bicycle * createBicycle(const ProductType type);
};


//  ----------------------------------------------------------------------------
/**
 * @file  factory.cpp
 */
#include "factory.h"
#include "MeridaBicycle.h"
#include "GiantBicycle.h"
#include "XdsBicycle.h"
Bicycle* Factory::creatBicycle(const ProductType type)
{
    switch(type) {
	case Merida: 
		return new MeridaBicycle();
	case Giant:
		return new GiantBicyle();
	case Xds:
		return new XdsBicycle();
	default:
        return nullptr;
    }
}

使用产品类的代码中只需要包含工厂类的头文件即可,完全没有引入产品类的源码,具体如下:

//  ----------------------------------------------------------------------------
/**
 * @file  main.cpp
 */
#include "factory.h"
int main()
{
    Factory builder;
    Bicycle* pMerida = builder.createBicycle(Merida);
    Bicycle* pGiant = builder.createBicycle(Giant);
    Bicycle* pXds = builder.createBicycle(Xds);
    
    pMerida->show();
    pGiant->show();
    pXds->show();
    
    delete pMerida;
    delete pGiant;
    delete pXds;
    
    return 0;
}

通过上述代码可知,使用简单工厂模式后,调用者不会引入对于产品类的源码的依赖,完全不知道产品类的具体实现细节,只需使用工厂类的抽象接口即可,这样就实现了不同模块之间的源码依赖;

但是简单工厂模式有一个缺点,当需要增加新的产品类时,要修改工厂类的源码,这违背了开闭原则,而下述的工厂方法改进了这一点;

工厂方法

针对简单工厂的缺陷,可将工厂类改为纯虚接口,并添加继承于纯虚接口类的具体工厂类,让产品类的创建延迟到工厂子类中实现;

当增加新的产品类时,同时添加一个继承于纯虚工厂类的子工厂类来实现新产品类的创建,这样就不用修改原工厂类的源码,虽然增加了部分类定义的代码,但是换来了开闭原则,这还是值得的;

产品类的代码与简单工厂是一样的,示例代码如下:

//  ----------------------------------------------------------------------------
/**
 * @file  Bicycle.h
 */
class Bicycle {
public:
	virtual ~Bicycle() {}
	virtual void show() = 0;
};


//  ----------------------------------------------------------------------------
/**
 * @file  MeridaBicycle.h 
 */
#include "Bicycle.h"
class MeridaBicycle : public Bicycle {
public:
	void show() { std::cout << "product : MeridaBicycle." << endl; }
};


//  ----------------------------------------------------------------------------
/**
 * @file  GiantBicycle.h 
 */
#include "Bicycle.h"
class GiantBicycle : public Bicycle {
public:
	void show() { std::cout << "product : GiantBicycle." << std::endl; }
};
 

//  ----------------------------------------------------------------------------
/**
 * @file  XdsBicycle.h 
 */
#include "Bicycle.h"
class XdsBicycle : public Bicycle {
public:
	void show() { std::cout << "product : XdsBicycle." << std::endl; }
};

不同的是工厂类的实现代码,需要创建纯虚工厂类,并让工厂子类继承于它:

//  ----------------------------------------------------------------------------
/**
 * @file  Factory.h 
 */
class Bicycle;
class Factory {
public:
    virtual ~Factory() {}
    virtual Bicycle* createBicycle() = 0;
};


//  ----------------------------------------------------------------------------
/**
 * @file  MeridaFactory.h 
 */
#include "Factory.h"
class MeridaFactory : public Factory {
public:
	Bicycle * createBicycle() override;
};

//  ----------------------------------------------------------------------------
/**
 * @file  MeridaFactory.cpp
 */
#include "MeridaFactory.h"
Bicycle* MeridaFactory::createBicycle()
{
    return new MeridaBicycle();
}



//  ----------------------------------------------------------------------------
/**
 * @file  GiantFactory.h 
 */
#include "Factory.h"
class GiantFactory : public Factory {
public:
	Bicycle * createBicyle() override;
};

//  ----------------------------------------------------------------------------
/**
 * @file  GiantFactory.cpp
 */
#include "GiantFactory.h"
Bicycle* GiantFactory::createBicyle()
{
    return new GiantBicycle();
}


//  ----------------------------------------------------------------------------
/**
 * @file  XdsFactory.h 
 */
#include "Factory.h"
class XdsFactory : public Factory {
public:
	Bicycle * createBicyle() override;
};

//  ----------------------------------------------------------------------------
/**
 * @file  XdsFactory.cpp 
 */
#include "XdsFactory.h"
Bicycle* XdsFactory::createBicycle()
{
    return new XdsBicycle();
}

调用工厂类的代码如下:

//  ----------------------------------------------------------------------------
/**
 * @file  main.cpp 
 */
#include "MeridaFactory.h"
#include "GiantFactory.h"
#include "XdsFactory.h"
int main()
{
    MeridaFactory meridaBuilder;
    GiantFactory giantBuilder;
    XdsFactory xdsBuilder;
    
    Bicycle* pMerida = meridaBuilder.createBicycle();
    Bicycle* pGiant = giantBuilder.createBicycle();
    Bicycle* pXds = xdsBuilder.createBicycle();
    
    pMerida->show();
    pGiant->show();
    pXds->show();
    
    delete pMerida;
    delete pGiant;
    delete pXds;
    
    return 0;
}

从上述代码可看出,相对于简单工厂,当有新的产品时,无需修改抽象工厂类代码,只要添加继承于抽象工厂类的子类即可,遵循了开闭原则;

综上,工厂方法成功实现了两大功能:

  • 隐藏产品类创建细节,遵循了依赖倒置原则;
  • 不修改工厂类源码的情况下添加新产品类,遵循了开闭原则;

抽象工厂

有时在开发中还会遇到一个抽象工厂类需要创建多种有一定关联性的产品类的需求,由于多种产品类有一定关联性,所以放在一个抽象工厂类中创建更为合适;

这时只需要在工厂方法的基础上作简单的扩展,提供更多创建产品类的接口即可,即衍生版的工厂方法,称之为抽象工厂;

若无需创建多种对象,则无需使用抽象工厂,使用工厂方法即可;

仍以生产自行车的厂商为例,若这三个品牌要添加电动车生产线,则产品类的代码需要添加电动车抽象产品类和三个子类,而自行车产品类代码不变,具体如下:

//  ----------------------------------------------------------------------------
/**
 * @file  Bicycle.h
 */
class Bicycle {
public:
	virtual ~Bicycle() {}
	virtual void show() = 0;
};


//  ----------------------------------------------------------------------------
/**
 * @file  MeridaBicycle.h 
 */
#include "Bicycle.h"
class MeridaBicycle : public Bicycle {
public:
	void show() { std::cout << "product : MeridaBicycle." << endl; }
};


//  ----------------------------------------------------------------------------
/**
 * @file  GiantBicycle.h 
 */
#include "Bicycle.h"
class GiantBicycle : public Bicycle {
public:
	void show() { std::cout << "product : GiantBicycle." << std::endl; }
};


//  ----------------------------------------------------------------------------
/**
 * @file  XdsBicycle.h 
 */
#include "Bicycle.h"
class XdsBicycle : public Bicycle {
public:
	void show() { std::cout << "product : XdsBicycle." << std::endl; }
};

下述代码为新添加的电动车产品类代码:

//  ----------------------------------------------------------------------------
/**
 * @file  Ebike.h 
 */
class Ebike {
public:
	virtual ~Ebike() {}
	virtual void show() = 0;
};
 
//  ----------------------------------------------------------------------------
/**
 * @file  MeridaEbike.h 
 */
#include "Ebike.h"
class MeridaEbike : public Ebike {
public:
	void show() { std::cout << "product : MeridaEbike." << std::endl; }
};
 
//  ----------------------------------------------------------------------------
/**
 * @file  GiantEbike.h 
 */
#include "Ebike.h"
class GiantEbike : public Ebike {
public:
	void show() { std::cout << "product : GiantEbike." << std::endl; }
};
 
//  ----------------------------------------------------------------------------
/**
 * @file  XdsEbike.h 
 */
#include "Ebike.h"
class XdsEbike : public Ebike {
public:
	void show() { std::cout << "product : XdsEbike." << std::endl; }
};

抽象工厂类中需要添加创建电动车的接口,各个子类也要同步添加并负责实现创建电动车产品类;

//  ----------------------------------------------------------------------------
/**
 * @file  Factory.h 
 */
class Factory {
public:
    virtual Bicycle * createBicyle() = 0;
    virtual Ebike * createEbike() = 0;
    virtual ~Factory() {}
};



//  ----------------------------------------------------------------------------
/**
 * @file  MeridaFactory.h 
 */
#include "Factory.h"
class MeridaFactory : public Factory {
public:
    Bicycle * createBicyle() override;
    Ebike * createEbike() override;
};

//  ----------------------------------------------------------------------------
/**
 * @file  MeridaFactory.cpp
 */
#include "MeridaBicycle.h"
#include "MeridaEbike.h"
Bicycle* MeridaFactory::createBicyle()
{
    return new MeridaBicyle();
}

Ebike * MeridaFactory::createEbike()
{
    return new MeridaEbike();
}




//  ----------------------------------------------------------------------------
/**
 * @file  GiantFactory.h 
 */
#include "Factory.h"
class GiantFactory : public Factory {
public:
    Bicycle * createBicyle() override;
    Ebike * createEbike() override;
};

//  ----------------------------------------------------------------------------
/**
 * @file  GiantFactory.cpp
 */
#include "GiantBicycle.h"
#include "GiantEbike.h"
Bicycle* GiantFactory::createBicyle()
{
    return new GiantBicyle();
}

Ebike * GiantFactory::createEbike()
{
    return new GiantEbike();
}




//  ----------------------------------------------------------------------------
/**
 * @file  XdsFactory.h 
 */
#include "Factory.h"
class XdsFactory : public Factory {
public:
    Bicycle * createBicyle() override;
    Ebike * createEbike() override;
};

//  ----------------------------------------------------------------------------
/**
 * @file  XdsFactory.cpp
 */
#include "XdsBicycle.h"
#include "XdsEbike.h"
Bicycle* XdsFactory::createBicyle()
{
    return new XdsBicyle();
}

Ebike * XdsFactory::createEbike()
{
    return new XdsEbike();
}

调用代码如下:

//  ----------------------------------------------------------------------------
/**
 * @file  main.cpp 
 */
#include "MeridaFactory.h"
#include "GiantFactory.h"
#include "XdsFactory.h"
int main()
{
    MeridaFactory meridaBuilder;
    Bicycle* pMeridaBicyle = meridaBuilder.createBicyle();
    Ebike* pMeridaEbike = meridaBuilder.createEbike();
    pMeridaBicyle->show();
    pMeridaEbike->show();
    delete pMeridaBicyle;
    delete pMeridaEbike;
    
    GiantFactory giantBuilder;
    Bicycle* pGiantBicyle = giantBuilder.createBicyle();
    Ebike* pGiantEbike = giantBuilder.createEbike();
    pGiantBicyle->show();
    pGiantEbike->show();
    delete pGiantBicyle;
    delete pGiantEbike;
    
    XdsFactory xdsBuilder;
    Bicycle* pXdsBicyle = xdsBuilder.createBicyle();
    Ebike* pXdsEbike = xdsBuilder.createEbike();
    pXdsBicyle->show();
    pXdsEbike->show();
    delete pXdsBicyle;
    delete pXdsEbike;
    
    return 0;
}

关于资源回收

工厂模式重点关注了类对象创建的问题,而关于类对象销毁的收尾工作并没有提及;

首先,考虑将对象的销毁放在对象的提供方--工厂类中。这样做有一个缺点,即当工厂类的变量生命周期结束时,产品类对象也随即销毁,而很多时候需要产品类对象的生命周期比工厂的生命周期长很多;同时由于“工厂模式”这样的字面含义就是“生产产品”,并没有销毁的意思;所以无论从需求上还是语义上,这样做都是不合理的;

接下来考虑将对象的销毁放在对象的使用方中。除非在工厂类接口的文档中进行详细说明,否则使用方只知道得到了一个对象的指针,并不了解其生命周期由谁管理,使用后贸然销毁可能会带来重复销毁的隐患;同时,另外一个问题是在具有一定规模的程序中常常会忘记销毁资源从而引起内存泄漏;所以这样做也是不合适的;

比较好的解决方法是使用智能指针 std::unique_ptr<T>,将资源的生命周期交由智能指针管理,这样也符合C++RAII的思想理念,同时也不用考虑资源由谁回收的问题了。具体做法是在工厂类中的对象创建函数返回一个智能指针,而不是一个裸指针,示例代码如下:

//  ----------------------------------------------------------------------------
/**
 * @file  Factory.h 
 */
#include <memory>
class Factory {
public:
    virtual std::unique_ptr<Bicycle> createBicyle() = 0;
    virtual ~Factory() {}
};


//  ----------------------------------------------------------------------------
/**
 * @file  MeridaFactory.h 
 */
#include "Factory.h"
class MeridaFactory : public Factory {
public:
    std::unique_ptr<Bicycle> createBicyle() override;
};

//  ----------------------------------------------------------------------------
/**
 * @file  MeridaFactory.cpp
 */
#include "MeridaBicycle.h"
std::unique_ptr<Bicycle> MeridaFactory::createBicyle()
{
    return std::make_unique<Bicycle>();
}

总结

工厂模式是不同模块之间传递资源的重要方式,其优点是资源的使用者无需了解资源提供者的实现细节,无源码依赖,只需遵循共同的抽象接口即可,这样两个模块就完成了解耦,相互独立,可以沿着各自方向变化;

简单工厂仅实现了最基本的源码隔离,遵循了依赖倒置原则;

工厂方法在简单工厂的基础上,还实现了当增加产品时仅扩展代码而不破坏代码的功能,遵循了开闭原则,

而抽象工厂仅在工厂方法的基础上拓展了创建产品的种类,其核心仍与工厂方法相同,无质的变化;

因此工厂方法是这三个工厂模式中最为重要的一个,其包含了两大核心观点:依赖倒置原则与开闭原则;其可作为工厂模式的典型代表在开发中多加理解与恰当应用;

参考


本文作者: 王同学