0x00 工厂方法模式
上一次讲到了简单工厂模式,由于简单工厂模式的局限性,比如:工厂现在能生产ProductA、ProductB和ProductC三种产品了,此时,需要增加生产ProductD产品;那么,首先是不是需要在产品枚举类型中添加新的产品类型标识,然后,修改Factory类中的switch结构代码。是的,这种对代码的修改,对原有代码的改动量较大,易产生编码上的错误(虽然很简单,如果工程大了,出错也是在所难免的!!!)。这种对代码的修改是最原始,最野蛮的修改,本质上不能称之为对代码的扩展。同时,由于对已经存在的函数进行了修改,那么以前进行过的测试,都将是无效的,所有的测试,都将需要重新进行,所有的代码都需要进行重新覆盖。这种,增加成本,不能提高效率的事情,在公司是绝对不允许的(除非昏庸的PM)。出于种种原因,简单工厂模式,在实际项目中使用的较少。那么该怎么办?需要对原有代码影响降到最小,同时能对原有功能进行扩展。
那么今天介绍的工厂方法模式,就隆重登场了。它只是对简单工厂模式的扩展,工厂方法模式是在简单工厂模式的基础上,对“工厂”添加了一个抽象层。将工厂共同的动作抽象出来,作为抽象类,而具体的行为由子类本身去实现,让子类去决定生产什么样的产品。提供一种“封装机制”来避免客户程序和“具体对象创建工作”的紧耦合。
工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。 ——《设计模式》GoF
工厂方法模式是一种实现了“工厂”概念的面向对象设计模式。就像其他创建型模式一样,它也是处理在不指定对象具体类型的情况下创建对象的问题。
工厂方法模式的实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。”
0x01 工厂方法UML类图
工厂方法模式包含如下角色:
IProduct:抽象产品
CConcreteProduct:具体产品
IFactory:抽象工厂
CConcreteFactory:具体工厂
用途
工厂方法模式和简单工厂模式虽然都是通过工厂来创建对象,他们之间最大的不同是——工厂方法模式在设计上完全完全符合“开闭原则”。
在以下情况下可以使用工厂方法模式:
一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
0x02 工厂方法的实例
/**************************************************************************************
* @Copyright (c) , All rights reserved.
* @file: main.cpp
* @version: ver 1.0
* @author: zbb
* @brief:
* @change:
* @email: binbin_erices@163.com
* Date Version Changed By Changes
* 2020/1/19 0:15 1.0 zbb create
***************************************************************************************/
#include <iostream>
class IProduct
{
public:
virtual ~IProduct() {}
};
class Dell : public IProduct
{
public:
Dell()
{
std::cout << "make Dell" << std::endl;
}
};
class Aphone : public IProduct
{
public:
Aphone()
{
std::cout << "make Aphone" << std::endl;
}
};
class IFactory
{
public:
virtual ~IFactory() {}
virtual IProduct* MakeProduct() = 0;
};
class DellFactory : public IFactory
{
public:
virtual IProduct* MakeProduct()
{
return new Dell();
}
};
class AphoneFactory : public IFactory
{
public:
virtual IProduct* MakeProduct()
{
return new Aphone();
}
};
int main()
{
IFactory* factory1 = new AphoneFactory();
IProduct* product1 = factory1->MakeProduct();
IFactory* factory2 = new DellFactory();
IProduct* product2 = factory2->MakeProduct();
delete factory1;
delete product1;
delete factory2;
delete product2;
return 0;
}
0x03 工厂方法模式的利与弊
1. 工厂方法模式优点
- 工厂方法模式是完全符合开闭原则的;
- 工厂模式是一种典型的解耦模式,可以降低对象之间的耦合度;
- 工厂模式是依靠抽象架构的,它把实例化产品的任务交由实现类完成,扩展性比较好。
- 可以使代码结构清晰,有效地封装变化。
- 对调用者屏蔽具体的产品类。如果使用工厂模式,调用者只关心产品的接口就可以了,至于具体的实现,调用者根本无需关心。即使变更了具体的实现,对调用者来说没有任何影响。
2. 工厂方法模式缺点
在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。
0x04 工厂方法与简单工厂的区别
工厂模式克服了简单工厂模式违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点。
他们都是集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可实现,降低了客户端与产品对象的耦合。
总结:
工厂方法模式是简单工厂模式的进一步抽象和推广。
由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
工厂方法模式的主要优点是增加新的产品类时无须修改现有系统,并封装了产品对象的创建细节,系统具有良好的灵活性和可扩展性;其缺点在于增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性