概念
工厂方法模式(Factory Method Pattern)又称工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
工厂方法,可以理解为一种将实例化逻辑委托给子类的方法,父基类提供共有实现,派生类完成个性化定制实现
使用时机
如果有这样的场景,存在部分共有的功能逻辑,但是在运行时又需要动态决定使用哪个子类的逻辑实现,也就是说
需要动态根据需求选择不同的子类实现,这个时候可以使用工厂方法。
建议在以下情况下选用工厂方法模式。
- 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现。
- 如果一个类本身就希望由他的子类来创建所需的对象的时候,应该使用工厂方法模式。
与简单工厂的区别
初接触工厂方法模式,会感觉,“这不是跟简单工厂一样吗”
从本质上讲,他们确实是非常类似的,在具体实现上都是“选择实现”。但是也存在不同点,简单工厂是直接在工厂类里进行“选择实现”;而工厂方法会把这个工作延迟到子类来实现,工厂类里面使用工厂方法的地方是依赖于抽象而不是具体的实现,从而使得系统更加灵活,具有更好的可维护性和可扩展性。
从某个角度讲,可以认为简单工厂就是工厂方法模式的一种特例,因此他们的本质是类似的。
具体的举例,简单工厂实例中,客户就是要一个门,而不关心创建过程,最后实际创造的是一个木门,这个比较尴尬,如果客户要的是个铁门呢?所以在这个例子中,也是存在两维抽象的,一是“门”这个类型的抽象,二是“造门”这个方法的抽象。简单工厂只做到了前者,而没有给出后者的解决方案,如果看我们按照工厂方法的思路,将门工厂造门这件事进行细分,木门交给木门工厂,铁门交给铁门工厂,这就是工厂方法的实现,客户需要先制定委托对象,而不必关系具体怎么造门。
具体实现
C++实现
结构图
代码实现
#include <iostream>
class IInterviewer
{
public:
virtual void askQuestions() = 0;
};
class Developer : public IInterviewer
{
public:
void askQuestions() override {
std::cout << "Asking about design patterns!" << std::endl;
}
};
class CommunityExecutive : public IInterviewer
{
public:
void askQuestions() override {
std::cout << "Asking about community building!" << std::endl;
}
};
class HiringManager
{
public:
void takeInterview() {
IInterviewer* interviewer = this->makeInterviewer();
interviewer->askQuestions();
}
protected:
virtual IInterviewer* makeInterviewer() = 0;
};
template <typename Interviewer>
class OtherManager : public HiringManager {
protected:
IInterviewer* makeInterviewer() override {
return new Interviewer();
}
};
int main()
{
HiringManager* devManager = new OtherManager<Developer>();
devManager->takeInterview();
HiringManager* marketingMnager = new OtherManager<CommunityExecutive>();
marketingMnager->takeInterview();
return 0;
}
运行结果
g++ -o factory-method factory-method.cpp --std=c++11
./factory-method
Asking about design patterns!
Asking about community building!
总结
IInterviwer为基础类,定义为一个虚基类,具体派生出了Devoloper和CommunityExecutive两个实现子类;
HiringManager为工厂基类,定义为一个虚基类,OtherManager为具体实现的派生类,根据传入的template type,返回不同的创建对象。
Golang实现
结构图
代码实现
package factory_method
//Operator是被封装的接口
type Operator interface {
SetA(int)
SetB(int)
Result() int
}
//工厂接口
type OperatorFactory interface {
Create() Operator
}
//OperatorBase是Operator接口实现的基类,封装公有方法
type OperatorBase struct {
a,b int
}
//SetA方法实现
func (op *OperatorBase) SetA(aValue int) {
op.a = aValue
}
//SetB方法实现
func (op *OperatorBase) SetB(bValue int) {
op.b = bValue
}
//下面是分别构造不同类型Operator以及对应生产的工厂类
//加法工厂类
type PlusOperatorFactory struct {
}
//加法工厂类实现工厂接口
func (pof PlusOperatorFactory) Create() Operator {
return &PlusOperator{}
}
//加法类
type PlusOperator struct {
OperatorBase
}
//加法类实现Operator接口的Result函数
func (pop *PlusOperator) Result() int {
return pop.a + pop.b
}
//减法工厂类
type MinusOperatorFactory struct {
}
//减法工厂类实现工厂接口
func (mof MinusOperatorFactory) Create() Operator {
return &MinusOperator{}
}
//减法类
type MinusOperator struct {
OperatorBase
}
//减法类实现Operator接口的Result函数
func (mop *MinusOperator) Result() int {
return mop.a - mop.b
}
测试用例
package factory_method
import "testing"
func TestPlusOperator_Result(t *testing.T) {
pof := PlusOperatorFactory{}
pop := pof.Create()
pop.SetA(1)
pop.SetB(2)
if pop.Result() != 3 {
t.Fatal("test plus operator factory failed")
}
}
func TestMinusOperator_Result(t *testing.T) {
mof := MinusOperatorFactory{}
mop := mof.Create()
mop.SetA(2)
mop.SetB(1)
if mop.Result() != 1 {
t.Fatal("test minus operator factory failed")
}
}
运行结果
go test -v factory-method.go factory-method_test.go
=== RUN TestPlusOperator_Result
--- PASS: TestPlusOperator_Result (0.00s)
=== RUN TestMinusOperator_Result
--- PASS: TestMinusOperator_Result (0.00s)
PASS
ok command-line-arguments 0.734s
优化后更实用的版本
func LoadFactory(name string) OperatorFactory {
switch name {
case "plus":
return PlusOperatorFactory{}
case "minus":
return MinusOperatorFactory{}
default:
return PlusOperatorFactory{}
}
}
测试用例
......
pof := LoadFactory("plus")
......
工厂方法模式的思考
工厂方法模式的缺点
- 在添加新产品时,需要编写新的具体产品类,而且还需要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。