抽象工厂是一个接口,包含一系列创建对象的抽象方法,这些对象在逻辑上相关。 由抽象工厂的不同实现类实现对象的具体创建逻辑。

对比工厂方法模式,抽象工厂的定义变为"提供一个创建一系列相关对象的接口",不再是工厂方法中"定义一个用于创建对象的接口"。 一个工厂有了创建多个不同类型对象的能力。

抽象工厂

使用场景

  • 代码需要与多个不同系列的相关产品交互,但是无法提前获取相关信息或者出于对未来扩展性的考虑,不希望代码基于产品的具体类进行构建。在这种情况下可以使用抽象工厂。
    • 抽象工厂提供了一系列接口,可用于创建每个系列产品的对象。只要代码通过该接口创建对象,就不会生成与应用程序已生成的产品类型不一致的产品。
  • 如果有一个基于一组抽象方法的类,且其主要功能因此变得不明确,在这种情况下可以考虑使用抽象工厂模式。
    • 在设计良好的程序中,每个类仅负责一件事
    • 如果一个类与多种类型产品交互,就可以考虑将工厂方法抽取到独立的工厂类或具备完整功能的抽象工厂类中。

实现步骤

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

优缺点

优点

  • 可以确保同一工厂生成的产品相互匹配。
  • 可以避免客户端和具体产品代码的耦合。
  • 单一职责原则。可以将产品生成代码抽取到同一位置,使得代码易于维护。
  • 开闭原则。向应用程序中引入新产品变体时,无需修改客户端代码。

缺点

  • 需要向应用中引入众多接口和类, 代码可能会比之前更加复杂

与其它模式的关系

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

示例

学生管理系统有两个表,学生表和成绩表。 学生表有插入、更新操作;成绩表有插入、列表操作。 之前的存储是Access,现在要支持MySQL,后面会支持Oracle。

这种情况工厂模式就不适合了:

  1. 工厂模式返回的Product都有同一个父类,Product因为多态可以被随便替换。但学生和成绩类操作不同,强制设置一个类并不合理。
  2. 即使强制出一个父类,会导致生成4个工厂。如果表个数有N个,数据库支持M种,工厂的数量就是N*M,导致工厂个数剧增。

这个例子中,有两个维度:

  1. 低维度的是学生类和成绩类,无论是哪种存储,各自的操作是一致的,所以可以使用继承
  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
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import "fmt"

// Student 产品1接口
type Student interface {
	insert() bool
	update() bool
}

// AccessStudent 产品1的实现类1
type AccessStudent struct {
}

func (a AccessStudent) insert() bool {
	fmt.Println("AccessStudent insert")
	return true
}

func (a AccessStudent) update() bool {
	fmt.Println("AccessStudent update")
	return true
}

// MySQLStudent 产品1的实现类2
type MySQLStudent struct {
}

func (m *MySQLStudent) insert() bool {
	fmt.Println("MySQLStudent insert")
	return true
}

func (m *MySQLStudent) update() bool {
	fmt.Println("MySQLStudent update")
	return true
}

 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
// Score 产品2的接口
type Score interface {
	insert() bool
	list() []int64
}

// AccessScore 产品2的实现1
type AccessScore struct {
}

func (a *AccessScore) insert() bool {
	fmt.Println("AccessScore insert")
	return true
}

func (a *AccessScore) list() []int64 {
	fmt.Println("AccessScore list")
	return []int64{1, 2}
}

// MySQLScore 产品2的实现2
type MySQLScore struct {
}

func (m *MySQLScore) insert() bool {
	fmt.Println("MySQLScore insert")
	return true
}

func (m *MySQLScore) list() []int64 {
	fmt.Println("MySQLScore list")
	return []int64{1, 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
26
27
28
29
30
// Factory 抽象工厂接口,定义生产产品的方法
type Factory interface {
	createStudent() Student
	createScore() Score
}

// AccessFactory 实现1方式的工厂
type AccessFactory struct {
}

func (a *AccessFactory) createStudent() Student {
	return &AccessStudent{}
}

func (a *AccessFactory) createScore() Score {
	return &AccessScore{}
}

// MySQLFactory 实现2方式的工厂
type MySQLFactory struct {
}

func (m *MySQLFactory) createStudent() Student {
	return &MySQLStudent{}
}

func (m *MySQLFactory) createScore() Score {
	return &MySQLScore{}
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 根据参数生产某一类工厂
func getFactory(storeType string) Factory {
	switch storeType {
	case "MySQL":
		return &MySQLFactory{}
	case "Access":
		return &AccessFactory{}
	}
	return nil
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

func main() {
	factory := getFactory("MySQL")
	if factory == nil {
		fmt.Println("不支持该存储方式")
		return
	}

	student := factory.createStudent()
	score := factory.createScore()

	student.insert()
	student.update()
	score.insert()
	score.list()
}

抽象工厂模式能减少工厂类的创建数量,体现了如何管理、分类信息的思想。 符合

  • 单一职责原则,每个表一个类
  • 里氏替换原则,使用继承无论是工厂还是产品,子类对象能够替换父类对象出现的任何地方
  • 依赖倒置原则,工厂类和抽象产品在一个层级关联,而不是和细节关联

对于开闭原则,增加新的类和新的存储方式,扩展是开放的;违背封闭的位置在getFactory函数和增加Factory接口中的函数,不过变动很小。

References

guru