使用Go初始化Struct的方法详解
作者:程序员读书
引言
面向对象编程语言最基础的概念就是类(class
),不过Go
语言并没有类的概念,所以使用Go
语言开发时,我们一般会用struct(结构体)
来模拟面向对象中的类。
类一般是通过构造方法(constructors
)来初始化类的实例(对象)中的属性,不过Go
的struct
并没有构造方法这种机制,那要怎么样初始化struct
类型的实例呢?
下面我们来介绍几种创建struct
类型变量的方法。
字面量
创建并初始化一个struct
变量最简单直接的方式就是使用struct
字面量:
//app/school.go package school type Student struct { ID int Name string Score int Grade string } //main.go package main func main() { s := &Student{ ID: 1, Name: "小明", Score: 90, Grade: "高中二班", } }
不过有时候我们只是需要一个临时struct
类型的话,可以使用匿名struct
:
func main() { s := struct { ID int Name string Score int Grade string }{ ID: 1, Name: "小明", Score: 90, Grade: "高中二年级", } }
使用内置new函数
除了字面量外,使用Go
内置的new函数也可以创建一个struct
变量,不过这种方式需要自己为struct
的每个字段赋初始值:
func main(){ s := new(Student) s.ID = 1 s.Name = "小明" s.Score = 90 s.Grade = "高中二班" }
构造函数
前面我们说过Go
语言的struct
没有构造函数,但是我们可以自定义函数来初始化struct
,自定义函数的好处在于:
- 可以达到复用的目的。
- 可以起到封装的效果。
- 可以校验初始化值是否在允许的范围内。
一般我们自定义的构造函数都以New
为前缀,比如:
package school type student struct { ID int Name string Score int Grade string } func NewStudent(ID int, Name string,Score int Grade string) *Student { if ID < 0{ panic("ID不能小于0") } if Score < 0 || Score > 100{ panic("Score必须在0~100之间") } s := new(Student) s.ID = 1 s.Name = "小明" s.Score = 90 s.Grade = "高中二班" return s } package main import "app/school" func main(){ s := school.NewStudent(1,"小明",90,"高中二班") }
上面的例子中,我们自定义了NewStudent()
函数,之后可以在其他包复用该函数来初始化struct
,将Student
改为student
,这样其他包无法直接访问student
结构体,达到了封装的效果,而在NewStudent()
函数中,我们也可以验证参数是否合理。
当然自定义构造函数也是必须以New
为前缀的,比如标准库os
包中,初始化文件句柄时,可以使用Open()
,Create()
和OpenFile()
等函数来初始化os.File
:
//os包的file.go文件的代码 func Open(name string) (*File, error) { return OpenFile(name, O_RDONLY, 0) } func Create(name string) (*File, error) { return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666) } func OpenFile(name string, flag int, perm FileMode) (*File, error) { testlog.Open(name) f, err := openFileNolog(name, flag, perm) if err != nil { return nil, err } f.appendMode = flag&O_APPEND != 0 return f, nil }
支持可变参数的构造函数
在上面的例子中,我们可以通过NewStudent()
函数创建一个student
类型的变量,但是这个构造函数的参数的顺序与个数是固定的,如果有多个地方调用NewStudent()
函数,此时在该函数新增一个参数时,那么所有调用到该函数的代码都必须调整。
我们可以再优化一下我们的构造函数,使用其参数的个数与顺序是可变的:
package school type student struct { ID int Name string Score int Grade string } type StudentOptionFunc func(*student) func WithID(id int) StudentOptionFunc { return func(s *student) { s.ID = id } } func WithName(name string) StudentOptionFunc { return func(s *student) { s.Name = name } } func WithScore(score int) StudentOptionFunc { return func(s *student) { s.Score = score } } func WithGrade(grade string) StudentOptionFunc { return func(s *student) { s.Grade = grade } } func NewStudent(opts ...StudentOptionFunc) *student { s := &student{} for _, opt := range opts { opt(s) } return s }
上面的例子中,构造函数NewStudent()
的参数是不定长StudentOptionFunc
类型的函数,可以看作是一个装了多个函数的切片,在NewStudent()
函数内部遍历这个切片,通过切片中的每个函数来初始化自己的字段。
接下来在调用中,我们就可以不受参数个数与顺序的影响来调用NewStudent()
函数了:
package main import ( "app/school" "fmt" ) func main() { s1 := school.NewStudent( school.WithName("小明"), school.WithScore(90), school.WithID(1), school.WithGrade(""), ) s2 := school.NewStudent( school.WithName("小花"), school.WithScore(90), ) }
折中方案
上面演示了两种自定义构造函数,一种初始化参数个数与顺序完全固定,这种方式太死板了,而一种则是可以自由传入参数的顺序与个数,这种方式又太自由了,因为我们可以想一个折中的方案,即把两种方式结合起来:
func NewStudent(id int, Name string, opts ...StudentOptionFunc) *student { s := &student{ID: id, Name: Name} for _, opt := range opts { opt(s) } return s }
小结
上面的几种介绍的几种初始化struct
的方式并没有好坏之分,选择你喜欢的方式去初始化struct
变量即可。
到此这篇关于Go初始化Struct的方法详解的文章就介绍到这了,更多相关Go初始化Struct内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!