Go語言實現-觀察者模式


前前言

這個類經過我的正式投入使用啊,發現不對勁,這樣做可能會導致線程死鎖

比如你dispatch一個event,然后在這個回調里把那個事件的偵聽給remove掉了,那么就會導致線程死鎖(這個問題找了好久啊,剛剛調試的時候才發現了)

還有就是獲取func的引用的問題,golang那半c半java的語法,我改用了新的方法

源碼已經修改!

前言:

呀,學Go語言兩周了,感覺上手挺快的,golang雖然和c語言很想,但是避免掉了很多指針相關的東東,所以學起來特別輕松。

但是途中坎坷頗多啊,資料是少之又少啊,搜索引擎都搜不到啥東西,唯有golang.orggithub的ebook上才有比較完整的資料,加了個golang群么,大牛都潛水不說話,然后我瞎了,只能自己琢磨。

也是自己鬧着玩吧,最近想寫一個服務器(目前已經能同步移動了),我也想學一門后端語言,於是就選了google的go語言了,聽說並發性能挺好的。

然而,golang這語言貌似都是被用作web服務開發了的,群里一般都是在討論web開發的問題,而我是做socket開發的,當然,golang里面不叫socket。

工作比較忙,只有下班后那點時間來學golang了,學得比較基礎,大牛就當走走場好了。

正題:

我是一名頁游前端開發人員,當然我是as3開發者,對as3的觀察者模式-事件機制,那是太依賴了,而golang里面原生並不提供這種機制。

golang有的只是十分相似的goroutine,也就是底層支持的並發機制,然后線程間通訊就是channel,好比於as3中的Event,當然不能直接比較,要封裝過。

觀察者模式就是指一對多的依賴關系,生產者分派消息,消費者全都能收到消息(全局觀察模式),這樣,可以降低模塊間的耦合度,我們要做的,就是來管理這三者。

然后,用golang來實現這一設計模式是很簡單的,我僅用了一百多行,就簡單地實現了,直接看代碼吧:

 

  1 package tbs
  2 
  3 import (
  4     //"fmt"
  5     "unsafe"
  6 )
  7 
  8 type Dispatcher struct {
  9     listeners map[string]*EventChain
 10 }
 11 
 12 type EventChain struct {
 13     chs       []chan *Event
 14     callbacks []*EventCallback
 15 }
 16 
 17 func createEventChain() *EventChain {
 18     return &EventChain{chs: []chan *Event{}, callbacks: []*EventCallback{}}
 19 }
 20 
 21 type Event struct {
 22     eventName string
 23     Params    map[string]interface{}
 24 }
 25 
 26 func CreateEvent(eventName string, params map[string]interface{}) *Event {
 27     return &Event{eventName: eventName, Params: params}
 28 }
 29 
 30 type EventCallback func(*Event)
 31 
 32 var _instance *Dispatcher
 33 
 34 func SharedDispatcher() *Dispatcher {
 35     if _instance == nil {
 36         _instance = &Dispatcher{}
 37         _instance.Init()
 38     }
 39 
 40     return _instance
 41 }
 42 
 43 func (this *Dispatcher) Init() {
 44     this.listeners = make(map[string]*EventChain)
 45 }
 46 
 47 func (this *Dispatcher) AddEventListener(eventName string, callback *EventCallback) {
 48     eventChain, ok := this.listeners[eventName]
 49     if !ok {
 50         eventChain = createEventChain()
 51         this.listeners[eventName] = eventChain
 52     }
 53 
 54     exist := false
 55     //fmt.Println("add len:", len(eventChain.callbacks))
 56     for _, item := range eventChain.callbacks {
 57         a := *(*int)(unsafe.Pointer(item))
 58         b := *(*int)(unsafe.Pointer(callback))
 59         //fmt.Println("add", a, b)
 60         if a == b {
 61             exist = true
 62             break
 63         }
 64     }
 65 
 66     if exist {
 67         return
 68     }
 69 
 70     ch := make(chan *Event)
 71 
 72     eventChain.chs = append(eventChain.chs[:], ch)
 73     eventChain.callbacks = append(eventChain.callbacks[:], callback)
 74 
 75     go this.handler(eventName, ch, callback)
 76 }
 77 
 78 func (this *Dispatcher) handler(eventName string, ch chan *Event, callback *EventCallback) {
 79     //fmt.Printf("add listener: %s\n", eventName)
 80     //fmt.Println("chan: ", ch)
 81     for {
 82         event := <-ch
 83         //fmt.Println("event out:", eventName, event, ch)
 84         if event == nil {
 85             break
 86         }
 87         go (*callback)(event)
 88     }
 89 }
 90 
 91 func (this *Dispatcher) RemoveEventListener(eventName string, callback *EventCallback) {
 92     eventChain, ok := this.listeners[eventName]
 93     if !ok {
 94         return
 95     }
 96 
 97     var ch chan *Event
 98     exist := false
 99     key := 0
100     for k, item := range eventChain.callbacks {
101         a := *(*int)(unsafe.Pointer(item))
102         b := *(*int)(unsafe.Pointer(callback))
103         //fmt.Println("remove", a, b)
104         if a == b {
105             exist = true
106             ch = eventChain.chs[k]
107             key = k
108             break
109         }
110     }
111 
112     if exist {
113         //fmt.Printf("remove listener: %s\n", eventName)
114         //fmt.Println("chan: ", ch)
115         ch <- nil
116 
117         eventChain.chs = append(eventChain.chs[:key], eventChain.chs[key+1:]...)
118         eventChain.callbacks = append(eventChain.callbacks[:key], eventChain.callbacks[key+1:]...)
119         //fmt.Println(len(eventChain.chs))
120     }
121 }
122 
123 func (this *Dispatcher) DispatchEvent(event *Event) {
124     eventChain, ok := this.listeners[event.eventName]
125     if ok {
126         ////fmt.Printf("dispatch event: %s\n", event.eventName)
127         for _, chEvent := range eventChain.chs {
128             chEvent <- event
129         }
130     }
131 }

 

 

