设计模式(三)工厂方法模式

前一篇文章介绍了简单工厂模式,留下了一个疑问,如果需要扩展员工等级,有没有不修改既有接口的方式?

答案就是工厂方法模式。

既然员工的职级可以扩展,那创建对应职级员工的工厂是不是也可以扩展?工厂基类提供一个接口获取具体的产品,一旦有新的产品,就创建一个与之对应的工厂类。

看代码(visual studio 2019):

#pragma once

#include <iostream>
#include <list>

//等级基类和工厂基类
class GradeBase
{
public:
	GradeBase(const std::string& strName) :m_strName(strName) {}
	virtual ~GradeBase() = default;

	std::string GetName() const { return m_strName; }
	virtual double GetSalary() = 0;

protected:
	std::string m_strName;  //姓名
};
class FactoryBase
{
public:
	virtual std::unique_ptr<GradeBase> GetLevel(const std::string& strName) = 0;
};

//等级1及其创建工厂类
class Grade_1 : public GradeBase
{
public:
	using GradeBase::GradeBase;
	double GetSalary() override {
		return 5000;
	}
};
class Factory_Grade1 : FactoryBase
{
public:
	std::unique_ptr<GradeBase> GetLevel(const std::string& strName) override {
		return std::make_unique<Grade_1>(strName);
	}
};

//等级2及其创建工厂类
class Grade_2 : public GradeBase
{
public:
	using GradeBase::GradeBase;
	double GetSalary() override {
		return 8000;
	}
};
class Factory_Grade2 : FactoryBase
{
public:
	std::unique_ptr<GradeBase> GetLevel(const std::string& strName) override{
		return std::make_unique<Grade_2>(strName);
	}
};


int main()
{
	std::list<std::unique_ptr<GradeBase>> lstEmployee;
	lstEmployee.push_back(Factory_Grade1().GetLevel("David"));
	lstEmployee.push_back(Factory_Grade2().GetLevel("Tom"));

	for (const auto& employee : lstEmployee)
	{
		if (!employee)
			continue;
		std::cout << employee->GetName() << "薪资:" << employee->GetSalary() << "\n";
	}

	system("pause");
	return 0;
}

上面的例子很简单,脱胎于上一篇的例子。工厂和等级都有一个基类,工厂基类定义了一个虚函数GetLevel,获取对应的等级。

需要注意的是,这个虚函数的返回值,只能是指针或者引用。原因很简单,C++的多态特性决定的,如果不了解,需要先补补C++的基础知识,这里不赘述。

等级Grade_1有其专门的创建工厂Factory_Grade1,继承自FactoryBase,重写GetLevel函数,创建Grade_1的对象并返回。

Grade_2类似,也有其专门的创建工厂Factory_Grade2。

如果需要扩展职工等级,例如Grade_3,只需要再创建一个它专属的工厂Factory_Grade3,重写GetLevel虚函数即可,非常简答。

主函数里,调用不同工厂的GetLevel函数,获取不同的职工等级。

需要注意的是,这里的GetLevel不是static函数,而是类的成员函数。因为,只有成员函数,才能实现多态的特性。

所以,在调用工厂创建具体的职工等级时,需要创建一个临时对象,再用这个临时对象调用成员成员。

可以看出来,工厂方法模式有个弊端:一旦有新的等级类,就必须创建一个新的工厂类,如此,会导致类的数量急剧增加

有没有什么方法能避免这种情况吗?答案是当然有!

C++的模板就能做到,请看下面代码(visual studio 2019):

#pragma once

#include <iostream>
#include <list>

//等级基类和工厂基类
class GradeBase
{
public:
	GradeBase(const std::string& strName) :m_strName(strName) {}
	virtual ~GradeBase() = default;

	std::string GetName() const { return m_strName; }
	virtual double GetSalary() = 0;

protected:
	std::string m_strName;  //姓名
};

//等级1
class Grade_1 : public GradeBase
{
public:
	using GradeBase::GradeBase;
	double GetSalary() override {
		return 5000;
	}
};

//等级2
class Grade_2 : public GradeBase
{
public:
	using GradeBase::GradeBase;
	double GetSalary() override {
		return 8000;
	}
};

template<typename T>
class Factory
{
public:
	std::unique_ptr<GradeBase> GetLevel(const std::string& strName) {
		return std::make_unique<T>(strName);
	}
};

int main()
{
	std::list<std::unique_ptr<GradeBase>> lstEmployee;
	lstEmployee.push_back(Factory<Grade_1>().GetLevel("David"));
	lstEmployee.push_back(Factory<Grade_2>().GetLevel("Tom"));

	for (const auto& employee : lstEmployee)
	{
		if (!employee)
			continue;
		std::cout << employee->GetName() << "薪资:" << employee->GetSalary() << "\n";
	}

	system("pause");
	return 0;
}

这里和上面最大的不同,在于只有一个工厂类Factory,它是一个模板类,根据传入的模板不同,可实例化不同的类对象。

只有一个前提条件,这些类必须继承自同一个基类。

客户端调用时,也不需要知道有哪些工厂,只需要知道有哪些员工等级就行。

执行结果如下:

这个模式虽然看起来很不错,但其实际应用,没有简单工厂普遍,为什么呢?

下一篇就会介绍,一些真正的面向对象程序的编程习惯和参考准则。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值