Go语言实现-观察者模式
前前言
这个类经过我的正式投入使用啊,发现不对劲,这样做可能会导致线程死锁
比如你dispatch一个event,然后在这个回调里把那个事件的侦听给remove掉了,那么就会导致线程死锁(这个问题找了好久啊,刚刚调试的时候才发现了)
还有就是获取func的引用的问题,golang那半c半java的语法,我改用了新的方法
源码已经修改!
前言:
呀,学Go语言两周了,感觉上手挺快的,golang虽然和c语言很想,但是避免掉了很多指针相关的东东,所以学起来特别轻松。
但是途中坎坷颇多啊,资料是少之又少啊,搜索引擎都搜不到啥东西,唯有golang.org和github的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/
Go语言实现-观察者模式的更多相关文章
- 学习Go语言之观察者模式
首先了解一下观察者模式 1.目标和观察者抽象对象需要首先建立 //抽象主题 type Subject interface { Add(o Observer) Send(str string) } // ...
- 《JAVA与模式》之观察者模式
转自:http://www.cnblogs.com/java-my-life/archive/2012/05/16/2502279.html 在阎宏博士的<JAVA与模式>一书中开头是这样 ...
- 设计模式(19)--Observer(观察者模式)--行为型
作者QQ:1095737364 QQ群:123300273 欢迎加入! 1.模式定义: 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图( ...
- java:从消息机制谈到观察者模式
从简单的例子开始 同样,我们还是先看一个简单例子:创建一个窗口实现加法的计算功能.其效果如下: 图1: 加法计算 Calculator.java: import javax.swing.*; impo ...
- Java设计模式の观察者模式(推拉模型)
目录: 一.观察者定义 二.观察者模式的结构(推模式实现) 三.推模型和拉模型(拉模式实现) 四.JAVA提供的对观察者模式的支持 五.使用JAVA对观察者模式的支持(自带推模式实现实例) 一.观察者 ...
- java设计模式之观察者模式以及在java中作用
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式.源-监听器(Source/Listener)模式或从属者(Dependen ...
- 《JAVA与模式》之观察者模式(转载)
<JAVA与模式>之观察者模式(转载) 原文链接:http://www.cnblogs.com/java-my-life/archive/2012/05/16/2502279.html ...
- Java常见设计模式之观察者模式
在阎宏博士的<JAVA与模式>一书中开头是这样描述观察者(Observer)模式的: 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Mo ...
- JAVA基础——设计模式之观察者模式
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式.源-监听器(Source/Listener)模式或从属者(Dependen ...
随机推荐
- T-SQL 游标
游标是面向行的,它会使开发人员变懒,懒得去想用面向集合的查询方式实现某些功能. 在性能上,游标会迟更多的内存,减少可用的并发,占用带宽,锁定资源,当然还有更多的代码量. 用一个比喻来说明为什么游标会占 ...
- 自定义searchview的编辑框,搜索按钮,删除按钮,光标等
//指定某个私有属性 Field mSearchHintIconField = argClass.getDeclaredField("mSearchHintIcon"); mSea ...
- elasticsearch 搜索不支持单词的部分进行匹配
zjtest7-frontend:/usr/local/logstash-2.3.4/config# curl -XGET http://192.168.32.80:9200/logstash-201 ...
- cf479A Expression
A. Expression time limit per test 1 second memory limit per test 256 megabytes input standard input ...
- 外部样式OL LI的定义 影响到了富文本框内的UL LI的定义,使用内部样式对其还原
<style type="text/css"> #intro { white-space: normal; word-break: break-all; overflo ...
- MyEclipse通过JDBC连接MySQL数据库基本介绍
转载自:http://www.jb51.net/article/31876.htm 1.前提是MyEclipse已经能正常开发Java工程 2.安装MySQL 个人使用的是版本是 mysql-5.0. ...
- c#将输入的人民币数字金额转换成小写
//// <summary> /// 人民币大小写金额转换 /// </summary> class RMBCapitalization { private const str ...
- Error Correct System(模拟)
Error Correct System Time Limit:2000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I ...
- 矩阵经典题目六:poj 3070 Fibonacci
http://poj.org/problem?id=3070 按已构造好的矩阵,那么该矩阵的n次方的右上角的数便是f[n]. #include <stdio.h> #include < ...
- DropDownList 绑定数据后 插入一条不属于表中的数据
ddlFGiftId.DataSource = dtGift; ddlFGiftId.DataTextField = "FGiftName"; ddlFGiftId.DataVal ...