博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Go语言实现-观察者模式
阅读量:5260 次
发布时间:2019-06-14

本文共 6865 字,大约阅读时间需要 22 分钟。

前前言

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

比如你dispatch一个event,然后在这个回调里把那个事件的侦听给remove掉了,那么就会导致线程死锁(这个问题找了好久啊,刚刚调试的时候才发现了)

还有就是获取func的引用的问题,golang那半c半java的语法,我改用了新的方法

源码已经修改!

前言:

呀,学Go语言两周了,感觉上手挺快的,golang虽然和c语言很想,但是避免掉了很多指针相关的东东,所以学起来特别轻松。

但是途中坎坷颇多啊,资料是少之又少啊,搜索引擎都搜不到啥东西,唯有和上才有比较完整的资料,加了个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 := 0100     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 = true106             ch = eventChain.chs[k]107             key = k108             break109         }110     }111 112     if exist {113         //fmt.Printf("remove listener: %s\n", eventName)114         //fmt.Println("chan: ", ch)115         ch <- nil116 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 <- event129         }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.Dispatcher11 }12 13 func main() {14     mc := &MClass{}15     mc.Start()16 }17 18 func (this *MClass) Start() {19     //获取分派器单例20     dispatcher := tbs.SharedDispatcher()21 22     //添加监听123     var fun1 tbs.EventCallback = this.onTest24     dispatcher.AddEventListener("test", &fun1)25 26     //再添加监听227     var fun2 tbs.EventCallback = this.onTest228     dispatcher.AddEventListener("test", &fun2)29 30     //随便弄个事件携带的参数,我把参数定义为一个map31     params := make(map[string]interface{})32     params["id"] = 100033     //创建一个事件对象34     event := tbs.CreateEvent("test", params)35     //把事件分派出去36     dispatcher.DispatchEvent(event)37 38     //移除监听139     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: testadd listener: testdispatch event: testonTest 1000remove listener: testdispatch event: testonTest2 1000onTest2 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的开发者社区能建立起来吧!

贴上我自己的博客地址:

转载于:https://www.cnblogs.com/rockyf/p/golang_subscribe.html

你可能感兴趣的文章
adidas crazylight 2018 performance analysis review
查看>>
typeset shell 用法
查看>>
python 之 循环语句
查看>>
心得25--JDK新特性9-泛型1-加深介绍
查看>>
[转]ceph网络通信模块_以monitor模块为例
查看>>
HDOJ 1754 I Hate It(线段树基本操作)
查看>>
latex tree
查看>>
安装NVIDIA驱动时禁用自带nouveau驱动
查看>>
HDU-1255 覆盖的面积 (扫描线)
查看>>
css3学习01
查看>>
【USACO】 奶牛会展
查看>>
ActiveMQ笔记之点对点队列(Point-to-Point)
查看>>
继承和多态
查看>>
Dijkstra+计算几何 POJ 2502 Subway
查看>>
修复IE不能执行JS的方法
查看>>
程序员究竟该如何提高效率zt
查看>>
希尔排序法(缩小增量法)
查看>>
PHP编程基础学习(一)——数据类型
查看>>
MongoDB-JAVA-Driver 3.2版本常用代码全整理(2) - 查询
查看>>
NPOI处理Word文本中上下角标
查看>>