抽象工厂 Abstract Factory

抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。

工厂模式分为:简单工厂、工厂方法、抽象工厂


为什么要使用?

抽象工厂模式的对象职责:

创建一系列相关的对象, 而无需指定其具体类。

例如现在有一个家具厂🏬(抽象工厂),对于购买者👨来说,他们并不关心家具厂的家具🛏️(抽象产品)是什么风格的;而对于创造者来说,他们必须设计各种风格的家具,以便更好的销售。

同一风格的不同家具是一种产品实现,制造同一风格家具的是一种工厂实现。又或者说顾客需要风格统一的家具,每个具体的家具工厂制作的家具都是同一风格。

家具的风格更新十分频繁,使用抽象工厂模式能够让我们不需要修改其他代码,只需要根据抽象工厂和抽象产品增加新风格的家具工厂和家具产品。

总的来说,使用者更关心一组产品的某些共性(有哪些家具),至于一些具体的实现(这些家具有多少种风格)并不在意。而创建者要找到正确的共性,并尽可能隐藏具体实现细节,始终围绕着提供符合共性的产品(不同风格的家具)。


模式结构

  • 抽象工厂(Abstract Factory) :接口声明了一组创建各种抽象产品的方法。
  • 抽象产品(Abstract Product 通用的一类对象或接口):为相同系列的不同产品声明接口。抽象产品的好坏直接决定了抽象工厂和具体工厂能否发挥最大作用的关键所在。
  • 具体工厂(Concrete Factory) :实现抽象工厂的构建方法。 每个具体工厂都对应特定产品变体, 且仅创建此种产品变体。
  • 具体产品(Concrete Product 继承通用对象或接口后扩展特有属性):是抽象产品的多种不同系列实现。所有系列产品都必须实现相应的抽象产品。

抽象工厂模式的类图:

抽象接口和抽象类的本质相同,可以根据不同业务进行修改。使用者无需关注当前工厂和产品是何种实现,只需要使用抽象工厂创建抽象产品即可。


模式实现

该示例使用抽象工厂模式定义抽象家具工厂和一系列抽象家具产品,分别用现代风格和维多利亚风格实现。

示例程序的类图

具体工厂实现类的方法是重写抽象工厂类的,所以其返回类型和抽象工厂类一样,这里由于害怕线条混乱,就不把具体工厂实现类和抽象产品类的依赖关系画出来了。

代码实现

抽象工厂类
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
package example.factory;

import example.product.Chair;
import example.product.Table;

/** 抽象家具工厂 */
public abstract class FurnitureFactory {
/**
* 用反射构建具体工厂
* @param className 具体工厂全类名
*/
public static FurnitureFactory getFactory(String className) {
FurnitureFactory factory = null;
try {
factory = (FurnitureFactory) Class.forName(className).newInstance();
} catch (ClassNotFoundException e) {
System.out.println("没有找到类:" + className);
} catch (Exception e) {
e.printStackTrace();
}
return factory;
}

/**
* 创建椅子产品
* @return 抽象椅子产品
*/
public abstract Chair createChair();

/**
* 创建桌子产品
* @return 抽象桌子产品
*/
public abstract Table createTable();
}
抽象产品类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package example.product;