這個類里定義了三個結構,Dispatcher:分派器主類,Event:事件類,EventChain:事件鏈類

如果你要使用這個類,那你只要那Dispatcher的單例方法:

 

SharedDispatcher()

 

來進行操作好了

要創建Event,你是要使用創建方法

 

CreateEvent(eventNamestring,paramsmap[string]interface{})

 

來創建

當然,demo還得貼上

 

 1 package main
 2 
 3 import (
 4     "fmt"
 5     "tbs"
 6     "time"
 7 )
 8 
 9 type MClass struct {
10     dispatcher tbs.Dispatcher
11 }
12 
13 func main() {
14     mc := &MClass{}
15     mc.Start()
16 }
17 
18 func (this *MClass) Start() {
19     //獲取分派器單例
20     dispatcher := tbs.SharedDispatcher()
21 
22     //添加監聽1
23     var fun1 tbs.EventCallback = this.onTest
24     dispatcher.AddEventListener("test", &fun1)
25 
26     //再添加監聽2
27     var fun2 tbs.EventCallback = this.onTest2
28     dispatcher.AddEventListener("test", &fun2)
29 
30     //隨便弄個事件攜帶的參數,我把參數定義為一個map
31     params := make(map[string]interface{})
32     params["id"] = 1000
33     //創建一個事件對象
34     event := tbs.CreateEvent("test", params)
35     //把事件分派出去
36     dispatcher.DispatchEvent(event)
37 
38     //移除監聽1
39     dispatcher.RemoveEventListener("test", &fun1)
40 
41     //再把事件分派出去一次
42     dispatcher.DispatchEvent(event)
43 
44     //因為主線程不會等子線程而直接關閉進程,這樣會看不到效果,所以我在這里加了阻塞式延時
45     time.Sleep(time.Second * 1)
46 }
47 
48 //回調出得到的就是一個event對象了
49 func (this *MClass) onTest(event *tbs.Event) {
50     fmt.Println("onTest", event.Params["id"])
51 }
52 
53 func (this *MClass) onTest2(event *tbs.Event) {
54     fmt.Println("onTest2", event.Params["id"])
55 }

 

 

輸出結果:

add listener: test
add listener: test
dispatch event: test
onTest 1000
remove listener: test
dispatch event: test
onTest2 1000
onTest2 1000
成功: 進程退出代碼 0.

哈哈,成功地運行了。

demo你面的注釋已經非常詳盡了,看不懂就在下面問我好了!

昨晚我拿他來封裝了一下golang的socket,改成了事件驅動,耦合度瞬間降低了很多。

 

 1 func onServerStarted(event *tbs.Event) {
 2     fmt.Println("server started.")
 3 }
 4 
 5 func onAccept(event *tbs.Event) {
 6     socket := (event.Params["socket"]).(*tbs.Socket)
 7 
 8     fmt.Printf("client[#%d] connect on:%s\n", socket.Sign, socket.Conn.RemoteAddr().String())
 9 }
10 
11 func onData(event *tbs.Event) {
12     socket := (event.Params["socket"]).(*tbs.Socket)
13     bytes := (event.Params["bytes"]).([]byte)
14 
15     fmt.Printf("[#%d]:", socket.Sign)
16     fmt.Println(bytes)
17 }
18 
19 func onClosed(event *tbs.Event) {
20     socket := (event.Params["socket"]).(*tbs.Socket)
21     fmt.Printf("[#%d] closed\n", socket.Sign)
22 }

 

我還是模仿了as3提供的socket,看如上四個回調,只要監聽並開啟了serversocket,那么我只要坐等這四個回調來處理游戲的邏輯即可,每個socket都綁有累加的Sign作為標識。

 

總結:

golang是一門不錯的語言,特別靈活,反射也很方便,應該會火吧,希望國內能有更多golang的開發者社區能建立起來吧!

貼上我自己的博客地址:http://blog.codeforever.net/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM