Go 语言自带设计模式( 二 )

 

Go 语言自带设计模式

文章插图
 
Go FUNCTIONAL OPTIONS 模式
观察者模式在对象间定义一个一对多的联系性,由此当一个对象改变了状态,所有其他相关的对象会被通知并且自动刷新 。
如果用传统的方法实现 观察者模式,对应的 Go 语言代码大致是下面这个样子:
package mainimport "math"// Observer 观察者接口type Observer interface {OnNotify(Event)}// Notifier 订阅接口type Notifier interface {Register(Observer)Deregister(Observer)Notify(Event)}type (Event struct {Data int64}eventObserver struct {id int}eventNotifier struct {observers map[Observer]struct{}})// OnNotify 观察者收到订阅的时间回调func (o *eventObserver) OnNotify(e Event) {}// Register 注册观察者func (o *eventNotifier) Register(l Observer) {o.observers[l] = struct{}{}}// Deregister 移除观察者func (o *eventNotifier) Deregister(l Observer) {delete(o.observers, l)}// Notify 发出通知func (o *eventNotifier) Notify(e Event) {for p := range o.observers {p.OnNotify(e)}}func main() {// 调用方代码notifier := eventNotifier{observers: make(map[Observer]struct{}),}notifier.Register(&eventObserver{1})notifier.Register(&eventObserver{2})notifier.Register(&eventObserver{3})notifier.Notify(Event{Data: math.MaxInt64})} 
Go 语言自带设计模式

文章插图
 
Go 实现观察者模式
但其实我们有更简洁的方法,直接使用标准库中的 sync.Cond 对象,改造之后的 观察者模式 代码大概是这个样子:
 
package mainimport ("fmt""sync""time")var done = falsefunc read(name string, c *sync.Cond) {fmt.Println(name, "starts reading")c.L.Lock()for !done {c.Wait() // 等待发出通知}c.L.Unlock()}func write(name string, c *sync.Cond) {fmt.Println(name, "starts writing")time.Sleep(100 * time.Millisecond)c.L.Lock()done = true // 设置条件变量c.L.Unlock()fmt.Println(name, "wakes all")c.Broadcast() // 通知所有观察者}func main() {cond := sync.NewCond(&sync.Mutex{}) // 创建时传入一个互斥锁// 3 个观察者go read("reader1", cond)go read("reader2", cond)go read("reader3", cond)time.Sleep(time.Second) // 模拟延时write("writer-1", cond) // 发出通知time.Sleep(time.Second) // 模拟延时} 
Go 语言自带设计模式

文章插图
 
Go 标准库观察者模式
将代码改造为 sync.Cond 之后,代码量更好,结构更简洁 。
ok/error 模式在 Go 语言中,经常在一个表达式返回 2 个参数时使用这种模式:
  • 第 1 个参数是一个值或者 nil
  • 第 2 个参数是 true/false 或者 error
在一个需要赋值的 if 条件语句中,使用这种模式去检测第 2 个参数值会让代码显得优雅简洁 。
在函数返回时检测错误 
package mainfunc foo() (int, error){return 0, nil}func main() {if v, err := foo(); err != nil {panic(err)} else {println(v)}} 
检测 map 是否存在指定的 key 
package mainfunc main() {m := make(map[int]string)if v, ok := m[0]; ok {println(v)}} 
类型断言 
package mainfunc foo() interface{} {return 1024}func main() {n := foo()if v, ok := n.(int); ok {println(v)}} 
检测通道是否关闭 
package mainfunc main() {ch := make(chan int)go func() {for i := 0; i < 5; i++ {ch <- i}close(ch)}()for {if v, ok := <-ch; ok {println(v)} else {return}}}// $ go run main.go// 输出如下// 0// 1// 2// 3// 4 
附加内容闭包有时候,我们可以利用 闭包 实现一些短小精悍的内部函数 。
计数器package mainfunc main() {newSeqInc := func() func() int {seq := 0return func() int {seq++return seq}}seq := newSeqInc() // 创建一个计数器println(seq())// 1println(seq())// 2println(seq())// 3seq2 := newSeqInc() // 创建另一个计数器println(seq2())// 1println(seq2())// 2println(seq2())// 3} 
小结下面表格列出了常用的 设计模式,其中 Go 标准库自带的 模式 已经用删除线标识,读者可以和自己常用的 设计模式 进行对比 。
创建型模式
结构性模式
行为型模式


推荐阅读