2021-09-09 14:23:55 +00:00
|
|
|
|
在介绍简单工厂方法模式时Jungle总结出简单工厂模式存在最大的问题是违背了“开闭原则”,每当增加新的产品时,需要修改工厂类的逻辑。为了规避这种不足,同时很好的利用简单工厂模式的优点,本节Jungle将声情并茂地为您奉上工厂方法模式。
|
|
|
|
|
|
|
|
|
|
# 1.工厂方法模式简介
|
|
|
|
|
简单工厂模式中,每新增一个具体产品,就需要修改工厂类内部的判断逻辑。为了不修改工厂类,遵循开闭原则,工厂方法模式中不再使用工厂类统一创建所有的具体产品,而是针对不同的产品设计了不同的工厂,每一个工厂只生产特定的产品。
|
|
|
|
|
|
|
|
|
|
工厂方法模式定义:
|
|
|
|
|
```
|
|
|
|
|
工厂方法模式:
|
|
|
|
|
|
|
|
|
|
定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
|
|
|
|
|
```
|
|
|
|
|
## 2.工厂方法模式结构
|
|
|
|
|
从工厂方法模式简介中,可以知道该模式有以下几种角色:
|
|
|
|
|
|
|
|
|
|
- **抽象工厂(AbstractFactory)**:所有生产具体产品的工厂类的基类,提供工厂类的公共方法;
|
|
|
|
|
- **具体工厂(ConcreteFactory)**:生产具体的产品
|
|
|
|
|
- **抽象产品(AbstractProduct)**:所有产品的基类,提供产品类的公共方法
|
|
|
|
|
- **具体产品(ConcreteProduct)**:具体的产品类
|
|
|
|
|
工厂方法模式UML类图如下:
|
2021-09-09 14:29:04 +00:00
|
|
|
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/02.FactoryMethod/1.Picture/%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8FUML%E5%9B%BE.png)
|
2021-09-09 14:23:55 +00:00
|
|
|
|
|
|
|
|
|
# 3.工厂方法模式代码实例
|
|
|
|
|
考虑这样一个场景,如下图:
|
2021-09-09 14:29:04 +00:00
|
|
|
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/02.FactoryMethod/1.Picture/%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F.png)
|
2021-09-09 14:23:55 +00:00
|
|
|
|
```
|
2021-09-09 14:29:04 +00:00
|
|
|
|
Jungle想要进行户外运动,它可以选择打篮球、踢足球或者玩排球。和上一次的体育保管室不同,这次分别由篮球保管室、足球保管室和排球保管室.
|
|
|
|
|
Jungle只需直接去相应的保管室就可以拿到对应的球!然后Jungle就可以愉快地玩耍了。
|
2021-09-09 14:23:55 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
对应的UML实例图如下图:
|
2021-09-09 14:29:04 +00:00
|
|
|
|
![avatar](https://github.com/FengJungle/DesignPattern/blob/master/02.FactoryMethod/1.Picture/%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8FUML%E5%AE%9E%E4%BE%8B%E5%9B%BE.png)
|
2021-09-09 14:23:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 3.1.定义抽象产品类AbstractSportProduct,方法不提供实现
|
|
|
|
|
```
|
|
|
|
|
//抽象产品类AbstractProduct
|
|
|
|
|
class AbstractSportProduct
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
AbstractSportProduct(){
|
|
|
|
|
|
|
|
|
|
}
|
2021-09-09 14:26:43 +00:00
|
|
|
|
virtual ~AbstractSportProduct(){}
|
2021-09-09 14:23:55 +00:00
|
|
|
|
//抽象方法:
|
|
|
|
|
virtual void printName() = 0;
|
|
|
|
|
virtual void play(){} = 0;
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 3.2.定义三个具体产品类
|
|
|
|
|
```
|
|
|
|
|
//具体产品类Basketball
|
|
|
|
|
class Basketball :public AbstractSportProduct
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Basketball(){
|
|
|
|
|
printName();
|
|
|
|
|
play();
|
|
|
|
|
}
|
|
|
|
|
//具体实现方法
|
|
|
|
|
void printName(){
|
|
|
|
|
printf("Jungle get Basketball\n");
|
|
|
|
|
}
|
|
|
|
|
void play(){
|
|
|
|
|
printf("Jungle play Basketball\n\n");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//具体产品类Football
|
|
|
|
|
class Football :public AbstractSportProduct
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Football(){
|
|
|
|
|
printName();
|
|
|
|
|
play();
|
|
|
|
|
}
|
|
|
|
|
//具体实现方法
|
|
|
|
|
void printName(){
|
|
|
|
|
printf("Jungle get Football\n");
|
|
|
|
|
}
|
|
|
|
|
void play(){
|
|
|
|
|
printf("Jungle play Football\n\n");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//具体产品类Volleyball
|
|
|
|
|
class Volleyball :public AbstractSportProduct
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Volleyball(){
|
|
|
|
|
printName();
|
|
|
|
|
play();
|
|
|
|
|
}
|
|
|
|
|
//具体实现方法
|
|
|
|
|
void printName(){
|
|
|
|
|
printf("Jungle get Volleyball\n");
|
|
|
|
|
}
|
|
|
|
|
void play(){
|
|
|
|
|
printf("Jungle play Volleyball\n\n");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 3.3.定义抽象工厂类AbstractFactory,方法为纯虚方法
|
|
|
|
|
```
|
|
|
|
|
//抽象工厂类
|
|
|
|
|
class AbstractFactory
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
virtual AbstractSportProduct *getSportProduct() = 0;
|
2021-09-09 14:26:43 +00:00
|
|
|
|
virtual ~AbstractFactory(){}
|
2021-09-09 14:23:55 +00:00
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 3.4.定义三个具体工厂类
|
|
|
|
|
```
|
|
|
|
|
/具体工厂类BasketballFactory
|
|
|
|
|
class BasketballFactory :public AbstractFactory
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
BasketballFactory(){
|
|
|
|
|
printf("BasketballFactory\n");
|
|
|
|
|
}
|
|
|
|
|
AbstractSportProduct *getSportProduct(){
|
|
|
|
|
printf("basketball");
|
|
|
|
|
return new Basketball();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//具体工厂类FootballFactory
|
|
|
|
|
class FootballFactory :public AbstractFactory
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
FootballFactory(){
|
|
|
|
|
printf("FootballFactory\n");
|
|
|
|
|
}
|
|
|
|
|
AbstractSportProduct *getSportProduct(){
|
|
|
|
|
return new Football();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//具体工厂类VolleyballFactory
|
|
|
|
|
class VolleyballFactory :public AbstractFactory
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
VolleyballFactory(){
|
|
|
|
|
printf("VolleyballFactory\n");
|
|
|
|
|
}
|
|
|
|
|
AbstractSportProduct *getSportProduct(){
|
|
|
|
|
return new Volleyball();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 3.5.客户端使用方法示例
|
|
|
|
|
```
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include "FactoryMethod.h"
|
|
|
|
|
|
|
|
|
|
int main()
|
|
|
|
|
{
|
|
|
|
|
printf("工厂方法模式\n");
|
|
|
|
|
|
|
|
|
|
//定义工厂类对象和产品类对象
|
|
|
|
|
AbstractFactory *fac = nullptr;
|
|
|
|
|
AbstractSportProduct *product = nullptr;
|
|
|
|
|
|
|
|
|
|
fac = new BasketballFactory();
|
|
|
|
|
product = fac->getSportProduct();
|
2021-09-09 14:26:43 +00:00
|
|
|
|
delete fac;
|
|
|
|
|
fac = nullptr;
|
|
|
|
|
delete product;
|
|
|
|
|
product = nullptr;
|
2021-09-09 14:23:55 +00:00
|
|
|
|
|
|
|
|
|
fac = new FootballFactory();
|
|
|
|
|
product = fac->getSportProduct();
|
2021-09-09 14:26:43 +00:00
|
|
|
|
delete fac;
|
|
|
|
|
fac = nullptr;
|
|
|
|
|
delete product;
|
|
|
|
|
product = nullptr;
|
2021-09-09 14:23:55 +00:00
|
|
|
|
|
|
|
|
|
fac = new VolleyballFactory();
|
|
|
|
|
product = fac->getSportProduct();
|
2021-09-09 14:26:43 +00:00
|
|
|
|
delete fac;
|
|
|
|
|
fac = nullptr;
|
|
|
|
|
delete product;
|
|
|
|
|
product = nullptr;
|
2021-09-09 14:23:55 +00:00
|
|
|
|
|
|
|
|
|
system("pause");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 3.6.效果
|
|
|
|
|
```
|
|
|
|
|
工厂方法模式
|
|
|
|
|
BasketballFactory
|
|
|
|
|
basketballJungle get Basketball
|
|
|
|
|
Jungle play Basketball
|
|
|
|
|
|
|
|
|
|
FootballFactory
|
|
|
|
|
Jungle get Football
|
|
|
|
|
Jungle play Football
|
|
|
|
|
|
|
|
|
|
VolleyballFactory
|
|
|
|
|
Jungle get Volleyball
|
|
|
|
|
Jungle play Volleyball
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
# 4.工厂方法模式总结
|
|
|
|
|
如果Jungle想玩棒球(Baseball),只需要增加一个棒球工厂(BaseballFacory),然后在客户端代码中修改具体工厂类的类名,而原有的类的代码无需修改。由此可看到,相较简单工厂模式,工厂方法模式更加符合开闭原则。工厂方法是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。
|
|
|
|
|
|
|
|
|
|
## 优点:
|
|
|
|
|
|
|
|
|
|
- 工厂方法用于创建客户所需产品,同时向客户隐藏某个具体产品类将被实例化的细节,用户只需关心所需产品对应的工厂;
|
|
|
|
|
- 工厂自主决定创建何种产品,并且创建过程封装在具体工厂对象内部,多态性设计是工厂方法模式的关键;
|
|
|
|
|
- 新加入产品时,无需修改原有代码,增强了系统的可扩展性,符合开闭原则。
|
|
|
|
|
|
|
|
|
|
## 缺点:
|
|
|
|
|
|
|
|
|
|
- 添加新产品时需要同时添加新的产品工厂,系统中类的数量成对增加,增加了系统的复杂度,更多的类需要编译和运行,增加了系统的额外开销;
|
|
|
|
|
- 工厂和产品都引入了抽象层,客户端代码中均使用的抽象层(AbstractFactory和AbstractSportProduct ),增加了系统的抽象层次和理解难度。
|
|
|
|
|
|
|
|
|
|
## 适用环境:
|
|
|
|
|
|
|
|
|
|
- 客户端不需要知道它所需要创建的对象的类;
|
2021-09-09 14:29:04 +00:00
|
|
|
|
- 抽象工厂类通过其子类来指定创建哪个对象(运用多态性设计和里氏代换原则)
|