设计模式-工厂模式

初识工厂模式

工厂模式定义:这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式、在工厂模式中,我们在创建对对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

简单来说,使用了c++多态的特性,将存在继承关系的类,通过一个工厂类创建对应的子类(派生类)对象。在项目复杂的情况下,可以便于子类对象的创建。

工厂模式的实现方式可分为简单工厂模式工厂方法模式抽象工厂模式,每个实现方式都存在优和劣。

简单工厂模式

具体的情况:

        鞋厂可以指定生成耐克、阿迪达斯和李宁牌子的鞋子。哪个鞋炒的火爆,老板就生产哪个,看形势生产。

UML图:

        设计模式-工厂模式_第1张图片

 简单工厂模式的结构组成:

  1. 工厂类(ShoesFactory):工厂模式的核心类,会定义一个用于创建指定的具体实例对象的接口。
  2. 抽象产品类(Shoes):是具体产品类的继承的父类或实现的接口。
  3. 具体产品类(NiKeShoes\AdidasShoes\LiNingShoes):工厂类所创建的对象就是此具有产品实例。

 简单工厂模式的特点:

  • 工厂类封装了创建具体产品对象的函数

 简单工厂模式的缺陷:

  •  扩展性非常差,新增产品的时候,需要去修改工厂类。

 简单工厂模式的代码:

  • Shoes为鞋子的抽象类(基类),接口函数为Show(),用于显示鞋子广告。
  • NiKeShoes、AdidasShoes、LiNingShoes为具体鞋子的类,分别是耐克、阿迪达斯和李宁鞋牌的鞋,它们都继承于Shoes抽象类
  • ShoesFactory为工厂类,类里实现根据鞋子类型创建对应鞋子产品对象CreateShoes(SHOES_TYPE type)函数
#include 

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

class NiKeShoes : public Shoes {
	void Show()
	{
		std::cout << "我是耐克,我的广告语:Just do it" << std::endl;
	}
};

class AdidasShoes : public Shoes {
	void Show()
	{
		std::cout << "我是阿迪达斯,我的广告语:Impossible is nothing" << std::endl;
	}
};

class LiNingShoes : public Shoes {
	void Show()
	{
		std::cout << "我是李宁,我的广告语:Everything is possible" << std::endl;
	}
};

enum SHOES_TYPE
{
	NIKE,
	LINING,
	ADIDAS
};

class ShoesFactory{
public:
	Shoes* CreateShoes(SHOES_TYPE type);
};

Shoes* ShoesFactory::CreateShoes(SHOES_TYPE type)
{
	switch (type)
	{
	case NIKE:
		return new NiKeShoes();
		break;
	case LINING:
		return new LiNingShoes();
		break;
	case ADIDAS:
		return new AdidasShoes();
		break;
	default:
		return NULL;
		break;
	}
}

int main()
{
	//构造工厂对象
	ShoesFactory  shoesFactory;

	//从鞋厂对象创建耐克对象
	Shoes* pNikeShoes = shoesFactory.CreateShoes(NIKE);
	if (pNikeShoes != NULL)
	{
		//耐克广告喊起
		pNikeShoes->Show();
		//释放资源
		delete pNikeShoes;
		pNikeShoes = NULL;
	}

	//从鞋厂对象创建李宁对象
	Shoes* pLiNingShoes = shoesFactory.CreateShoes(LINING);
	if (pLiNingShoes != NULL)
	{
		//李宁广告喊起
		pLiNingShoes->Show();
		//释放资源
		delete pLiNingShoes;
		pLiNingShoes = NULL;
	}

	//从鞋厂对象创建阿迪达斯对象
	Shoes* pAdidasShoes = shoesFactory.CreateShoes(ADIDAS);
	if (pAdidasShoes != NULL)
	{
		//阿迪达斯广告喊起
		pAdidasShoes->Show();
		//释放资源
		delete pAdidasShoes;
		pAdidasShoes = NULL;
	}

	return 0;
}

工厂方法模式

具体情形:

  • 现各类鞋子抄的非常火热,于是为了大量生产每种类型的鞋子,则要针对不同品牌的鞋子开设对立的生产线,那么每个生产线就只能生成同类型品牌的鞋。

