更多最新文章欢迎大家访问我的个人博客:smile::豆腐别馆

上文介绍的工厂方法模式我们以豆腐跟猪蹄举栗,考虑的是一类产品的生产,如豆腐厂只生产豆腐,猪蹄加工厂只生产大猪蹄。同种类称为同等级,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。而抽象工厂方法,正是可以提供一个创建一系列相关或互相依赖对象的接口,且无需指定它们具体的类。

一、模式的定义

  1. 抽象工厂(AbstractFactory)模式,又称为Kit(工具箱)模式:
  • 是围绕一个超级工厂创建其它的工厂,该超级工厂又称为其它工厂的工厂。
  • 是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂方法可生产多个等级的产品。
  • 为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无需指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。(每个生成的工厂都能按照工厂模式提供对象。)
  1. 使用抽象工厂模式一般要满足以下条件:
    • 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
    • 系统一次只可能消费其中某一族产品,即同族的产品一起使用。

ps: 关于产品等级结构与产品族:

产品等级结构
产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。

产品族
在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

二、模式的优缺点

1. 优点
抽象工厂模式除了具有工厂方法模式的优点外,其主要优点如下:

  • 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来管理。
  • 当增加一个新的产品族时不需要修改原代码,满足开闭原则。

2. 缺点
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。

即产品族易扩展,产品等级难扩展。

三、模式的结构

抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等四个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。

1. 主要角色

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 new Product(),可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Abstract Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品
  • 具体产品(Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

2. 模式结构图

四、模式的实现

依旧以豆腐猪蹄为例,不管怎么变,底层的对象总不会变,咱们先把上文中的豆腐跟猪蹄类都拷贝过来:

1. 主食产品族

(1)抽象产品:食物类

1
2
3
4
5
6
7
8
9
10
/**
* 食物类
* 转载请注明出处,更多技术文章欢迎大家访问我的个人博客站点:https://www.doufuplus.com
*
* @author doufuplus
*/
public abstract class Food {

public abstract void eat();
}

(2)具体产品:豆腐类

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 豆腐类
* 转载请注明出处,更多技术文章欢迎大家访问我的个人博客站点:https://www.doufuplus.com
*
* @author doufuplus
*/
public class Doufu extends Food {

@Override
public void eat() {
System.out.println("软软的豆腐...");
}
}

(3)具体产品:猪蹄类

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 猪蹄类
* 转载请注明出处,更多技术文章欢迎大家访问我的个人博客站点:https://www.doufuplus.com
*
* @author doufuplus
*/
public class Trotter extends Food {

@Override
public void eat() {
System.out.println("大大的猪蹄...");
}
}

2. 饮料产品族

上文提到抽象工厂需要满足系统中存在有多个产品族,那么我们就再来创建个饮料产品族:

(1)抽象产品:饮料类

1
2
3
4
5
6
7
8
9
10
/**
* 饮料类
* 转载请注明出处,更多技术文章欢迎大家访问我的个人博客站点:https://www.doufuplus.com
*
* @author doufuplus
*/
public abstract class Drink {

public abstract void drink();
}

(2)具体产品:红茶类

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 红茶类
* 转载请注明出处,更多技术文章欢迎大家访问我的个人博客站点:https://www.doufuplus.com
*
* @author doufuplus
*/
public class BlackTea extends Drink {

@Override
public void drink() {
System.out.println("冰镇的红茶!");
}
}

(3)具体产品:绿茶类

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 绿茶类
* 转载请注明出处,更多技术文章欢迎大家访问我的个人博客站点:https://www.doufuplus.com
*
* @author doufuplus
*/
public class GreenTea extends Drink {

@Override
public void drink() {
System.out.println("冰镇的绿茶!");
}
}

3. 工厂类

(1)抽象工厂:我们需要创建一个抽象工厂,用于提供创建产品的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 抽象工厂
* <p>
* 提供了创建产品的接口,它包含多个创建产品的方法 new Product(),可以创建多个不同等级的产品。
*
* @author doufuplus
*/
public abstract class AbstractFactory {

public abstract Food getFood(String food);

public abstract Drink getDrink(String drink);
}

(2)具体工厂:既然有了抽象的工厂,那就会有具体的工厂去实现抽象工厂中的方法,新建如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 具体工厂(一)食物工厂
* <p>
* 主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
*
* @author doufuplus
*/
public class FoodFactory extends AbstractFactory {

@Override
public Food getFood(String food) {
if ("doufu".equalsIgnoreCase(food)) {
return new Doufu();
} else if ("trotter".equalsIgnoreCase(food)) {
return new Trotter();
} else {
return null;
}
}

@Override
public Drink getDrink(String drink) {
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 具体工厂(二)饮料工厂
* <p>
* 主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
*
* @author doufuplus
*/
public class DrinkFactory extends AbstractFactory {

@Override
public Food getFood(String food) {
return null;
}

@Override
public Drink getDrink(String drink) {
if ("blackTea".equalsIgnoreCase(drink)) {
return new BlackTea();
} else if ("greenTea".equalsIgnoreCase(drink)) {
return new GreenTea();
} else {
return null;
}
}
}

4. 调用测试

事实上代码到了这里已经是可以运行的了,测试如下:
在这里插入图片描述
但其实还没完,既然声称为”工厂的工厂”,那么我们就还需要对工厂做到统一的调用,而不是目前这样还需要手动去实例化一个具体的工厂,因此我们需要新建一个工厂处理器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 工厂处理器
* <p>
* 主要是实现对工厂的处理,方便客户端调用
*
* @author doufuplus
*/
public class FactoryHandler {

/**
* 调用具体工厂
*/
public static AbstractFactory getFactory(String factory) {
if (factory.equalsIgnoreCase("food")) {
return new FoodFactory();
} else if (factory.equalsIgnoreCase("drink")) {
return new DrinkFactory();
}
return null;
}
}

最终调用如下:
在这里插入图片描述

五、工厂模式的退化

当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。实际上在日常开发中我们并不需要可以去分清到底哪个用了工厂方法,哪个用了抽象工厂,我们只要知道,什么时候该用工厂模式即可。

六、模式的应用场景

抽象工厂模式最早的应用是用于创建属于不同操作系统的视窗构件。如Java中的AWT中的Button和Text等构件在 Windows 和 UNIX 中的本地实现是不同的。

抽象工厂模式通常适用于以下场景:

  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
  • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

参考:
菜鸟教程
C语言中文网-设计模式