基本原则
- struct名称小写开头, 则此struct不会被导出,即不会被外部包使用
- struct名称大写开头,则struct会被导出,但是内部只有是大写字母开头的字段名称被导出, 即小写字母开头的内部字段不会被导出
例外情况: struct的嵌套
1
2
3
4
5
6
7
8
9
| // Horse能被导出, 虽然animal字段是小写,但是依然能够访问到其中的Speak属性
type animal struct{
name string
Speak string
}
type Horse struct {
animal
sound string
}
|
实践
最好不要直接暴露struct,而是通过提供func来间接暴露struct
为了避免类似struct嵌套情况下违背小写字母开头名称不被导出的原意(例如上文例子中的animal的Speak属性被访问),我们最好不直接暴露struct, 而是建立一个func来给外部访问.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| //abc.go文件内容:
package abc
type animal struct{
name string
Speak string
}
// 通过NewAnimal向外界暴露animal
func NewAnimal() *animal{
a := new(animal)
return a
}
// test.go内容:
package main
import (
"fmt"
"./abc"
)
func main() {
t1 := abc.NewAnimal()
// t1.name = "haha" // 无法访问name属性
t1.Speak = "hhhh"
fmt.Println(t1.Speak)
}
|
上述代码虽然通过NewAnimal()使外部包能够构建animal, 但是其中的name字段因为是小写字母开头,依然无法被访问, 如果需要name可以被外界操作又不希望能通过访问name属性直接修改, 可以提供两个func来完成这项工作, 例如:
1
2
3
4
5
6
7
| // 通过暴露GetName和SetName使外部包可以访问和修改animal的name属性值,并且你可以增加一些逻辑,例如对name字段的合法化校验等
func (a *animal) GetName() string {
return a.name
}
func (a *animal) SetName(name string){
a.name = name
}
|
小心结构体的嵌入
规范
通常在结构体中,嵌入的结构体应该放在字段顶部, 并且通过一个空行将其与常规字段分开.
1
2
3
4
5
6
7
8
9
10
11
12
| // Good
type Client struct {
http.Client
version int
}
// Bad
type Client struct {
version int
http.Client
}
|
避免在公共结构中嵌入类型,因为这些嵌入的类型泄漏实现细节, 禁止类型演化, 使文档模糊。通过委托的方式调用可以方便在未来更改具体的Add行为和Remove行为, 否则你就要直接更改AbstractList里的方法, 这可能并不是你的原意.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| type AbstractList struct {}
// Add adds an entity to the list.
func (l *AbstractList) Add(e Entity) {
// ...
}
// Remove removes an entity from the list.
func (l *AbstractList) Remove(e Entity) {
// ...
}
// Bad
// ConcreteList is a list of entities.
type ConcreteList struct {
*AbstractList
}
// Good
// ConcreteList is a list of entities.
type ConcreteList struct {
list *AbstractList
}
// Add adds an entity to the list.
func (l *ConcreteList) Add(e Entity) {
return l.list.Add(e)
}
// Remove removes an entity from the list.
func (l *ConcreteList) Remove(e Entity) {
return l.list.Remove(e)
}
|