go

go设计模式

函数类型实现接口,隐式回调接口

Posted by Liangjf on August 28, 2020

函数类型实现接口,隐式回调接口

这是一种技巧, 用于隐式回调接口, 可以做到直接通过函数类型转换就可以实现函数相当于实现了接口.

典型特点是: 函数类型和接口入参出参一致并实现其接口

http包的应用

1.定义路由

http.HandleFunc("/v1/test", handlerTest)

2.HandleFunc的第二个参数就是func(ResponseWriter, *Request)函数类型, 和下面的Handler接口类型是一致的

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

3.通过HandlerFunc函数类型把路由响应函数转换为HandlerFunc类型

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	if handler == nil {
		panic("http: nil handler")
	}
	mux.Handle(pattern, HandlerFunc(handler))
}

4.HandlerFunc和Handler接口一致并实现Handler接口. 把函数类型转换接口, 用于隐式回调接口, 因为入参出参一致, 并且实现Handler接口

type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

5.实现了Handler接口的都可以接管http请求, 全部的web框架(gin,beego,echo等)都是从这一步开始框架内部处理(丰富路由, 内部封装context等)

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

nsq中应用

1.Handler接口

type Handler interface {
	HandleMessage(message *Message) error
}

2.HandlerFunc函数类型和Handler接口一致并实现接口

type HandlerFunc func(message *Message) error
func (h HandlerFunc) HandleMessage(m *Message) error {
	return h(m)
}

nsq中隐式调用接口应用流程

1.使用HandlerFunc函数类型避免定义struct,实现Handler接口,支持隐式调用接口,因为HandlerFunc实现Handler,内部转换为接口了

nsqCconsumer.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
	....
	return err
}))

2.源码调用逻辑

//添加响应函数
func (r *Consumer) AddHandler(handler Handler) {
	r.AddConcurrentHandlers(handler, 1)
}

func (r *Consumer) AddConcurrentHandlers(handler Handler, concurrency int) {
	...
	for i := 0; i < concurrency; i++ {
		go r.handlerLoop(handler)
	}
}

func (r *Consumer) handlerLoop(handler Handler) {
	...
	for {
		message, ok := <-r.incomingMessages
		...

		//nsq收到消息时会回调传入的用户定义处理消息函数
		err := handler.HandleMessage(message)
		
	}
	...
}

如果没有函数类型转换接口, 那么就需要以下的实现

type Consumers struct {
	Topic   string
	Channel string
	Address []string
}

func (c *Consumers) HandleMessage(msg *nsq.Message) error {
	return nil
}

consumer.AddHandler(&Consumers{})

有时候只是为了简洁直接使用匿名函数的方式, 那么这样就无法做到了.

因此有了函数类型(实现了对应接口), 实现了内部类型转换, 在内部调用真正的处理函数, 很多开源框架都是有这种骚操作的.

总结

函数类型实现接口,隐式回调接口是一种符合go的设计模式,在很多开源框架否有其存在,因为这样可以实现两种方案的传参。1.通过声明结构体,实现接口,传入结构体指针。2.通过匿名函数类型传入,函数类型实现接口内部回调传入的匿名函数。

这样处理可以方便有时直接匿名函数处理就ok了,不必额外声明结构体再实现接口。