概述

工厂模式中,使用一个公共的接口来创建对象,将创建逻辑封装在工厂类中,由工厂类负责各种对象的实例化。

工厂模式有如下优点:

  1. 若创建逻辑可能有所变动,将其封装在独立的工厂类中,可以实现代码复用。
  2. 创建逻辑不会暴露给调用者,亦即调用者无需了解创建对象的细节,实现对象的创建与使用之间的解耦合。
  3. 将创建逻辑抽离出来,使各函数职能专一化,简化代码。

工厂模式一般细分为三种具体的类型:简单工厂工厂方法抽象工厂,其抽象度依次提高。

简单工厂模式

定义

简单工厂模式(不属于23种设计模式)使用一个工厂类,该类根据参数的不同返回不同类型的实例,一般情况下被创建的实例往往拥有相同的父类(或实现了相同的接口)。
此外,由于简单工厂模式使用静态的工厂方法,因此又称为静态工厂模式

优点

  • 根据传入的不同参数,工厂类可以动态地选择创建哪种实例,调用方只需要负责”消费“即可。

缺点

  • 工厂类扩展困难,一旦需要新增产品就必须修改工厂类的逻辑,违反了开闭原则。(对扩展开放,对修改关闭)。
  • 随着产品种类的增多,简单工厂会变得愈加臃肿,为了解决这个问题,产生出了抽象工厂模式。

代码实现

与C++,Java等语言不同,Go语言没有构造函数,所以一般使用NewXxx函数来实现简单工厂,通过返回一个接口来实例化对象。

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
package factory  

import "fmt"

// ConfigParser interfacetype ConfigParser interface {
Parse(data []byte)
}

// JsonConfigParser json config parsertype JsonConfigParser struct{}

// Parse parse json configfunc (j JsonConfigParser) Parse(data []byte) {
// parse json
fmt.Println("implement json config parser")
}

// YamlConfigParser yaml config parsertype YamlConfigParser struct{}

// Parse parse yaml configfunc (y YamlConfigParser) Parse(data []byte) {
// parse yaml
fmt.Println("implement yaml config parser")
}

// XmlConfigParser xml config parsertype XmlConfigParser struct{}

// Parse parse xml configfunc (x XmlConfigParser) Parse(data []byte) {
// parse xml
fmt.Println("implement xml config parser")
}

// NewConfigParser create config parser by typefunc NewConfigParser(t string) ConfigParser {
switch t {
case "json":
return JsonConfigParser{}
case "yaml":
return YamlConfigParser{}
case "xml":
return XmlConfigParser{}
default:
return nil
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package factory  

import (
"reflect" "testing")

func TestNewConfigParser(t *testing.T) {
tests := []struct {
name string
want ConfigParser
}{
{name: "json", want: JsonConfigParser{}},
{name: "xml", want: XmlConfigParser{}},
{name: "yaml", want: YamlConfigParser{}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewConfigParser(tt.name); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewConfigParser() = %v, want %v", got, tt.want)
}
})
}
}

工厂方法模式

定义

工厂方法模式(Factory Method)又称多态性工厂模式,属于23种设计模式的创建型模式。工厂方法模式定义了一个创建对象的“接口”,由实现这个接口的工厂类(子类)决定实例化哪个产品类,将类的实例化推迟到子类中进行。

原先的工厂不再负责具体的实例化,仅负责给出具体工厂子类必须实现的接口。

优点

  • 扩展性高,当需要新增一个产品时,只要扩展一个工厂类即可,符合开闭原则
  • 用户只需要关心产品对应的工厂即可,无需关心创建细节。
  • 在产品创建细节复杂的时候,将其分离到单独的具体工厂中,不至于让单个工厂类的逻辑过于复杂。

缺点

  • 在增加产品时,要同时增加产品类和对应的具体工厂类,这会使类的数量过多,系统的复杂度增高。
  • 只能生产一类产品,如果需要生产多种不同类的产品,请使用抽象工厂模式

