阅读目录
go 函数类型别名(Function Type Alias)
函数类型别名(Function Type Alias)或简称为函数别名。
在 Go 语言中,使用 type 关键字可以创建函数类型的别名,使其更易于使用和理解。
package main
import "fmt"
type DataEventHandle func(data interface{})
func main() {
// 定义一个名为 handle 的数据事件处理函数
handle := func(data interface{}) {
fmt.Println("Handling data:", data)
}
// 声明一个变量为 DataEventHandle 类型,并将 handle 函数赋值给它
var eventHandle DataEventHandle = handle
// 调用数据事件处理函数
eventHandle("Hello, World!")
}
PS D:\TEXT\test> go run .\hello.go
Handling data: Hello, World!
PS D:\TEXT\test>
Go 观察者模式(Observer Pattern)
观察者模式是一种行为设计模式,用于在对象之间建立一对多的依赖关系。
在该模式中,一个被观察的对象(通常称为主题或可观察者)维护一个列表,其中包含了所有依赖于它的观察者对象。
当主题的状态发生变化时,它会通知所有观察者,使它们能够自动更新自己。
package main
import "fmt"
// DataEventHandle 则充当观察者
type DataEventHandle func(data interface{})
// 事件上的观察者(事件处理函数)
type dataEventHandlerInfo struct {
handler DataEventHandle
once bool
}
/*
DataEvent 充当主题(或可观察者)
DataEvent 类型维护一个 handlers 列表,
其中存储了所有附加到事件上的观察者(事件处理函数)。
*/
type DataEvent struct {
handlers []dataEventHandlerInfo
}
// 将一个观察者(事件处理函数)附加到 DataEvent 上。
func (e *DataEvent) Attach(handler DataEventHandle) int {
handlerInfo := dataEventHandlerInfo{handler, false}
for i, h := range e.handlers {
if h.handler == nil {
e.handlers[i] = handlerInfo
return i
}
}
e.handlers = append(e.handlers, handlerInfo)
return len(e.handlers) - 1
}
/*
Publish 方法被调用时,
它会遍历 handlers 列表,
并依次触发每个观察者(调用相应的事件处理函数)。
*/
func (e *DataEvent) Publish(data interface{}) {
for i, h := range e.handlers {
if h.handler != nil {
h.handler(data)
if h.once {
e.handlers[i].handler = nil
}
}
}
}
func main() {
event := DataEvent{
handlers: make([]dataEventHandlerInfo, 0),
}
// 定义一个事件处理函数
handler1 := func(data interface{}) {
fmt.Println("Handler 1:", data)
}
// 将事件处理函数附加到事件上
event.Attach(handler1)
// 定义另一个事件处理函数
handler2 := func(data interface{}) {
fmt.Println("Handler 2:", data)
}
// 将另一个事件处理函数附加到事件上
event.Attach(handler2)
// 发布事件,触发所有附加的事件处理函数
event.Publish("Hello, World!")
}
PS D:\TEXT\test> go run .\hello.go
Handler 1: Hello, World!
Handler 2: Hello, World!
PS D:\TEXT\test>
在这个示例中,DataEvent 充当主题(或可观察者),而 DataEventHandle 则充当观察者。 DataEvent 类型维护一个 handlers 列表,其中存储了所有附加到事件上的观察者(事件处理函数)。
通过 Attach 方法,我们可以将一个观察者(事件处理函数)附加到 DataEvent 上。当 Publish 方法被调用时,它会遍历 handlers 列表,并依次触发每个观察者(调用相应的事件处理函数)。
这种模式的优点是解耦了主题和观察者之间的关系,使它们可以独立演化。
主题不需要关心观察者的具体实现细节,只需通知它们即可。同时,观察者可以根据需要动态地添加或移除。
观察者模式在许多应用场景中非常有用,比如事件驱动系统、GUI 应用程序、消息队列和发布/订阅模型等。
请注意,示例中的实现方式是一种简化版本,可能并没有考虑线程安全性、错误处理等方面的细节。在实际应用中,可能需要根据具体需求进行更复杂的实现。
从事件处理函数列表中移除指定的事件处理函数
Detach 函数是用于从 DataEvent 结构的事件处理函数列表中移除特定的事件处理函数的方法。
package main
import "fmt"
// DataEventHandle 则充当观察者
type DataEventHandle func(data interface{})
// 事件上的观察者(事件处理函数)
type dataEventHandlerInfo struct {
handler DataEventHandle
once bool
}
/*
DataEvent 充当主题(或可观察者)
DataEvent 类型维护一个 handlers 列表,
其中存储了所有附加到事件上的观察者(事件处理函数)。
*/
type DataEvent struct {
handlers []dataEventHandlerInfo
}
// 将一个观察者(事件处理函数)附加到 DataEvent 上。
func (e *DataEvent) Attach(handler DataEventHandle) int {
handlerInfo := dataEventHandlerInfo{handler, false}
for i, h := range e.handlers {
if h.handler == nil {
e.handlers[i] = handlerInfo
return i
}
}
e.handlers = append(e.handlers, handlerInfo)
return len(e.handlers) - 1
}
func (e *DataEvent) Publish(data interface{}) {
for i, h := range e.handlers {
if h.handler != nil {
h.handler(data)
if h.once {
e.handlers[i].handler = nil
}
}
}
}
func (e *DataEvent) Detach(handle int) {
if handle >= 0 && handle < len(e.handlers) {
e.handlers[handle].handler = nil
}
}
func main() {
event := DataEvent{
handlers: make([]dataEventHandlerInfo, 0),
}
// 定义一个事件处理函数
handler1 := func(data interface{}) {
fmt.Println("Handler 1:", data)
}
// 将事件处理函数附加到事件上
handle1 := event.Attach(handler1)
// 定义另一个事件处理函数
handler2 := func(data interface{}) {
fmt.Println("Handler 2:", data)
}
// 将另一个事件处理函数附加到事件上
handle2 := event.Attach(handler2)
// 发布事件,触发所有附加的事件处理函数
event.Publish("未移除前的发布")
fmt.Println(handle2)
// 从事件中移除第一个事件处理函数
event.Detach(handle1)
// 发布事件,触发所有附加的事件处理函数
event.Publish("Hello, World!")
fmt.Println(handle1)
}
在上面的示例中,我们首先附加了两个事件处理函数 handler1 和 handler2 到事件中,并通过调用 event.Publish 来触发它们。
然后,我们调用 event.Detach 方法,传递之前返回的事件处理函数的句柄 handle1,以从事件中移除第一个事件处理函数。
最后,我们再次调用 event.Publish 来触发剩下的事件处理函数。由于第一个事件处理函数已被移除,只有第二个事件处理函数会被触发。
运行该示例会输出以下内容:
PS D:\TEXT\test> go run .\hello.go
Handler 1: 未移除前的发布
Handler 2: 未移除前的发布
1
Handler 2: Hello, World!
0
PS D:\TEXT\test>
这表明在第一次发布事件时,两个事件处理函数都被触发。
然而,在第二次发布事件时,只有剩下的事件处理函数被触发,因为第一个事件处理函数已被成功移除。
标记该事件处理函数只会被触发一次
Once 函数是用于将事件处理函数附加到 DataEvent 结构的事件处理函数列表中,并标记该事件处理函数只会被触发一次。
下面是一个示例,展示了如何使用 Once 函数将事件处理函数附加到事件上,并在触发后自动将其从事件处理函数列表中移除:
package main
import "fmt"
// DataEventHandle 则充当观察者
type DataEventHandle func(data interface{})
// 事件上的观察者(事件处理函数)
type dataEventHandlerInfo struct {
handler DataEventHandle
once bool
}
/*
DataEvent 充当主题(或可观察者)
DataEvent 类型维护一个 handlers 列表,
其中存储了所有附加到事件上的观察者(事件处理函数)。
*/
type DataEvent struct {
handlers []dataEventHandlerInfo
}
// 将一个观察者(事件处理函数)附加到 DataEvent 上。
func (e *DataEvent) Attach(handler DataEventHandle) int {
handlerInfo := dataEventHandlerInfo{handler, false}
for i, h := range e.handlers {
if h.handler == nil {
e.handlers[i] = handlerInfo
return i
}
}
e.handlers = append(e.handlers, handlerInfo)
return len(e.handlers) - 1
}
func (e *DataEvent) Publish(data interface{}) {
for i, h := range e.handlers {
if h.handler != nil {
h.handler(data)
if h.once {
e.handlers[i].handler = nil
}
}
}
}
// 标记该事件处理函数只会被触发一次
func (e *DataEvent) Once(handler DataEventHandle) {
i := e.Attach(handler)
e.handlers[i].once = true
}
func main() {
/*
创建了一个名为 event 的 DataEvent 对象,
并初始化了其 handlers 字段为一个空的 dataEventHandlerInfo 切片。
1 DataEvent 是一个自定义类型,表示一个数据事件对象,其中包含了事件处理函数列表。
2 handlers 是 DataEvent 结构中的一个字段,
它是一个 dataEventHandlerInfo 类型的切片。它用于存储事件处理函数的信息。
3 make([]dataEventHandlerInfo, 0) 是创建了一个空的 dataEventHandlerInfo 类型的切片。
通过传递切片的长度为0,我们表示这是一个空的切片,没有任何元素。
4 event 是用于存储数据事件的变量,通过赋值运算符 = 将创建的 DataEvent 对象赋值给它。
*/
event := DataEvent{
handlers: make([]dataEventHandlerInfo, 0),
}
/*
综合起来,这行代码的作用是创建一个空的 DataEvent 对象,
并将其赋值给 event 变量。
这个对象可以用来存储事件处理函数并进行相关操作,
例如附加事件处理函数、发布事件等。
*/
// 定义一个事件处理函数
handler1 := func(data interface{}) {
fmt.Println("Handler 1:", data)
}
// 将事件处理函数附加到事件上,并标记为只触发一次
event.Once(handler1)
// 发布事件,触发附加的事件处理函数
event.Publish("Hello, World!")
// 再次发布事件,不会触发事件处理函数,因为它已被移除
event.Publish("Hello, Again!")
}
在上述示例中,我们使用 Once 函数将 handler1 事件处理函数附加到事件中,并将其标记为只触发一次。在事件发布后,handler1 事件处理函数会被触发,并输出相应的消息。
然后,我们再次发布事件,但由于 handler1 被标记为只触发一次并且在第一次触发后被自动移除,因此第二次发布事件时不会触发该事件处理函数。
运行该示例会输出以下内容:
PS D:\TEXT\test> go run .\hello.go
Handler 1: Hello, World!
PS D:\TEXT\test>
这表明在第一次发布事件时,只有标记为只触发一次的事件处理函数被触发,而在第二次发布事件时,该事件处理函数已被成功移除,不会被触发。
封装事件的发布过程,通过提供简洁的接口来管理和触发事件处理函数
这段代码示例展示了如何使用 DataEventPublisher 结构体来发布事件:
package main
import "fmt"
// DataEventHandle 则充当观察者
type DataEventHandle func(data interface{})
// 事件上的观察者(事件处理函数)
type dataEventHandlerInfo struct {
handler DataEventHandle
once bool
}
/*
DataEvent 充当主题(或可观察者)
DataEvent 类型维护一个 handlers 列表,
其中存储了所有附加到事件上的观察者(事件处理函数)。
*/
type DataEvent struct {
handlers []dataEventHandlerInfo
}
// 将一个观察者(事件处理函数)附加到 DataEvent 上。
func (e *DataEvent) Attach(handler DataEventHandle) int {
handlerInfo := dataEventHandlerInfo{handler, false}
for i, h := range e.handlers {
if h.handler == nil {
e.handlers[i] = handlerInfo
return i
}
}
e.handlers = append(e.handlers, handlerInfo)
return len(e.handlers) - 1
}
// Publish 发布数据事件,触发所有附加的数据事件处理函数
func (e *DataEvent) Publish(data interface{}) {
for i, h := range e.handlers {
if h.handler != nil {
h.handler(data)
if h.once {
e.handlers[i].handler = nil
}
}
}
}
// 从事件处理函数列表中移除指定的事件处理函数
func (e *DataEvent) Detach(handle int) {
if handle >= 0 && handle < len(e.handlers) {
e.handlers[handle].handler = nil
}
}
// 使用 DataEventPublisher 结构体来发布事件
type DataEventPublisher struct {
event DataEvent
}
/*
Event() 方法返回指向 event 字段的指针,
允许外部代码直接访问 DataEvent 对象。
*/
func (p *DataEventPublisher) Event() *DataEvent {
return &p.event
}
/*
Publish(data interface{}) 方法用于发布事件,
它会遍历 event 字段中的事件处理函数列表,
并依次调用每个事件处理函数。
*/
func (p *DataEventPublisher) Publish(data interface{}) {
for i, h := range p.event.handlers {
if h.handler != nil {
h.handler(data)
if h.once {
p.event.Detach(i)
}
}
}
}
func main() {
/*
这段代码创建了一个名为 publisher 的 DataEventPublisher 对象,
并初始化了它的 event 字段为一个新的 DataEvent 对象。
让我们逐步解释这段代码的含义:
1 DataEventPublisher 是一个自定义类型,表示一个数据事件发布者对象,
它封装了事件的发布和管理功能。
2 event 是 DataEventPublisher 结构体中的一个字段,
它是一个 DataEvent 类型的对象,用于存储事件及其相关的处理函数。
3 DataEvent 是一个自定义类型,表示一个数据事件对象,其中包含了事件处理函数列表。
4 handlers 是 DataEvent 结构体中的一个字段,
它是一个 dataEventHandlerInfo 类型的切片,用于存储事件处理函数的信息。
5 make([]dataEventHandlerInfo, 0) 创建了一个空的 dataEventHandlerInfo 类型的切片。
这表示创建一个长度为 0 的切片,即空切片,没有任何元素。
6 DataEventPublisher{ event: DataEvent{ handlers: make([]dataEventHandlerInfo, 0), }, }
使用结构体字面量的方式创建了一个 DataEventPublisher 对象。
在对象的声明中,我们通过指定 event 字段为初始化的 DataEvent 对象,
使得 publisher 对象包含了一个空的事件对象。
*/
publisher := DataEventPublisher{
event: DataEvent{
handlers: make([]dataEventHandlerInfo, 0),
},
}
/*
综合起来,这段代码的作用是创建一个 DataEventPublisher 对象,
并将其赋值给 publisher 变量。该对象具有一个空的事件对象,
可以用于管理和发布事件以及相关的事件处理函数。
*/
// 定义一个事件处理函数
handler := func(data interface{}) {
fmt.Println("Event Handler:", data)
}
// 将事件处理函数附加到事件中
publisher.Event().Attach(handler)
// 发布事件,触发附加的事件处理函数
publisher.Publish("Hello, World!")
}
这段代码示例中的 DataEventPublisher 结构体和其方法的目的是为了封装事件的发布过程,通过提供简洁的接口来管理和触发事件处理函数。
PS D:\TEXT\test> go run .\hello.go
Event Handler: Hello, World!
PS D:\TEXT\test>