UML图:

 设计模式-工厂模式_第2张图片

 工厂方法模式的结构组成:

  1. 抽象工厂类厂(ShoesFactory):工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。
  2. 具体工厂类(NiKeProducer\AdidasProducer\LiNingProducer):继承于抽象工厂,实现创建对应具体产品对象的方式。
  3. 抽象产品类(Shoes):它是具体产品继承的父类(基类)。
  4. 具体产品类(NiKeShoes\AdidasShoes\LiNingShoes):具体工厂创建的对象,就是此类。

 工厂方法模式的特点:

  • 工厂方法模式抽象出了工厂类,提供创建具体产品的接口,交由子类去实现。
  • 工厂方法模式的应用并不只是为了封装具体产品对象的创建,而是把具体产品对象的创建放到具体工厂类实现。

 工厂方法模式的缺陷:

  • 每新增一个产品,就需要增加一个对应的产品的具体工厂类。相比简单工厂模式而言,工厂方法模式需要更多的类定义。
  •  一条生产线只能一个产品

工厂方法模式的代码:

  • ShoesFactory抽象工厂类,提供了创建具体鞋子产品的纯虚函数。
  • NiKeProducer、AdidasProducer、LiNingProducer具体工厂类,继承持续工厂类,实现对应具体鞋子产品对象的创建。
  • main函数针对每种类型的鞋子,构造了每种类型的生产线,再由每个生成线生产对应的鞋子。需注意的是具体工厂对象和具体产品对象,用完了需要通过delete释放资源。
#include 

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

class NiKeShoes : public Shoes {
	void Show()
	{
		std::cout << "我是耐克,我的广告语:Just do it" << std::endl;
	}
};

class AdidasShoes : public Shoes {
	void Show()
	{
		std::cout << "我是阿迪达斯,我的广告语:Impossible is nothing" << std::endl;
	}
};

class LiNingShoes : public Shoes {
	void Show()
	{
		std::cout << "我是李宁,我的广告语:Everything is possible" << std::endl;
	}
};


class ShoesFactory {
public:
	virtual Shoes* CreateShoes() = 0;
	virtual ~ShoesFactory() {};
};
//耐克生成者/生产链
class NiKeProducer : public ShoesFactory{
public:
	Shoes* CreateShoes() {
		return new NiKeShoes();
	}
};
//阿迪达斯生成者/生产链
class AdidasProducer : public ShoesFactory {
public:
	Shoes* CreateShoes() {
		return new AdidasShoes();
	}
};
//李宁生成者/生产链
class LiNingProducer : public ShoesFactory {
public:
	Shoes* CreateShoes() {
		return new LiNingShoes();
	}
};

int main()
{
	// ==================== 生产耐克流程 ================= //
	//鞋厂开设耐克生产线
	ShoesFactory* niKeProducer = new NiKeProducer();
	//耐克生产线产出球鞋
	Shoes* nikeShoes = niKeProducer->CreateShoes();
	//耐克球鞋喊起
	nikeShoes->Show();
	//释放资源
	delete niKeProducer;
	delete nikeShoes;
	// ==================== 生产阿迪达斯流程 ================= //
	//鞋厂开设阿迪达斯生产线
	ShoesFactory* adidasProducer = new AdidasProducer();
	//阿迪达斯生产线产出球鞋
	Shoes* adidasShoes = adidasProducer->CreateShoes();
	//阿迪达斯球鞋喊起
	adidasShoes->Show();
	//释放资源
	delete adidasProducer;
	delete adidasShoes;
	// ==================== 生产李宁流程 ================= //
	//鞋厂开设李宁生产线
	ShoesFactory* liNingProducer = new LiNingProducer();
	//李宁生产线产出球鞋
	Shoes* liNingShoes = liNingProducer->CreateShoes();
	//李宁球鞋喊起
	liNingShoes->Show();
	//释放资源
	delete liNingProducer;
	delete liNingShoes;
	
	return 0;
}

 抽象工厂模式

