Go 第4章 面向“对象”

2021-06-09

一、结构体和方法

  • go 语言仅支持封装,不支持继承和多态

  • go 语言没有class,只有struct

package main
import "fmt"
type treeNode struct {
    value   int
    left, right *treeNode
}
//为结构定义方法
func (node treeNode) print() {
    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( ) {
    if node == nil{
        return
    }
    node.left.traverse( )
    node. print( )
    node.right.traverse( )
}
func createTreeNode(value int) *treeNode {
    return &treeNode{value: value}
}
func main( ) {
    var root treeNode
    root = treeNode{value: 3}
    root.left = &treeNode{}
    root.right = &treeNode{5, nil, nil}
    root.right.left = new(treeNode)
    root.left.right = createTreeNode(2)
    
    //遍历树
    root.traverse()
    fmt.Println()
    
    //为结构定义方法
    root.right.left.print()
    root.right.left.setValue(4)
    root.right.left.print()
    fmt.Println()
    root.print()
    root.setValue(100)
    root.print()
    fmt.Println()
    
    pRoot := &root
    pRoot.print()
    pRoot.setValue(200)
    pRoot.print()
    fmt.Println()
  
    //nil 指针也可以调用方法!
    var qRoot *treeNode
    qRoot.setValue(200)
    qRoot = &root
    qRoot.setValue(300)
    qRoot.print()
    fmt.Println()
}

https://file.lulublog.cn/images/3/2022/08/B7A997PuVp9a9vp5a5g57RPA9apjg5.png

  • 值接收者 vs 指针接收者

    • 要改变内容必须使用指针接收者

    • 结构过大也考虑使用指针接收者

    • 一致性︰如有指针接收者,最好都是指针接收者

    • 值接收者是 go 语言特有

    • 值/指针接收者均可接收值/指针

二、包和封装

2.1、封装

  • 名字一般使用 CamelCase

  • 首字母大写:public

  • 首字母小写:private

2.2、包

2.2.1、注意事项

  • 每个目录一个包

  • main 包包含可执行入口

  • 为结构定义的方法必须放在同一个包内

  • 可以是不同文件

2.2.2、示例

  • 文件目录

https://file.lulublog.cn/images/3/2022/08/Uc66v8ez807qIrKXTIQ6d6zIXc3C6R.png

  • tree.go

package tree

import "fmt"

type Node struct {
    Value   int
    Left, Right *Node
}

func CreateNode(value int) *Node {
    return &Node{Value: 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) Print() {
    fmt.Print(node.Value, " ")
}
  • traverse.go

package tree

func (node *Node) Traverse( ) {
    if node == nil{
        return
    }
    node.Left.Traverse( )
    node.Print( )
    node.Right.Traverse( )
}
  • entry.go

package main
import (
    "fmt"
    "learn/tree"
)
func main( ) {
    var root tree.Node
    
    root = tree.Node{Value: 3}
    root.Left = &tree.Node{}
    root.Right = &tree.Node{5, nil, nil}
    root.Right.Left = new(tree.Node)
    root.Left.Right = tree.CreateNode(2)
    
    root.SetValue(100)
    root.Traverse()
    
    fmt.Println()
}
  • 执行

go run /mnt/go/src/learn/tree/entry/entry.go
  • 执行结果

0 2 100 0 5

三、扩展已有类型

3.1、定义别名

  • entry.go

package main
import (
   "fmt"
   "learn/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{5, nil, nil}
   root.Right.Left = new(tree.Node)
   root.Left.Right = tree.CreateNode(2)
   
   root.SetValue(100)
   root.Traverse()
   
   fmt.Println()
   
   myRoot := myTreeNode{&root}
   myRoot.postOrder()
   
   fmt.Println()
}

3.2、使用组合

  • 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
}

  • entry.go

package main

import (
    "fmt"
    "learn/queue"
)

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())
    fmt.Println(q.IsEmpty())
}

  • 代码目录

https://file.lulublog.cn/images/3/2022/08/gp7307M6L6wIiiMAPVWol7v6V6M7pA.png

  • 运行结果

1
2
false
3
true

四、GOPATH以及目录结构

4.1、注意事项

  • 默认在~/go(unix, linux),%USERPROFILE%\go (windows)

  • 官方推荐∶所有项目和第三方库都放在同一个 GOPATH 下

  • 也可以将每个项目放在不同的 GOPATH

  • 获取 GOPATH 目录

echo $GOPATH
  • 设置 GOPATH

vim /etc/profile

export GOROOT=/usr/local/go #设置为go安装的路径
export GOPATH=/mnt/go #go项目路径
export PATH=$PATH:$GOPATH/bin:$GOROOT/bin #以冒号分隔

source /etc/profile
  • GOPATH 下的目录结构

https://file.lulublog.cn/images/3/2022/08/AGOG4pzBpgF94FPnir7rB7yNoiOav2.png

4.2、go get 获取第三方库

  • 使用 gopm 来获取无法下载的包

go get -v github.com/gpmgo/gopm

执行后之后会在 GOPATH 目录生成如下文件

https://file.lulublog.cn/images/3/2022/08/LmWwlNN6jG79oh2m2pWp0JPYYPhPG2.png

https://file.lulublog.cn/images/3/2022/08/et59P6Z1150SsyYTl5Ph1ATm595Aap.png

  • gopm 帮助

gopm help get

https://file.lulublog.cn/images/3/2022/08/f9eEG9Qc9zEmdE09eMem9t0hIegGHh.png

  • 安装 goimports

gopm get -g -v -u golang.org/x/tools/cmd/goimports
  • go build来编译

  • go install 产生pkg 文件和可执行文件

cd /mnt/go/src/learn/tree
go intsall

https://file.lulublog.cn/images/3/2022/08/ayfP0m2gMwgN2r2E0z9eq5urn9r3gA.jpg

  • go run 直接编译运行

阅读 644