前言
在golang中对某个结构体进行初始化时会创建一个函数,一般情况下该函数取名为NewXXXX()
当结构体多了之后,对其进行实例化各种New眼花缭乱,使得代码可读性降低,而golang在1.18版本推出了泛型之后
可以使用泛型模拟一个Default构造器
1. 实现方法
// Defaulter 接口定义了 InitDefault 方法,用于初始化结构体
type Defaulter interface {
InitDefault(args ...any)
}
// defaulterPtr 类型约束,要求类型 T 的指针实现 Defaulter 接口
type defaulterPtr[T any] interface {
*T
Defaulter
}
// Default 函数使用泛型创建并初始化一个类型为 T 的对象
// 参数 args 可变,传递给 InitDefault 方法
func Default[T any, P defaulterPtr[T]](args ...any) T {
var v T // 创建一个类型为 T 的变量
P.InitDefault(&v, args...) // 使用类型 T 的指针调用 InitDefault 方法进行初始化
return v // 返回初始化后的变量
}2. 使用方法
type Cat struct {
Name string
}
// Cat 实现了 Defaulter 接口的 InitDefault 方法
// 方法使用传入的参数进行初始化
// 此方法可以视作构造函数,所有结构体只要实现了此方法,既可使用Default进行构造
func (c *Cat) InitDefault(args ...any) {
c.Name = args[0].(string) // 将第一个参数转换为字符串并赋值给 Name 字段
}
// Cat 的方法,输出猫的名字
func (c *Cat) Say() {
fmt.Printf("My name is %s\n", c.Name)
}
func main() {
tom := Default[Cat]("Tom")
tom.Say()
jerry := Default[Cat]("Jerry")
jerry.Say()
}
/********************
* 输出结果
*
* My name is Tom
* My name is Jerry
*
********************/
3. 为什么这样写?
实现通用初始化逻辑: 通过定义一个通用的
Default函数,可以避免为每个类型都编写重复的初始化逻辑。例如,初始化Cat对象时,只需要实现InitDefault方法,而不需要编写特定的构造函数。利用泛型提高代码复用性: Go 1.18 引入了泛型,通过泛型可以编写更加通用和复用的代码。
Default函数就是一个很好的例子,它利用泛型和类型约束,实现了对任意类型的通用初始化。保证类型安全: 使用类型约束
defaulterPtr[T]确保传入的类型T的指针实现了Defaulter接口,这样在编译时就能保证类型安全,避免运行时错误。可扩展性: 通过接口
Defaulter,可以很容易地扩展新的类型。只需要实现InitDefault方法,就可以通过Default函数进行初始化,而不需要修改现有代码。