世界是复杂的。一个人、一个事物可以有多样的特性,而多个人多个事物又可以表现出一致的特性;从微观角度说,即具体到个体时,要求这个个体是多功能的多样性的,能够完成越多的功能具有越多的特性越好。比如一个人可以同时具有贤妻、慈母、严师和路人甲的身份,比如一个数字可以表示年龄、高度、分数等。从宏观角度说,从管理复杂性的角度出发,又要求许多的个体具有一致性以便进行统一处理以降低管理成本,比如各种机动车都可以“驾驶”,各种食物都可以“食用”,各种书写工具都可以“写”。换而言之:微观上一人千面;宏观上千人一面。
编程本质上是为了解决人类的问题满足人类的需要,所以需要把现实世界映射到编程语言里,即在编程语言中建立对应的模型。在Go语言里,提供了很好的机制来建立这种模型,也就是type和interface。type很好地实现了一人千面,而interface很好地支持了千人一面。Go语言的非侵入式的鸭子类型的interface已经有很多文章讨论过了,这里我主要谈谈我对type的理解。
我们知道,Go语言里可以使用type关键字来把一个类型来转换成另外一个类型而保持数据的本质不变,例如:
type Age int
type Height int
type Grade int
type绝不只是对应于C/C++中的typedef,它不是用于定义一系列的别名。更关键的是,它定义了一系列互不相干的行为特征:通过这些互不相干的行为特征,本质上同一的事物表现出不同事物的特征:整数还是整数,但年龄却不是高度也不是分数。我们可以分别为Age、Height、Grade定义出下列不同的行为(表示为方法或者函数):
func (a Age) Old() bool {
// 超过50岁算老年
return a > 50
}
func (l Length) NeedTicket() bool {
// 高于120cm需要买票
return l > 120
}
func (g Grade) Pass() bool {
// 60分及格
return g >= 60
}
看一个游戏编程中常见的例子:一个队伍可以包括若干玩家,这个队伍可以有加入退出等一系列游戏逻辑相关的行为,也可以按照玩家的等级进行排序。分析如下:
1.队伍在本质上可以是一个slice
2.加入、退出队伍和队伍排序是游戏逻辑相关的
3.为了排序,队伍需要实现sort.Interface接口,这个是游戏逻辑无关的,它的存在只是为了满足按等级排序这个游戏逻辑的要求。
实现如下:
import "sort"
type Player struct {
name string
level int
}
// 队伍实现
type Team []*Player
func (t *Team) Join(p *Player) {
*t = append(*t, p)
}
func (t *Team) Quit(p *Player) {
for i, v := range *t {
if v.name == p.name {
copy((*t)[i:], (*t)[i+1:])
*t = (*t)[:len(*t)-1]
return
}
}
}
func (t *Team) Sort() {
sort.Sort(sortTeam(*t))
}
// 排序实现
type sortTeam Team
func (t sortTeam) Len() int {
return len(t)
}
func (t sortTeam) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
func (t sortTeam) Less(i, j int) bool {
return t[i].level < t[j].level
}
可以看到,一个slice,可以是玩家队伍,也可以是sort.Interface接口的实现。它们的数据仍然还是同一个slice,但是不同的type对应的行为特征却迥然不同。这很好地反映了现实世界里对同一个数据的不同解读,从代码维护的角度而言,也因为不同类别的逻辑相互分离从而使得代码更加清晰易读和容易维护。
我们继续把一人千面的精神发扬下去。这次增加一些需求:队伍还要根据名字来排序。实现如下:
import "sort"
// 队伍实现
type Player struct {
name string
level int
}
type Team []Player
func (t *Team) Join(p Player) {
// 实现同上
}
func (t *Team) Quit(p Player) {
// 实现同上
}
func (t *Team) SortByLevel() {
// 根据级别排序
sort.Sort(sortLevel(*t))
}
func (t *Team) SortByName() {
// 根据名字排序
sort.Sort(sortName(*t))
}
// 级别排序
type sortLevel Team
func (t sortLevel) Len() int {
return len(t)
}
func (t sortLevel) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
func (t sortLevel) Less(i, j int) bool {
return t[i].level < t[j].level
}
// 名字排序
type sortName Team
func (t sortName) Len() int {
return len(t)
}
func (t sortName) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
func (t sortName) Less(i, j int) bool {
return t[i].name < t[j].name
}
综上所述,Go语言通过type关键字很好地揭示了事物的本质,就是同一个事物可以有完全不同的一系列行为特征以至于它们表现像是不同的事物;而事物表象上的一系列行为特征的独立,对写出清晰易读易维护的代码提供了很好的帮助。Go语言这种对事物本质的透彻理解和把握,在C/C++语言里是看不到的。