简单工厂
生产某一种抽象产品(水果),根据输入不同(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 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 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"
type singelton struct {}
var instance *singelton = new(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() if instance == nil { instance = new(singelton) return instance }
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 } o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() } }
|
原来是用了原子的一个数字。执行过就标记为 1,否则就是 0