前端开发者的GO语言之路(3) —— 结构体和方法、包和封装、拓展已有类型、GOPATH以及目录结构

1. 面向对象

  • go语言仅支持封装,不支持继承和多台
  • go语言没有class只有struct

1.1 创建一个树形结构

结构的创建:

type treeNode struct {
	value       int
	left, right *treeNode
}

使用treeNode创建一个如下图所示的树形结构:

image.png

func main(){
    root = treeNode{value: 3} // var返回的是一个值
	root.left = &treeNode{}
	root.right = &treeNode{5, nil, nil}
	root.right.left = new(treeNode) // new返回的是一个地址
}

可以直接使用slice来创建多个node:

nodes := []treeNode{
    {value: 3},
    {},
    {6, nil, &root},
}

使用工厂 函数创建一个叶子:

// 工厂函数
func createNode(value int) *treeNode {
	return &treeNode{value: value} // 返回了一个局部变量的地址,这在Go语言中是允许的
}

func main() {
    root = createNode(3)
    fmt.Println(root) // &{0 <nil> <nil>}
    fmt.Println(*root) // {0 <nil> <nil>}
}

1.2 遍历树形结构

1.2.1 接收者方式调用函数

首先了解一下函数调用者方式去调用函数,我们设置一个方法可以输出当前节点的value:

func (node treeNode) print() {
	fmt.Println(node.value)
}

func main(){
    var root treeNode
    root.print() // 0
}

这种方法的调用方式我们称之为接收者方式调用,node指的就是 接收者 。其中接收者又分为 值接收者指针接收者

  • 值接收者将调用方法的对象作为一份拷贝在方法中引用,在函数内对接收者进行的操作 不会影响调用该方法的对象本身
  • 指针接收者则是将调用方法的对象的地址引用近函数内,在函数内对接收者进行操作 会影响调用该方法的对象本身

同时,接收者方式调用方法,只是方法执行了另外一种形式,如上述的函数方法,其等价于:

func () print(node treeNode) {
	fmt.Println(node.value)
}

func main(){
    var root treeNode
    print(root) // 0
}

接收者方式调用方法的好处是可以更清晰的调用函数,如我们定义一个 setValue() 方法:

func (node *treeNode) setValue(value int) {
	node.value = value
}

这里要注意的是我们需要使用 指针接收者 调用方法,因为我们需要修改调用方法的对象本身。

之后,我们就可以按照如下方式使用,来更改节点的value:

node.setValue(233) 

另外,为了方便使用,Go语言会将调用方法的地址对象或者实体对象自动转换为地址或者实体,如:

var node treeNode // 用var方法创建的是一个实体
root.setValue("233") // Go语言会自动将root转化为地址对象

nil指针也可以调用方法

  • 要改变内容必须使用指针接收者
  • 结构大也考虑使用指针接收者
  • 尽量都使用指针接收者

1.2.2 先序遍历

func (node *treeNode) traverse() {
	if node == nil {
		return
	}
    // 由于nil也可以调用方法,所以在此无需判断是当前调用者是否是nil
	node.print()
	node.left.traverse()
	node.right.traverse()
}

2. 封装

封装:

  • 函数方法使用驼峰命名
  • public 公共变量首字母大写
  • privite 私有变量首字母小写

包:

  • 一个目录下只能有一个package
  • main包包含可执行入口
  • 结构定义的方法必须放在同一个包内
  • 可以是不同文件

3. 包

3.1 如何扩充系统类型或者别人的类型

3.1.1 使用组合

当我们想要在别人定义的结构体(类型)上创建方法,但是我们会发现在自己的包下无法给非本地的结构体扩展方法:

func (node *tree.Node) postOrder() { // 报错
    // do something here
}

那么如果我们想要拓展方法则需要使用组合,定义另一种结构体类型,结构体内部指向原有的类型数据,这样我们就可以通过操作新创建的结构体来访问外部结构体:

type myTreeNode struct {
	node *tree.Node
}

之后我们可以创建 myTreeNode 类的数据:

var root tree.Node
// ... create a tree here
myRoot := myTreeNode{&root}

现在我们可以通过在 myTreeNode 数据类型上创建方法来访问到 tree.Node 类型了,于是我们在 myTreeNode 上创建一个后序遍历的方法:

// 后序遍历
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()
}

使用后序遍历:

var root tree.Node
// ... create a tree here
fmt.Println("后序遍历:")
myRoot := myTreeNode{&root}
myRoot.postOrder()

3.1.2 定义别名

如果我们想要在 slice 上创建一个 push 方法是肯定不被允许的,所以我们可以创建一个结构体以供操作,然而这个结构体本身就是一个 slice,所以我们只是将 slice 重新命名而已,如:

type Queue []int

在函数中创建一个 Queue,他本身实际就是一个 slice,可以按照 slice 的方法进行操作:

q := new(Queue) // &[]

我们这样就可以在定义的 Queue 上创建方法了:

func (q *Queue) Push(v int) {
	*q = append(*q, v)
}

使用定义的 Push 方法:

q.Push(2)
fmt.Println(q) // &[2]

4. GOPATH环境变量

go get 获取第三方库:

go get golang.org/x/tools/cmd/goimports

gopm 下载第三方包:

go get -v -u github.com/gpmgo/gopm
...
gopm get -g -v golang.org/x/tools/cmd/goimports

使用 go install 指令可以将当前所有的文件安装到 GOPATH 的 bin 目录下

5. 文件配置

每个文件夹中只能存在一个拥有 mian() 函数的文件

发布了48 篇原创文章 · 获赞 28 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u012925833/article/details/100772257