不同类型函数统一处理如何实现

如何实现代码自动生成

地址:https://www.zhihu.com/question/349309063/answer/849122773

标签:Go

详情描述

大致的含义是,protobuf 生成的 Client 创建函数 NewClient,但由于不同类型的 Client 创建函数定义不同,如何在它基础上实现通过一个统一的处理函数创建 Client,比如。

type ServiceManager struct {}

func GetClient(serviceName string, newClient func() *Client) interface{} {
    return newClient()
}

但现在的问题是,Client 的类型不统一,NewClient 返回值类型也就不同。于是,想到的就是将 newClient 的类型定义如下:

func() interface{}

但这样产生的问题是,需要为每个 NewClient 包装下,如下面这样的形式:

func newClient() interface{} {
    return NewClient()
}

当出现的 Client 时,就需要增加新的函数包装一下,非常的笨,有没有什么好的办法?

我的回答

不好意思,我好像理解错问题的意思了。之前的回答先留在下面,重新回答下。

还是同样的道理,要灵活的话,就要想到反射。既然是 func 类型不同,那就使用 interface{} 作为参数吧,再通过反射调用函数。

把 GetClient 的实现修改下,如下:

func (s *ServiceManager) GetClient(serviceName string, c interface{}) interface{} {
	rs := reflect.ValueOf(c).Call([]reflect.Value{})
	return rs[0].Interface()
}

通过反射实现函数参数的调用,这里只是演示,没有做必要的类型检查。 使用时,只要直接 NewClient,比如:

func main() {
	serviceMansger := ServiceManager{}

	fmt.Println(serviceMansger.GetClient("hello_service", NewClient))
}

反射的主要缺点是性能较差。

题主可以压测下,比较两种方式的性能差异,不过,NewClient 这种操作一般只会进行一次,偶尔会需要重连,对性能影响有限。

看了下其他回答,如果能修改下代码生成工具,生成的 NewClient 返回的是 interface{},的确也是挺好的,就是有点太 hacker 了。

之前的理解题意错误的回答保留,如下:


想来想去,没什么好的方法,要想方便还就得用上反射。

一句话说就是,interface 作为GetClient 的参数,传递 client 的地址,通过反射修改 interface (即 client)变量的值。

话说,题主已经用了 interface 参数了,那就可以直接修改指针中的内存,并不需要返回值。

举个示例吧。

假如,Client 的定义如下:

type Client struct {
	Name string
}

func NewClient() *Client {
	return &Client{Name: "client"}
}

GetClient 函数的实现可以修改如下:

func (s *ServiceManager) GetClient(serviceName string, c interface{}) {
	reflect.ValueOf(c).Elem().FieldByName("Name").SetString("one")
}

反射主要是表达意思,修改地址内结构体中某个成员的值。这里写的比较粗糙,也没有做必要的错误检查,自己实现需要优化下。

这样的话,使用起来会比之前简单一些。

func main() {
	serviceMansger := ServiceManager{}

	client := NewClient()
	serviceMansger.GetClient("hello_service", client)

	fmt.Println(client)
}

不用考虑再包装一层,而且无需再通过断言进行类型转化。