代码实现

很遗憾,Golang没有“继承”这一概念,在Go中实现“工厂方法模式”,采用让具体工厂类直接实现工厂接口的方法。

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
package factory  

// 产品类实现位于simple_factory.go文件中

// ConfigParserFactory 工厂方法接口
type ConfigParserFactory interface {
Create() ConfigParser
}

// JsonConfigParserFactory 实现工厂方法接口的具体工厂
type JsonConfigParserFactory struct{}

// Create 具体工厂的创建方法
func (f JsonConfigParserFactory) Create() ConfigParser {
return JsonConfigParser{}
}

// YamlConfigParserFactory 实现工厂方法接口的具体工厂
type YamlConfigParserFactory struct{}

// Create 具体工厂的创建方法
func (f YamlConfigParserFactory) Create() ConfigParser {
return YamlConfigParser{}
}

// XmlConfigParserFactory 实现工厂方法接口的具体工厂
type XmlConfigParserFactory struct{}

// Create 具体工厂的创建方法
func (f XmlConfigParserFactory) Create() ConfigParser {
return XmlConfigParser{}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package factory  

import (
"testing")

func TestConfigParserFactory(t *testing.T) {
tests := []struct {
name string
factory ConfigParserFactory
want ConfigParser
}{
{name: "json", factory: JsonConfigParserFactory{}, want: JsonConfigParser{}},
{name: "xml", factory: YamlConfigParserFactory{}, want: YamlConfigParser{}},
{name: "yaml", factory: XmlConfigParserFactory{}, want: XmlConfigParser{}},
}
for _, tt := range tests {
got := tt.factory.Create()
if got != tt.want {
t.Errorf("%s Create() = %v, want %v", tt.name, got, tt.want)
}
}
}

抽象工厂模式

定义

抽象工厂模式(Abstract Factory)提供一个创建一系列相关或相互依赖对象的接口,而无须指定所要产品的具体类。

举例

考虑一个生产家具的场景:

  • 我们需要关注3种产品:椅子、茶几、沙发
  • 这3种产品必须配套:必须均为简约风格、美式风格或日式风格
  • 同一种风格的产品在一起生产(显而易见的道理)
  • 用户往往只关心某一套风格的家具的订购和使用

现在考虑如何实现上述要求:

  1. 首先,需要声明抽象工厂,该工厂包含所有产品的构造方法的接口。
    例如:CreateChairCreateSofaCreateCoffee­Table
    这些方法返回抽象产品,即他们无论风格如何,都是椅子、茶几或沙发。
  2. 现在基于不同风格,但是成套的产品,分别声明其具体工厂,这些工厂分别负责生产某一风格的一套家具。
    例如:现代家具工厂Modern­Furniture­Factory只能创建现代椅子Modern­Chair 、​现代沙发Modern­Sofa 和现代咖啡桌Modern­Coffee­Table产品;其他工厂类似。
    具体工厂负责生产具体产品以供用户使用。
  3. 接下来,用户只需要看重某一风格的家具即可成套购买。
    用户无需关心具体的工厂类,他们只关心自己要获取的是一套家具而已。
    至于家具如何生产,那是工厂要干的事。。。

另外,显然用户明确知道家具(抽象产品)是干什么的——可以坐下、放茶杯等等,即抽象产品实现了哪些方法。
还有一件事,由于用户只关心抽象接口,因此需要根据配置文件等信息来选择具体工厂。

抽象工厂实现

根据上面的例子进行抽象:

  • 产品族:即彼此之间有一定联系的产品,例如上例中的椅子、茶几或沙发共同组成一套家具。
  • 不同等级产品:即同族中,各个不同的产品,例如同一风格的椅子、茶几或沙发属于3个不同等级的产品。
    接下来实现整体的抽象工厂模式:
  • 抽象工厂(Abstract Factory):提供了创建抽象产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义产品的规范,描述产品的功能接口,抽象工厂可以创建多个抽象产品。
  • 具体产品(ConcreteProduct):实现了抽象产品所定义的接口,由具体工厂来创建。

优点

  • 同一工厂生产的产品相互匹配,即属于同一族。
  • 客户端无需关心创建细节,通过抽象接口操作实例。
  • 满足开闭原则: 向程序中引入新产品时,无需修改客户端代码。

缺点

  • 代码结构愈加复杂。
  • 新产品的加入非常困难:抽象工厂确定了产品集合,并且规定了工厂接口,新增产品涉及到所有工厂的接口扩展。

代码实现

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package factory

import "fmt"

/******************************************
* Abstract products and abstract factory
******************************************/

// Chair is an abstract product
type Chair interface {
Sit()
}

// Table is an abstract product
type Table interface {
PutDown()
}

// Sofa is an abstract product
type Sofa interface {
Sit()
}

// FurnitureFactory is an abstract factory
type FurnitureFactory interface {
CreateChair() Chair
CreateTable() Table
CreateSofa() Sofa
}

/******************************************
* Concrete products and concrete factory of modern furniture
******************************************/

// ModernChair is a concrete product of modern furniture
type ModernChair struct{}

func (c ModernChair) Sit() {
fmt.Println("Sitting on modern chair")
}

// ModernTable is a concrete product of modern furniture
type ModernTable struct{}

func (t ModernTable) PutDown() {
fmt.Println("Putting down modern table")
}

// ModernSofa is a concrete product of modern furniture
type ModernSofa struct{}

func (s ModernSofa) Sit() {
fmt.Println("Sitting on modern sofa")
}

// ModernFurnitureFactory is a concrete factory
type ModernFurnitureFactory struct{}

func (f ModernFurnitureFactory) CreateChair() Chair {
return ModernChair{}
}
func (f ModernFurnitureFactory) CreateTable() Table {
return ModernTable{}
}
func (f ModernFurnitureFactory) CreateSofa() Sofa {
return ModernSofa{}
}

/******************************************
* Concrete products and concrete factory of concise furniture
******************************************/

type ConciseChair struct{}

func (c ConciseChair) Sit() {
fmt.Println("Sitting on concise chair")
}

type ConciseTable struct{}

func (t ConciseTable) PutDown() {
fmt.Println("Putting down concise table")
}

type ConciseSofa struct{}

func (s ConciseSofa) Sit() {
fmt.Println("Sitting on concise sofa")
}

type ConciseFurnitureFactory struct{}

func (f ConciseFurnitureFactory) CreateChair() Chair {
return ConciseChair{}
}
func (f ConciseFurnitureFactory) CreateTable() Table {
return ConciseTable{}
}
func (f ConciseFurnitureFactory) CreateSofa() Sofa {
return ConciseSofa{}
}

/******************************************
* GetFactory will choose the concrete factory
******************************************/

// GetFactory will choose the concrete factory
func GetFactory(furnitureType string) FurnitureFactory {
switch furnitureType {
case "modern":
return ModernFurnitureFactory{}
case "concise":
return ConciseFurnitureFactory{}
default:
return nil
}
}

测试:

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
package factory  

import "testing"

func TestAbstractFactory(t *testing.T) {
type Unit struct {
name string
want []interface{}
}
tests := []Unit{
{
name: "modern",
want: []interface{}{ModernChair{}, ModernSofa{}, ModernTable{}},
},
{
name: "concise",
want: []interface{}{ConciseChair{}, ConciseSofa{}, ConciseTable{}},
},
}
for _, tt := range tests {
factory := GetFactory(tt.name)
got := []interface{}{
factory.CreateChair(),
factory.CreateSofa(),
factory.CreateTable(),
}
for i, v := range got {
if v != tt.want[i] {
t.Errorf("%s got %T, want %T", tt.name, v, tt.want[i])
}
}
}
}