Go 函数类型别名(Function Type Alias)

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> 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知其黑、受其白

喝个咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值