Go语言实现-观察者模式

时间:2022-12-30 12:25:34

前前言

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

比如你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/

Go语言实现-观察者模式的更多相关文章

  1. 学习Go语言之观察者模式

    首先了解一下观察者模式 1.目标和观察者抽象对象需要首先建立 //抽象主题 type Subject interface { Add(o Observer) Send(str string) } // ...

  2. 《JAVA与模式》之观察者模式

    转自:http://www.cnblogs.com/java-my-life/archive/2012/05/16/2502279.html 在阎宏博士的<JAVA与模式>一书中开头是这样 ...

  3. 设计模式&lpar;19&rpar;--Observer&lpar;观察者模式&rpar;--行为型

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义: 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图( ...

  4. java&colon;从消息机制谈到观察者模式

    从简单的例子开始 同样,我们还是先看一个简单例子:创建一个窗口实现加法的计算功能.其效果如下: 图1: 加法计算 Calculator.java: import javax.swing.*; impo ...

  5. Java设计模式の观察者模式(推拉模型)

    目录: 一.观察者定义 二.观察者模式的结构(推模式实现) 三.推模型和拉模型(拉模式实现) 四.JAVA提供的对观察者模式的支持 五.使用JAVA对观察者模式的支持(自带推模式实现实例) 一.观察者 ...

  6. java设计模式之观察者模式以及在java中作用

    观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式.源-监听器(Source/Listener)模式或从属者(Dependen ...

  7. 《JAVA与模式》之观察者模式(转载)

    <JAVA与模式>之观察者模式(转载)  原文链接:http://www.cnblogs.com/java-my-life/archive/2012/05/16/2502279.html ...

  8. Java常见设计模式之观察者模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述观察者(Observer)模式的: 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Mo ...

  9. JAVA基础——设计模式之观察者模式

    观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式.源-监听器(Source/Listener)模式或从属者(Dependen ...

随机推荐

  1. Java 集合深入理解(6):AbstractList

    点击查看 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~ 今天心情比天蓝,来学学 AbstractList 吧! 什么是 AbstractList AbstractList 继承自 ...

  2. hdu 5154 Harry and Magical Computer

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5154 Harry and Magical Computer Description In reward ...

  3. 公司开发的APP,如何生成一个二维码,供客户下载使用

    1.其实和简单,因为一般的用户使用扫一扫,大多数都是用微信自带的扫一扫工具 而,微信打开的二维码页面,会自动屏蔽apk文件,所以显然把apk的url生成一个二维码,让用户扫一扫就能直接下载,这样是行不 ...

  4. ISAP 模板

    #include <iostream> #include <cstring> #include <cstdio> #include <queue> us ...

  5. 【深度学习系列】用PaddlePaddle和Tensorflow实现GoogLeNet InceptionV2&sol;V3&sol;V4

    上一篇文章我们引出了GoogLeNet InceptionV1的网络结构,这篇文章中我们会详细讲到Inception V2/V3/V4的发展历程以及它们的网络结构和亮点. GoogLeNet Ince ...

  6. JS解析JSON字符串

    问题描述:后台需要传递给前台一些数据,用于页面数据显示,因为是一些Lable标签,所以数据传递到前台需要解析. 思路:因为数据比较杂乱,所以我选择传递的数据类型是Json格式,但是数据展示时需要解析成 ...

  7. Dubbo2&period;6&period;5&plus;Nacos注册中心(代替Zookeeper)

    在上一节的小栗子的基础上,只需要更改两个地方 第一个:父工程的pom依赖增加 <!-- Dubbo Nacos registry dependency --> <dependency ...

  8. 利用Centos服务器来搭建自己的splash&comma;不再被安装的各种环境繁琐而担忧

    Centos7.5 ----- docker ------- splash               第一步:服务器环境的配置               第二步:dcoker环境的配置       ...

  9. 【Linux】Mac PD set centos static ip

    2,修改Centos的网络设置. (1)进入脚本. vi /etc/sysconfig/network-scripts/ifcfg-eth0 My Mac ip: # 从dhcp改成static BO ...

  10. Scala编程 摘录

    有件你会注意到的事情是,几乎所有的 Scala 的控制结构都会产生某个值.这是函数式语言所采用的方式,程序被看成是计算值的活动,因此程序的控件也应当这么做.你也可以把这种方式看做早已存在于指令式语言中 ...