对象,是一个永恒的话题
—— 开发实习生
面向对象
Go 里面的面向对象,没有继承,没有多态这些花里胡哨的东西,它就一个封装。
结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func main() { var root TreeNode
root = TreeNode{value: 0}
root.left = &TreeNode{} root.right = &TreeNode{nil , nil , 1} root.right.left = new(TreeNode) fmt.Println(root) }
type TreeNode struct { left , right *TreeNode value int }
|
Go 里面的对象和我们C里面学的结构体基本上是一致的。而且在Go里面也是没有什么构造函数啥的,但是我们要是一定要构造函数怎么办,一般可以使用工厂函数,比如说:
1 2 3
| func createTreeNode(value int) *TreeNode { return &TreeNode{value: value} }
|
这里虽然返回的是一个局部的变量的地址,在C++中,显然是不可以的,但是这里是可以。
1 2 3 4 5 6 7
| func (tree TreeNode) printTree(){ fmt.Println(tree.value) } func (tree TreeNode) setValue(value int){ tree.value = value }
|
这是两个方法,请注意,这里的 setValue 是无法修改的,因为这里传递的是值,无法去修改原内容,如果想要修改的话,需要这么来写:
1 2 3
| func (tree *TreeNode) setValue(value int){ tree.value = value }
|
值接收者 VS 指针接收者
- 要改变内容,必须使用指针接收者
- 结构过大也考虑使用指针接收者
- 值接收者,是 go 的特有
- 值 / 指针 接收者 均可接收 值 / 指针
封转
- 名字一般使用 CamelCase
- 首字母大写代表 public
- 首字母小写代表 private
包
- 每个目录只能有一个包
- main 包包含可执行入口
- 为结构体定义的方法必须在同一个包内
- 可以是不同文件
扩充系统类型或者别人的类型
1 2 3 4 5 6 7 8
| func (tree *TreeNode) travel() { if tree == nil { return } tree.left.travel() tree.printTree() tree.right.travel() }
|
可以看到,我这里的遍历方式是 中序遍历,但是现在我们要是在使用的时候,我们不想中序遍历了,但是代码是别人的,我们怎么去做?用Java的话来说就是像重写怎么办呢?可以这么来做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| type CustomeTreeNode struct { node *TreeNode }
func (node *CustomeTreeNode) printOrder() { if node == nil || node.node == nil { return } treeNode := CustomeTreeNode{node: node.node.left} treeNode.printOrder() customeTreeNode := CustomeTreeNode{ node: node.node.right, } customeTreeNode.printOrder() node.node.printTree() }
|
方便更好的去理解这个封装,这里可以看下,使用切片实现的简单的队列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| type Queue []interface{}
func (q *Queue)Push(o interface{}){ *q = append(*q , o) }
func (q *Queue)Pop() interface{}{ head := (*q)[0] *q = (*q)[1:] return head }
func (q *Queue) IsEmpty() bool{ return len(*q) ==0 }
|