/** 抽象椅子产品 */
public abstract class Chair {
/**
* 获取椅子腿数
* @return 椅子腿数
*/
public abstract int getLegsNum();

/**
* 获取椅子高度
* @return 椅子高度
*/
public abstract double getHeight();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package example.product;

/** 抽象桌子产品 */
public abstract class Table {
/**
* 获取桌子面积
* @return 桌子面积
*/
public abstract int getSize();

/**
* 获取桌子高度
* @return 桌子高度
*/
public abstract double getHeight();
}
具体工厂类
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
package example.factory.impl;

import example.factory.FurnitureFactory;
import example.product.Chair;
import example.product.Table;
import example.product.impl.ModernChairImpl;
import example.product.impl.ModernTableImpl;

/** 现代风格家具工厂实现 */
public class ModernFurnitureFactoryImpl extends FurnitureFactory {
/**
* 创建椅子产品
* @return 抽象椅子产品
*/
@Override
public Chair createChair() {
return new ModernChairImpl();
}

/**
* 创建桌子产品
* @return 抽象桌子产品
*/
@Override
public Table createTable() {
return new ModernTableImpl();
}
}
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
package example.factory.impl;

import example.factory.FurnitureFactory;
import example.product.Chair;
import example.product.Table;
import example.product.impl.VictorianChairImpl;
import example.product.impl.VictorianTableImpl;

/** 维多利亚风格家具工厂实现 */
public class VictorianFurnitureFactoryImpl extends FurnitureFactory {
/**
* 创建椅子产品
* @return 抽象椅子产品
*/
@Override
public Chair createChair() {
return new VictorianChairImpl();
}

/**
* 创建桌子产品
* @return 抽象桌子产品
*/
@Override
public Table createTable() {
return new VictorianTableImpl();
}
}
具体产品类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package example.product.impl;

import example.product.Chair;

/** 现代风格椅子产品实现 */
public class ModernChairImpl extends Chair {
/**
* 获取椅子腿数
* @return 椅子腿数
*/
@Override
public int getLegsNum() {
return 4;
}

/**
* 获取椅子高度
* @return 椅子高度
*/
@Override
public double getHeight() {
return 70;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package example.product.impl;

import example.product.Table;

/** 现代风格桌子产品实现 */
public class ModernTableImpl extends Table {
/**
* 获取桌子面积
* @return 桌子面积
*/
@Override
public int getSize() {
return 200;
}

/**
* 获取桌子高度
* @return 桌子高度
*/
@Override
public double getHeight() {
return 70;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package example.product.impl;

import example.product.Chair;

/** 维多利亚风格产品椅子实现 */
public class VictorianChairImpl extends Chair {
/**
* 获取椅子腿数
* @return 椅子腿数
*/
@Override
public int getLegsNum() {
return 4;
}

/**
* 获取椅子高度
* @return 椅子高度
*/
@Override
public double getHeight() {
return 50;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package example.product.impl;

import example.product.Table;

/** 维多利亚风格产品桌子实现 */
public class VictorianTableImpl extends Table {
/**
* 获取桌子面积
* @return 桌子面积
*/
@Override
public int getSize() {
return 100;
}

/**
* 获取桌子高度
* @return 桌子高度
*/
@Override
public double getHeight() {
return 50;
}
}

代码测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import example.factory.FurnitureFactory;
import example.product.Chair;
import example.product.Table;

/** 测试抽象工厂模式 */
public class Test {
public static void main(String[] args) {
if (args.length != 1) {
System.out.println("未找到参数或参数格式不正确!");
}
FurnitureFactory factory = FurnitureFactory.getFactory(args[0]);
Chair chair = factory.createChair();
Table table = factory.createTable();
System.out.println("当前椅子有" + chair.getLegsNum() + "条腿,高为" + chair.getHeight() + "!");
System.out.println("当前桌子的桌面面积为" + table.getSize() + ",高为" + table.getHeight() + "!");
}
}

输出结果

1
2
3
# 执行参数为 example.factory.impl.ModernFurnitureFactoryImpl 时
当前椅子有4条腿,高为70.0!
当前桌子的桌面面积为200,高为70.0!
1
2
3
# 执行参数为 example.factory.impl.VictorianFurnitureFactoryImpl 时
当前椅子有4条腿,高为50.0!
当前桌子的桌面面积为100,高为50.0!

从上面代码可以看出抽象工厂主要是用来生成抽象产品的,而具体工厂则是用来生成同一系列的具体产品。并且在编写代码时,我是先编写测试代码再编写具体实现的。这也印证了抽象工厂模式不需要考虑具体实现,使用与实现分开的优点。


常用场景和解决方案

  • 不同系列产品有较多共性特征时,使用抽象工厂模式有助于提高组件复用性。
  • 解决跨平台带来的兼容性问题。
  • 知道相关产品但是不知道具体实现,使用抽象工厂模式可以让你在以后任意添加新的实现而不变更使用代码。例如在注入时,不需要修改使用代码,就能更改注入方式等。

模式的优缺点

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

拓展知识

  • 生成器重点关注如何分步生成复杂对象。抽象工厂专门用于生产一系列相关对象。 抽象工厂会马上返回产品,生成器则允许你在获取产品前执行一些额外构造步骤。
  • 在许多设计工作的初期都会使用工厂方法模式(较为简单, 而且可以更方便地通过子类进行定制),随后演化为使用抽象工厂模式、原型模式或生成器模式(更灵活但更加复杂)。


🔙 设计模式

📌最后:希望本文能够给您提供帮助,文章中有不懂或不正确的地方,请在下方评论区💬留言!

🔗参考文献:

🌐 设计模式 –refactoringguru

▶️ bilibili-趣学设计模式;黄靖锋. –拉勾教育

📖 图解设计模式 /(日)结城浩著;杨文轩译. –北京:人民邮电出版社,2017.1