简单工厂

生产某一种抽象产品(水果),根据输入不同(apple, banana),生产不同的实现(苹果、香蕉)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//一个工厂, 有一个生产水果的机器,返回一个抽象水果的指针
type Factory struct {}

func (fac *Factory) CreateFruit(kind string) Fruit {
var fruit Fruit

if kind == "apple" {
fruit = new(Apple)
} else if kind == "banana" {
fruit = new(Banana)
} else if kind == "pear" {
fruit = new(Pear)
}

return fruit
}

对象的创建更方便。

缺点:

  1. 对工厂类职责过重,一旦不能工作,系统受到影响。
  2. 增加系统中类的个数,复杂度和理解度增加。
  3. 违反“开闭原则”,添加新产品需要修改工厂逻辑,工厂越来越复杂。

适用场景:

  1. 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
  2. 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

工厂方法

上面违反了开闭原则。那么就需要把工厂也抽象一下:

1
2
3
4
5
6
7
8
9
10
11
12
type AppleFactory struct {
AbstractFactory
}

func (fac *AppleFactory) CreateFruit() Fruit {
var fruit Fruit

//生产一个具体的苹果
fruit = new(Apple)

return fruit
}

这样新增一种水果,就新增这个水果的工厂。

拓展性好,也符合开闭原则。

缺点:

  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
39
40
41
package main

import "fmt"

/*
三个要点:
一是某个类只能有一个实例;
二是它必须自行创建这个实例;
三是它必须自行向整个系统提供这个实例。
*/

/*
保证一个类永远只能有一个对象
*/


//1、保证这个类非公有化,外界不能通过这个类直接创建一个对象
// 那么这个类就应该变得非公有访问 类名称首字母要小写
type singelton struct {}

//2、但是还要有一个指针可以指向这个唯一对象,但是这个指针永远不能改变方向
// Golang中没有常指针概念,所以只能通过将这个指针私有化不让外部模块访问
var instance *singelton = new(singelton)

//3、如果全部为私有化,那么外部模块将永远无法访问到这个类和对象,
// 所以需要对外提供一个方法来获取这个唯一实例对象
// 注意:这个方法是否可以定义为singelton的一个成员方法呢?
// 答案是不能,因为如果为成员方法就必须要先访问对象、再访问函数
// 但是类和对象目前都已经私有化,外界无法访问,所以这个方法一定是一个全局普通函数
func GetInstance() *singelton {
return instance
}

func (s *singelton) SomeThing() {
fmt.Println("单例对象的某方法")
}

func main() {
s := GetInstance()
s.SomeThing()
}

改成懒汉式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var lock sync.Mutex

func GetInstance() *singelton {
//为了线程安全,增加互斥
lock.Lock()
defer lock.Unlock()

//只有首次GetInstance()方法被调用,才会生成这个单例的实例
if instance == nil {
instance = new(singelton)
return instance
}

//接下来的GetInstance直接返回已经申请的实例即可
return instance
}

但是这个实现里,每次拿实例都要锁,很浪费。可以使用 Go 的 Once 类型保证某一段代码只会执行一次:

1
2
3
4
5
6
7
8
9
var once sync.Once
func GetInstance() *singelton {

once.Do(func(){
instance = new(singelton)
})

return instance
}

详细了解 Once 的原理:

1
2
3
4
5
6
7
8
9
10
11
12
func (o *Once) Do(f func()) {   //判断是否执行过该方法,如果执行过则不执行
if atomic.LoadUint32(&o.done) == 1 {
return
}
// Slow-path.
o.m.Lock()
defer o.m.Unlock()  
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}

原来是用了原子的一个数字。执行过就标记为 1,否则就是 0