十六、go 包和封装
一、封装和包
- 名字一般使用
CamelCase
- 首字母大写:
public
- 首字母小写:
private
public
、private
是针对“包”来说的
- 每个目录一个包:包名和目录名不一样要一样,每一个目录只有一个包
main
包:包含可执行入口,只有main
包才可以执行main
函数- 为结构定义的方法,必须放在同一个包内,可以是不同文件
二、包-扩展已有类型
如何扩充系统类型或者别人的类型,两种方法扩展:
1. 定义别名扩充类型(最简单)
slice
定义一个别名 Queue
,然后进行扩充
package queue
type Queue []int
func (q *Queue) Push(v int) {
*q = append(*q, v)
}
func (q *Queue) Pop() int {
head := (*q)[0]
*q = (*q)[1:]
return head
}
func (q *Queue) IsEmpty() bool {
return len(*q) == 0
}
package main
import (
"fmt"
queue "learngo/14queue"
)
func main() {
q := queue.Queue{1}
q.Push(2)
q.Push(3)
fmt.Println(q.Pop())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
}
2. 使用组合扩充类型(最常用)
package main
import (
"fmt"
"learngo/tree"
)
// 组合的方式扩展类型,把原先的类型,当作新类型的一个参数
type myTreeNode struct {
node *tree.Node
}
// 后序遍历
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.node == nil {
return
}
left := myTreeNode{myNode.node.Left}
right := myTreeNode{myNode.node.Right}
left.postOrder()
right.postOrder()
myNode.node.Print()
}
func main() {
var root tree.Node
root = tree.Node{Value: 3}
root.Left = &tree.Node{}
root.Right = &tree.Node{Value: 5}
node := myTreeNode{&root}
node.postOrder()
}
3. 使用内嵌(省下许多代码)
内嵌是在组合的情况下,修改为内嵌的
package main
import (
"fmt"
tree "learngo/13tree"
)
type myTreeNode struct {
// Embedding 这种不指定名称的方式,就是内嵌
*tree.Node
}
// 后序遍历
func (myNode *myTreeNode) postOrder() {
// 注意这里,判断的时候还是要用 myNode.Node
if myNode == nil || myNode.Node == nil {
return
}
left := myTreeNode{myNode.Node.Left} // myNode.Node 方式访问到内嵌类型
right := myTreeNode{myNode.Right} // 直接通过访问 myNode 也能实现效果,这就是内嵌的好处,可以直接调到 myNode.Node 内容
left.postOrder()
right.postOrder()
myNode.Print()
}
func (myNode *myTreeNode) Traverse() {
fmt.Println("myTreeNode 的 Traverse")
}
func main() {
// root 直接初始化为 myTreeNode类型,下面直接调用roo就行
root := myTreeNode{&tree.Node{Value: 3}}
root.Left = &tree.Node{}
root.Right = &tree.Node{Value: 5}
fmt.Println("后序遍历")
root.postOrder()
root.Node.Traverse() // 这里调的是root.Node的Traverse
// 这里打印的 myTreeNode 定义的 Traverse
// 虽然他们看起来像继承,其实就是一个语法糖
// 如果myTreeNode定义了同名方法,就调用自己定义的同名方法,否则调用Node的方法
root.Traverse()
// go 不支持下面的写法,其它语言继承可以,这也是和继承的一个区别
// 如果go也想实现下面的功能,使用的是接口
// var baseRoot *tree.Node
// baseRoot := &root
}