具体情形:

  • 鞋厂为了扩大业务,不仅只生成鞋子,把运动品牌的衣服也一起生产了。

UML图:

设计模式-工厂模式_第3张图片

 抽象工厂模式的结构组成(和工厂方法模式一样)

  • 抽象工厂类厂(ShoesFactory): 工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现
  • 具体工厂类厂(NiKeProducer): 继承于抽象工厂,实现创建对应具体产品对象的方式。
  • 抽象产品类(Shoes\Clothe): 它是具体产品继承的父类(基类)。
  • 具体产品类(NiKeShoes\NiKeClothe):具体工厂创建的对象,就是此类。

抽象工厂模式的特点:

  • 提供一个接口,可以创建多个产品族中的产品对象。如创建耐克工厂,则可以创建耐克鞋子产品、衣服产品、裤子产品等。

 抽象工厂模式的缺点:

  • 同工厂方法模式一样,新增产品时,都需要增加一个对应的产品的具体工厂类。

 抽象工厂的代码:

  • Clothe和Shoes,分别为衣服和鞋子的抽象产品类。
  • NiKeClothe和NiKeShoes,分别是耐克衣服和耐克鞋子的具体产品类。

#include 

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

class NiKeShoes : public Shoes {
public:
	void Show()
	{
		std::cout << "我是耐克鞋,让你酷起来" << std::endl;
	}
};
class Clothe {
public:
	virtual ~Clothe() {};
	virtual void Show() = 0;
};

class NiKeClothe : public Clothe {
public:
	void Show()
	{
		std::cout << "我是耐克衣服,时尚我最在行!" << std::endl;
	}
};
//总厂
class Factory {
public:
	virtual Shoes* CreateShoes() = 0;
	virtual Clothe* CreateClothe() = 0;
	virtual ~Factory() {};
};
//耐克生成者/生产链
class NiKeProducer : public Factory {
public:
	Shoes* CreateShoes() {
		return new NiKeShoes();
	}

	Clothe* CreateClothe() {
		return new NiKeClothe();
	}
};

int main()
{
	// ==================== 生产耐克流程 ================= //
	//鞋厂开设耐克生产线
	NiKeProducer* niKeProducer = new NiKeProducer();
	//耐克生产线产出球鞋
	Shoes* nikeShoes = niKeProducer->CreateShoes();
	//耐克生产线产出衣服
	Clothe* nikeClothe = niKeProducer->CreateClothe();

	//耐克球鞋喊起
	nikeShoes->Show();
	//耐克衣服广告喊起
	nikeClothe->Show();
	//释放资源
	delete niKeProducer;
	delete nikeShoes;
	delete nikeClothe;

	return 0;
}

总结

以上三种工厂模式,在新增产品时,都存在一定的缺陷。

  • 简单工厂模式,需要去修改工厂类,这违背了开闭法则。
  • 工厂方法模式和抽象工厂模式,都需要增加一个对应的产品的具体工厂类,这就会增大了代码的编写量

那么有什么好的方法,在新增产品时,即不用修改工厂类,也不用新增具体的工厂类?

 模板工厂

针对工厂方法模式封装成模版工厂类,那么这样在新增产品时,是不需要新增具体的工厂类,减少了代码的编写量。

UML图:

设计模式-工厂模式_第4张图片

 模板工厂代码:

  • Shoes和Clothe,分别为鞋子和衣服的抽象类(基类)
  • NiKeShoes和UniqloClothe,分别为耐克鞋子和优衣库衣服具体产品类。
  • AbstractFactory为抽象工厂类,其中模板参数:AbstractProduct_t 产品抽象类,如Shoes、Clothe
  • ConcreateFactory为具体模板工厂类,其中模板参数:AbstractProduct_t产品抽象类(如Shoes、Clothe),Concreateproduct_t 产品具体类(如NiKeShoes、UniqloClothe)
  • main函数,根据不同类型的产品,构造对应的产品的工厂对象,便可通过对应产品的工厂对象创建具体的产品对象。

