本博客大部分内容来于免费在线学习设计模式

1:抽象工厂模式

抽象工厂(AbstractFactory)模式是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

2:抽象工厂模式问题

工厂方法模式考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机、计算机软件学院只培养计算机软件专业的学生等。

但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

而抽象工厂模式将考虑多等级产品的生产。将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,

3:抽象工厂模式解决方案

首先,抽象工厂模式建议为系列中的每件产品明确声明接口。然后确保所有产品变体都继承这些接口,例如(所有品牌的电视机都继承电视机接口,所有品牌的空调都继承空调接口)

然后声明抽象工厂–包含系列中所有产品构造方法的接口。例如:createWasher(创建洗衣机),createTV(创建电视机)…
这些方法必须返回抽象产品类型,即我们之前抽取的那些接口。

对于系列产品的每个变体,我们都将基于抽象工厂接口创建不同的工厂类。每个工厂类都只能返回特定类别的产品,例如,海尔只能创建海尔空调;格力只能创建格力空调。

客户端代码可以通过相应的抽象接口调用工厂和产品类。你无需修改实际客户端代码,就能更改传递给客户端的工厂类,也能更改客户端代码接收的产品变体。

4:抽象工厂模式结构

20200816195948

  1. 抽象产品 (Abstract Product) 为构成系列产品的一组不同但相关的产品声明接口。
  2. 具体产品 (Concrete Product) 是抽象产品的多种不同类型实现。所有变体 (海尔/格力) 都必须实现相应的抽象产品 (洗衣机/电视机)。
  3. 抽象工厂 (Abstract Factory) 接口声明了一组创建各种抽象产品的方法。
  4. 具体工厂 (Concrete Factory) 实现抽象工厂的构建方法。每个具体工厂都对应特定产品变体,且仅创建此种产品变体。
  5. 尽管具体工厂会对具体产品进行初始化,其构建方法签名必须返回相应的抽象产品。这样,使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。客户端 (Client) 只需通过抽象接口调用工厂和产品对象,就能与任何具体工厂/产品变体交互。

代码示例:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 抽象产品接口
interface Product {
void display();
}

// 具体产品1:洗衣机
class Washer implements Product {
@Override
public void display() {
System.out.println("洗衣服");
}
}

// 具体产品2:电视机
class TV implements Product {
@Override
public void display() {
System.out.println("看电视");
}
}

// 抽象工厂接口:包含所有产品的创建方法
interface AbstractFactory1 {

Product createWasher();

Product createTV();
}

// 具体工厂1:海尔
class HaierFactory implements AbstractFactory1 {
@Override
public Product createWasher() {
System.out.println("海尔洗衣机");
return new Washer();
}

@Override
public Product createTV() {
System.out.println("海尔电视机");
return new TV();
}
}

// 具体工厂2:格力
class GreeFactory implements AbstractFactory1{
@Override
public Product createWasher() {
System.out.println("格力洗衣机");
return new Washer();
}

@Override
public Product createTV() {
System.out.println("格力电视机");
return new TV();
}
}

public class AbstractFactoryTest {
public static void main(String[] args) {
// 创建海尔品牌电视机,洗衣机
HaierFactory haierFactory = new HaierFactory();
haierFactory.createTV().display();
haierFactory.createWasher().display();
// 创建格力品牌电视机,洗衣机
GreeFactory greeFactory = new GreeFactory();
greeFactory.createTV().display();
greeFactory.createWasher().display();
}

}

5:象工厂模式适用场景

  1. 如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息,或者出于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建,在这种情况下,你可以使用抽象工厂。
  2. 如果你有一个基于一组抽象方法的类,且其主要功能因此变得不明确,那么在这种情况下可以考虑使用抽象工厂模式。

6:抽象工厂模式优缺点

优点 缺点
你可以确保同一工厂生成的产品相互匹配。 由于采用该模式需要向应用中引入众多接口和类,代码可能会比之前更加复杂。
你可以避免客户端和具体产品代码的耦合。 添加新产品需要修改全部工厂代码
单一职责原则。你可以将产品生成代码抽取到同一位置,使得代码易于维护。
开闭原则。向应用程序中引入新产品变体时,你无需修改客户端代码。

7:抽象工厂和其他模式关系

  • 在许多设计工作的初期都会使用工厂方法模式(较为简单,而且可以更方便地通过子类进行定制),随后演化为使用抽象工厂模式、原型模式或生成器模式(更灵活但更加复杂)。
  • 生成器重点关注如何分步生成复杂对象。抽象工厂专门用于生产一系列相关对象。抽象工厂会马上返回产品,生成器则允许你在获取产品前执行一些额外构造步骤。
  • 抽象工厂模式通常基于一组工厂方法,但你也可以使用原型模式来生成这些类的方法。
  • 当只需对客户端代码隐藏子系统创建对象的方式时,你可以使用抽象工厂来代替外观模式。
  • 你可以将抽象工厂和桥接模式搭配使用。如果由桥接定义的抽象只能与特定实现合作,这一模式搭配就非常有用。在这种情况下,抽象工厂可以对这些关系进行封装,并且对客户端代码隐藏其复杂性。
  • 抽象工厂、生成器和原型都可以用单例模式来实现。

8:抽象工厂模式举例

继续用工厂模式的例子,现在你觉得之前那个水果摊卖的东西不够好,你想买更新鲜的水果。所以你找到另一个水果摊,这等于另一个工厂。通过不同工厂你可以获得因种种因素而不同口味的水果。这两个水果摊加起来就是抽象工厂。

  1. 以不同的产品类型与产品变体为维度绘制矩阵。
    例如小米手机,小米电脑,华为手机,华为电脑构成矩阵
  2. 为所有产品声明抽象产品接口。然后让所有具体产品类实现这些接口。
    例如手机,电脑都是电器;苹果,香蕉都是水果。
  3. 声明抽象工厂接口,并且在接口中为所有抽象产品提供一组构建方法。
    对于所有电器,有一个抽象接口,和通用的方法。
  4. 为每种产品变体实现一个具体工厂类。
    小米工厂,华为工厂
  5. 在应用程序中开发初始化代码。该代码根据应用程序配置或当前环境,对特定具体工厂类进行初始化。然后将该工厂对象传递给所有需要创建产品的类。
  6. 找出代码中所有对产品构造函数的直接调用, 将其替换为对工厂对象中相应构建方法的调用。

9:参考