Go语言实现-观察者模式

时间:2024-01-08 17:38:33

前前言

这个类经过我的正式投入使用啊,发现不对劲,这样做可能会导致线程死锁

比如你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来实现这一设计模式是很简单的,我仅用了一百多行,就简单地实现了,直接看代码吧:

 package tbs

 import (
//"fmt"
"unsafe"
) type Dispatcher struct {
listeners map[string]*EventChain
} type EventChain struct {
chs []chan *Event
callbacks []*EventCallback
} func createEventChain() *EventChain {
return &EventChain{chs: []chan *Event{}, callbacks: []*EventCallback{}}
} type Event struct {
eventName string
Params map[string]interface{}
} func CreateEvent(eventName string, params map[string]interface{}) *Event {
return &Event{eventName: eventName, Params: params}
} type EventCallback func(*Event) var _instance *Dispatcher func SharedDispatcher() *Dispatcher {
if _instance == nil {
_instance = &Dispatcher{}
_instance.Init()
} return _instance
} func (this *Dispatcher) Init() {
this.listeners = make(map[string]*EventChain)
} func (this *Dispatcher) AddEventListener(eventName string, callback *EventCallback) {
eventChain, ok := this.listeners[eventName]
if !ok {
eventChain = createEventChain()
this.listeners[eventName] = eventChain
} exist := false
//fmt.Println("add len:", len(eventChain.callbacks))
for _, item := range eventChain.callbacks {
a := *(*int)(unsafe.Pointer(item))
b := *(*int)(unsafe.Pointer(callback))
//fmt.Println("add", a, b)
if a == b {
exist = true
break
}
} if exist {
return
} ch := make(chan *Event) eventChain.chs = append(eventChain.chs[:], ch)
eventChain.callbacks = append(eventChain.callbacks[:], callback) go this.handler(eventName, ch, callback)
} func (this *Dispatcher) handler(eventName string, ch chan *Event, callback *EventCallback) {
//fmt.Printf("add listener: %s\n", eventName)
//fmt.Println("chan: ", ch)
for {
event := <-ch
//fmt.Println("event out:", eventName, event, ch)
if event == nil {
break
}
go (*callback)(event)
}
} func (this *Dispatcher) RemoveEventListener(eventName string, callback *EventCallback) {
eventChain, ok := this.listeners[eventName]
if !ok {
return
} var ch chan *Event
exist := false
key :=
for k, item := range eventChain.callbacks {
a := *(*int)(unsafe.Pointer(item))
b := *(*int)(unsafe.Pointer(callback))
//fmt.Println("remove", a, b)
if a == b {
exist = true
ch = eventChain.chs[k]
key = k
break
}
} if exist {
//fmt.Printf("remove listener: %s\n", eventName)
//fmt.Println("chan: ", ch)
ch <- nil eventChain.chs = append(eventChain.chs[:key], eventChain.chs[key+:]...)
eventChain.callbacks = append(eventChain.callbacks[:key], eventChain.callbacks[key+:]...)
//fmt.Println(len(eventChain.chs))
}
} func (this *Dispatcher) DispatchEvent(event *Event) {
eventChain, ok := this.listeners[event.eventName]
if ok {
////fmt.Printf("dispatch event: %s\n", event.eventName)
for _, chEvent := range eventChain.chs {
chEvent <- event
}
}
}

这个类里定义了三个结构,Dispatcher:分派器主类,Event:事件类,EventChain:事件链类

如果你要使用这个类,那你只要那Dispatcher的单例方法:

SharedDispatcher()

来进行操作好了

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

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

来创建

当然,demo还得贴上

 package main

 import (
"fmt"
"tbs"
"time"
) type MClass struct {
dispatcher tbs.Dispatcher
} func main() {
mc := &MClass{}
mc.Start()
} func (this *MClass) Start() {
//获取分派器单例
dispatcher := tbs.SharedDispatcher() //添加监听1
var fun1 tbs.EventCallback = this.onTest
dispatcher.AddEventListener("test", &fun1) //再添加监听2
var fun2 tbs.EventCallback = this.onTest2
dispatcher.AddEventListener("test", &fun2) //随便弄个事件携带的参数,我把参数定义为一个map
params := make(map[string]interface{})
params["id"] =
//创建一个事件对象
event := tbs.CreateEvent("test", params)
//把事件分派出去
dispatcher.DispatchEvent(event) //移除监听1
dispatcher.RemoveEventListener("test", &fun1) //再把事件分派出去一次
dispatcher.DispatchEvent(event) //因为主线程不会等子线程而直接关闭进程,这样会看不到效果,所以我在这里加了阻塞式延时
time.Sleep(time.Second * )
} //回调出得到的就是一个event对象了
func (this *MClass) onTest(event *tbs.Event) {
fmt.Println("onTest", event.Params["id"])
} func (this *MClass) onTest2(event *tbs.Event) {
fmt.Println("onTest2", event.Params["id"])
}

输出结果:

add listener: test
add listener: test
dispatch event: test
onTest
remove listener: test
dispatch event: test
onTest2
onTest2
成功: 进程退出代码 .

哈哈,成功地运行了。

demo你面的注释已经非常详尽了,看不懂就在下面问我好了!

昨晚我拿他来封装了一下golang的socket,改成了事件驱动,耦合度瞬间降低了很多。

 func onServerStarted(event *tbs.Event) {
fmt.Println("server started.")
} func onAccept(event *tbs.Event) {
socket := (event.Params["socket"]).(*tbs.Socket) fmt.Printf("client[#%d] connect on:%s\n", socket.Sign, socket.Conn.RemoteAddr().String())
} func onData(event *tbs.Event) {
socket := (event.Params["socket"]).(*tbs.Socket)
bytes := (event.Params["bytes"]).([]byte) fmt.Printf("[#%d]:", socket.Sign)
fmt.Println(bytes)
} func onClosed(event *tbs.Event) {
socket := (event.Params["socket"]).(*tbs.Socket)
fmt.Printf("[#%d] closed\n", socket.Sign)
}

我还是模仿了as3提供的socket,看如上四个回调,只要监听并开启了serversocket,那么我只要坐等这四个回调来处理游戏的逻辑即可,每个socket都绑有累加的Sign作为标识。

总结:

golang是一门不错的语言,特别灵活,反射也很方便,应该会火吧,希望国内能有更多golang的开发者社区能建立起来吧!

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