#include 
//基类鞋子
class Shoes {
public:
	virtual ~Shoes() {};
	virtual void Show() = 0;
};
//基类衣服
class Clothe {
public:
	virtual ~Clothe() {};
	virtual void Show() = 0;
};
//耐克鞋子
class NiKeShoes :public Shoes {
public:
	void Show() {
		std::cout << "我是耐克球鞋,我的广告语:Just do it" << std::endl;
	}
};
//耐克衣服
class UniqloClothe :public Clothe {
public:
	void Show() {
		std::cout << "我是优衣库衣服,我的广告语:I am Uniqlo" << std::endl;
	}
};

//抽象模版工厂类
//模板参数:AbstractProduct_t 产品抽象类
template 
class AbstractFactoy
{
public:
	virtual Abstractproduct_t* CreateProduct() = 0;
	virtual ~AbstractFactoy() {};
};
//具体模板工厂类
template 
class ConcreteFactory :public AbstractFactoy
{
public:
	AbstractProduct_t* CreateProduct()
	{
		return new ConcreteProduct_t();
	}
};

int main()
{
	//构建耐克鞋的工厂对象
	ConcreteFactory nikeFactory;
	//创建耐克鞋对象
	Shoes* pNiKeShoes = nikeFactory.CreateProduct();
	//打印耐克鞋广告语
	pNiKeShoes->Show();

	//构建优衣库的工厂对象
	ConcreteFactory uniqloFactory;
	//创建优衣库对象
	Clothe* pUniqloClothe = uniqloFactory.CreateProduct();
	//打印优衣库广告语
	pUniqloClothe->Show();

	delete pNiKeShoes;
	pNiKeShoes = NULL;

	delete pUniqloClothe;
	pUniqloClothe = NULL;

	return 0;
}

产品注册模版类+单例工厂模板类

前面的模板工厂虽然在新增产品的时候,不需要新增具体的工厂类,但是缺少一个可以统一随时随地获取指定的产品对象的类。

还有改进的空间,我们可以把产品注册的对象用std::map的方式保存,通过key-value的方式可以轻松简单的获取对应的产品对象实例。

实现的思路:

  • 把产品注册的功能封装成产品注册模板类。注册的产品对象保存在工厂模板类的std::map,便于产品对象的获取。
  • 把获取产品对象的功能封装成工厂模板类。为了能随时随地获取指定产品对象,则把工厂设计成单例模式。

UML图:

设计模式-工厂模式_第5张图片

产品注册模板类+单例工厂模板类

  • IProductRegistrar为产品注册抽象类 ,模板参数ProductType_t 表示的类是产品抽象类(如Shoes、Clothe)。提供了产品对象创建的纯虚函数CreateProduct。
  • ProductFactory 为工厂模板类,模板参数ProductType_t 表示的类是产品抽象类(如Shoes、Clothe)。用于保存注册产品对象到std::map中和获取对应的产品对象。
  • ProductRegistrar 为产品注册模板类,模板参数ProductType_t 表示的类是产品抽象类(如Shoes、Clothe),productImpl_t 表示的类是具体产品(如NikeShoes、UniqloClothe)。用于注册产品到工厂类和创建产品实例对象。
  • main 函数,通过ProductRegistrar注册各种不同类型产品,在统一由ProductFactory 单例工厂获取指定的产品对象。
#include 
#include 
//基类鞋子
class Shoes {
public:
	virtual ~Shoes() {};
	virtual void Show() = 0;
};
//基类衣服
class Clothe {
public:
	virtual ~Clothe() {};
	virtual void Show() = 0;
};
//耐克鞋子
class NiKeShoes :public Shoes {
public:
	void Show() {
		std::cout << "我是耐克球鞋,我的广告语:Just do it" << std::endl;
	}
};
//耐克衣服
class UniqloClothe :public Clothe {
public:
	void Show() {
		std::cout << "我是优衣库衣服,我的广告语:I am Uniqlo" << std::endl;
	}
};
//基类,产品注册模版接口类
//模板参数ProductType_t 表示的类是产品抽象类
template 
class IProductRegistrar
{
public:
	//获取产品对象抽象接口
	virtual ProductType_t* CreateProduct() = 0;
protected:
	//禁止外部构造和虚构,子类的内部的其他函数可以调用
	IProductRegistrar(){}
	virtual ~IProductRegistrar(){}
private:
	//禁止外部拷贝和赋值操作
	IProductRegistrar(const IProductRegistrar&);
	const IProductRegistrar& operator=(const IProductRegistrar&);
};

