golang笔记04--golang 面向对象
1 介绍
本文继上文 golang笔记03–golang 內建容器, 进一步了解 golang 面向对象和相应注意事项。
具体包括 : 结构体和方法、包和封装、扩展已有类型、使用内嵌来扩展已有类型等。
2 面向对象
2.1 结构体和方法
go 语言仅支持封装,不支持继承和多态;
go 语言没有class, 只有struct;
package main
import "fmt"
type treeNode struct {
value int
left, right *treeNode
}
func createNode(value int) *treeNode {
// 使用自定义工厂函数,可以返回局部变量的地址
return &treeNode{value: value}
}
func (node treeNode) print() {
// 此处node为接收者
fmt.Print(node.value, " ")
}
func (node *treeNode) setValue(value int) {
//此处如果不传指针,则为按值传递,无法达到修改值的目的
if node == nil {
fmt.Println("setting value to nil node, ignored")
return
}
node.value = value
}
func (node *treeNode) traverse() {
// 中序(中根)遍历tree: 首先遍历左子树,然后访问当前根节点,最后遍历右子树
if node == nil {
return
}
node.left.traverse()
node.print()
node.right.traverse()
}
func main() {
fmt.Println("hello chapter4.1")
var root treeNode
root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
fmt.Println(root, root.right)
root.right.left = new(treeNode)
root.left.right = createNode(2)
fmt.Println(root.right)
root.print() //直接调用print函数
root.setValue(33)
root.print()
fmt.Println("\ntraverse tree")
root.setValue(3)
root.right.left.setValue(4)
root.traverse()
/*
nodes := []treeNode{
{value: 3},
{},
{6, nil, &root},
}
fmt.Println(nodes)
*/
}
输出:
hello chapter4.1
{3 0xc000112000 0xc000112020} &{5 <nil> <nil>}
&{5 0xc000112080 <nil>}
3 33
traverse tree
0 2 3 4 5
2.2 包和封装
包:
每个目录一个包
main包内部包含可执行入口
为结构定义的方法必须放在同一个包内,但是可以是不同文件
封装:
名字一般使用 CamelCase
首字母大写: public
首字母小写: private
以下案例中 4.2.go 中是使用tree包,entry 目录中的main.go 使用main包;
tree$ tree -L 2
.
├── 4.2.go
└── entry
└── main.go
vim 4.2.go
package tree
import "fmt"
type Node struct {
Value int
Left, Right *Node
}
func (node Node) Print() {
// 此处node为接收者
fmt.Print(node.Value, " ")
}
func (node *Node) SetValue(Value int) {
//此处如果不传指针,则为按值传递,无法达到修改值的目的
if node == nil {
fmt.Println("setting Value to nil node, ignored")
return
}
node.Value = Value
}
func (node *Node) Traverse() {
// 中序遍历tree: 首先遍历左子树,然后访问当前节点,最后遍历右子树
if node == nil {
return
}
node.Left.Traverse()
node.Print()
node.Right.Traverse()
}
vim main.go
package main
import (
"fmt"
"learngo/chapter4/tree"
)
func main() {
fmt.Println("hello chapter4.2 main.go")
var root tree.Node
root = tree.Node{Value: 3}
root.Left = &tree.Node{}
root.Right = &tree.Node{Value: 5}
root.Right.Left = new(tree.Node)
root.Left.Right = &tree.Node{Value: 2}
fmt.Println("traverse tree")
root.Traverse()
}
输出:
hello chapter4.2 main.go
traverse tree
0 2 3 0 5
2.3 扩展已有类型
go语言可以通过定义别买或者使用组合的方式扩展已有的类型。
定义别名:
tree$ tree -L 2
.
├── 4.2.go
└── entry
├── 4.3.go
└── main.go
vim 4.3.go
package main
import (
"fmt"
"learngo/chapter4/tree"
)
type myTreeNode struct {
// 使用组合
node *tree.Node
}
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.node == nil {
return
}
left := myTreeNode{myNode.node.Left}
left.postOrder()
right := myTreeNode{myNode.node.Right}
right.postOrder()
myNode.node.Print()
}
func main() {
fmt.Println("hello chapter4.3 main.go")
var root tree.Node
root = tree.Node{Value: 3}
root.Left = &tree.Node{}
root.Right = &tree.Node{Value: 5}
root.Right.Left = new(tree.Node)
root.Left.Right = &tree.Node{Value: 2}
fmt.Println("traverse postOrder tree")
myRoot := myTreeNode{&root}
myRoot.postOrder()
}
输出:
traverse postOrder tree
2 0 0 5 3
使用组合:
queue$ tree -L 2
.
├── entry
│ └── main.go
└── queue.go
vim queue.go
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
}
vim main.go
package main
import (
"fmt"
"learngo/chapter4/queue"
)
func main() {
fmt.Println("chapter 4.3, new queue")
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())
fmt.Println(q.IsEmpty())
}
输出:
chapter 4.3, new queue
1
2
false
3
true
2.4 使用内嵌来扩展已有类型
go 语言中可以通过 *packegeName.结构 的形式来内嵌已有的类型;
go 语言中可以实现重载,但是不能类似于C++将子类的指针赋值个父类指针。
vim 4.4.go
package main
import (
"fmt"
"learngo/chapter4/tree"
)
//embedding
type myTreeNode struct {
*tree.Node
}
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.Node == nil {
return
}
left := myTreeNode{myNode.Left}
left.postOrder()
right := myTreeNode{myNode.Right}
right.postOrder()
myNode.Print()
}
func (myNode *myTreeNode) Traverse() {
// 该方法类似于c++中的重载
fmt.Println("4.4 Traverse")
}
func main() {
fmt.Println("chapter 4.4")
root := myTreeNode{&tree.Node{Value: 3}}
root.Left = &tree.Node{}
root.Right = &tree.Node{Value: 5}
root.Right.Left = new(tree.Node)
root.Left.Right = &tree.Node{Value: 2}
root.Right.Left.SetValue(4)
fmt.Println("traverse postOrder tree")
root.postOrder()
fmt.Println()
root.Traverse() //调用 myTreeNode 的 Traverse
root.Node.Traverse() //调用 Node 的 TraVerse
}
输出:
chapter 4.4
traverse postOrder tree
2 0 4 5 3
4.4 Traverse
0 2 3 4 5
3 注意事项
- 如何选择值接收者 & 指针接收者
1)要改变内容必须使用指针接收者
2)结构过大也考虑使用指针接收者
3)建议:如果有指针接收者,最好都是指针接收者
4 说明
- 软件环境
go版本:go1.15.8
操作系统:Ubuntu 20.04 Desktop
Idea:2020.01.04 - 参考文档
由浅入深掌握Go语言–慕课网
go 语言编程–许式伟