//工厂模板类,用于获取和注册产品对象
//模板参数ProductType_t 表示的类是产品抽象类
template
class ProductFactory
{
public:
	//获取工厂单例,工厂的实例是唯一的
	static ProductFactory& instance()
	{
		static ProductFactory instance;
		return instance;
	}
	//产品注册  !!!
	void RegisterProduct(IProductRegistrar* registrar, std::string name)
	{
		m_ProductRegistry[name] = registrar;
	}

	//根据名字name,获取对应具体的产品对象
	ProductType_t* GetProduct(std::string name)
	{
		//从map找到银镜注册过的产品,并返回产品对象
		if (m_ProductRegistry.find(name) != m_ProductRegistry.end())
		{
			return m_ProductRegistry[name]->CreateProduct();
		}

		//未注册的产品,则报错未找到
		std::cout << "No Product found for " << name << std::endl;

		return NULL;
	}
private:
	//禁止外部构造和虚构
	ProductFactory(){}
	~ProductFactory(){}

	//禁止外部拷贝和赋值操作
	ProductFactory(const ProductFactory&);
	const ProductFactory& operator=(const ProductFactory&);
	//保存注册过的产品,key:产品名字, value:产品类型
	std::map*> m_ProductRegistry;  //!!!
};

//产品注册模板类,用于创建具体产品和从工厂里注册产品
//模板参数ProductType_t 表示的类是产品抽象类(基类),ProductImpl_t 表示的类是具体产品(产品种类的子类)
template
class ProductRegistrar : public IProductRegistrar
{
public:
	//构造函数,用于注册产品到工厂,只能显示调用
	explicit ProductRegistrar(std::string name)  //explicit 防止隐形类型转换
	{
		//通过工厂单例把产品注册到工厂
		ProductFactory::instance().RegisterProduct(this, name);
	}

	// 创建具体产品对象指针
	ProductType_t* CreateProduct()
	{
		return new ProductImpl_t();
	}

};
int main()
{
	// ============== 生产耐克球鞋过程 ================= //
	// 注册产品种类为Shoes(基类),产品为NiKe(子类)到工厂,产品名为nike
	ProductRegistrar  nikeShoes("nike");
	//从工厂获取产品种类为Shoes,名称为nike的产品对象
	Shoes* pNiKeShoes = ProductFactory::instance().GetProduct("nike");
	//显示产品的广告语
	pNiKeShoes->Show();
	// 释放资源
	if (pNiKeShoes)
	{
		delete pNiKeShoes;
	}

	// ============== 生产耐克球鞋过程 ================= //
	// 注册产品种类为Clothe(基类),产品为UniqloClothe(子类)到工厂,产品名为uniqlo
	ProductRegistrar  uniqloClothe("uniqlo");
	//从工厂获取产品种类为Shoes,名称为nike的产品对象
	Clothe* pUniqloClothe = ProductFactory::instance().GetProduct("uniqlo");
	//显示产品的广告语
	pUniqloClothe->Show();
	// 释放资源
	if (pUniqloClothe)
	{
		delete pUniqloClothe;
	}

	return 0;
}

总结:

将工厂方法模式改良成模板工厂,虽然可以解决产品新增时,不需要新增具体工厂类,但是缺少一个可以随时随地获取产品对象的方式,说明还有改进的空间。

将模板工厂改良成产品注册模板类+单例工厂模板类,产品注册模板类用于注册不同类型的产品,单例工厂模板类用于获取指定已注册的产品对象。这种方式,可以把工厂模式中产品的注册和获取的主要功能很好的抽象成两个类,并且使用单例模式使得工厂类可以随时随地获取已注册的产品对象。

所以产品注册模板类+单例模板类的工厂模式,达到了开闭法则,并且扩展性高和封装度高。

你可能感兴趣的:(设计模式,设计模式)