go语言知识分享(三):面向对象和依赖管理

本文探讨了Go语言中面向对象的特性,如不支持继承和多态但可通过接口实现,以及如何使用struct、工厂函数和指针操作。同时介绍了Go的包管理和依赖管理,包括GOPATH、GOVENDOR和gomod的使用。
摘要由CSDN通过智能技术生成

面向对象

仅支持封装,不支持继承和多态,没有像python的mro操作;继承和多态转化为面向接口编程

go语言没有class 只有struct

struct 结构体

  • 不论地址还是结构本身,一律使用.来访问成员
  • 没有构造函数,可以使用工厂函数,注意返回的是局部变量的地址,go是允许的
  • 我们不需要知道go的变量是放在堆或者栈上的,因为go语言内部的垃圾回收机制
  • go语言函数在运行结束之后,其中的局部变量不一定会马上销毁
package main

import "fmt"

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

func createNode(value int) *treeNode { // 工厂函数
	return &treeNode{value: value} //这里返回的是一个局部变量的地址,在go里面是允许的不会报错
}

func main() {
	var root treeNode
	fmt.Println(root)
	root = treeNode{value: 3}
	root.left = &treeNode{}
	root.right = &treeNode{5, nil, nil}
	root.right.left = new(treeNode)
	root.left.right = createNode(2)

	nodes := []treeNode{
		{value: 3},
		{},
		{6, nil, &root},
	}
	fmt.Println(nodes)
}

给结构体定义方法

Func (node TreeNode) print(){ fmt.print(node.value)}

显示定义和命名方法接收者

只有使用指针才可以改变结构内容,传入指针

nil指针也可以调用方法,可以使用,安全的

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 createNode(value int) *treeNode { // 工厂函数
	return &treeNode{value: value} //这里返回的是一个局部变量的地址,在go里面是允许的不会报错
}

func main() {
	var root treeNode
	fmt.Println(root)
	root = treeNode{value: 3}
	root.left = &treeNode{}
	root.right = &treeNode{5, nil, nil}
	root.right.left = new(treeNode)
	root.left.right = createNode(2)

	root.print()
	root.right.left.setValue(4)
	root.right.left.print()

	root.print()       // 值接受者,或拷贝一份给函数
	root.setValue(100) // 给地址

	pRoot := &root
	pRoot.print()
	pRoot.setValue(200)
	pRoot.print()

	var rRoot *treeNode
	rRoot.setValue(200)
	rRoot = &root
	rRoot.setValue(300)
	rRoot.print()

}

遍历方法

func (node *treeNode) traverse() {
	if node == nil {
		return
	}
	node.left.traverse()
	node.print()
	node.right.traverse()
}

值接收者vs指针接收者

  • 要改变内容需要使用指针接收者
  • 结构过大也需要考虑使用指针接收者
  • 一致性:如有指针接收者,最好都是指针接收者
  • 值接收者是go语言特有的,指针接收者其他都有
  • 值/指针接收者均可以接收值或者指针

包和封装

  • 名字使用驼峰命名法
  • 首字母大写:pulic
  • 首字母小写:private
  • public和private针对包来说的,每个目录只有一个包(一个目录中所有文件的包名称相同)
  • main包包含可执行入口,main包对应mian函数
  • 为结构定的方法必须包含在同一个包中,可以是不同的文件
  • 包名称和目录名称不一定相同,但是一个目录只能有一个包名称
  • main是入口包,独立于其他功能包
  • goland 定义的gopath需要包含src文件夹,下面的子目录是所有的包

扩展已有类型

扩充系统类型或别人的类型

  • 定义别名
  • 使用组合
// 组合的方式
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{5, nil, nil}
	root.Right.Left = new(tree.Node)
	root.Left.Right = tree.CreateNode(2)
	root.Right.Left.SetValue(4)
	root.Traverse()

	fmt.Println()
	myRoot := myTreeNode{&root}
	myRoot.postOrder()
	fmt.Println()

}

// 使用别名

package queue

type Queue []int  //别名

func (q *Queue) Push(v int) {
	*q = append(*q, v) // 改变q指向的slice改变了
}

func (q *Queue) Pop() int {
	head := (*q)[0]
	*q = (*q)[1:]
	return head
}

func (q *Queue) IsEmpty() bool {
	return len(*q) == 0
}

使用内嵌的方式扩展已有类型

Embedding方式,类似使用组合的方式,可以省下许多代码,但是这种方法是熟练的人看得懂

package main

import (
	"fmt"
	"learngo/tree"
)

type myTreeNode struct {
	*tree.Node // Embedding内嵌,语法糖,省略代码
}

func (myNode *myTreeNode) postOrder() {
	if myNode == nil || myNode.Node == nil {
		return
	}
	left := myTreeNode{myNode.Left}
	right := myTreeNode{myNode.Right}
	left.postOrder()
	right.postOrder()
	myNode.Print()

}

func (myNode *myTreeNode) Traverse() {
	fmt.Printf("this method is shadowed.")
}

func main() {
	root := myTreeNode{&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.Right.Left.SetValue(4)
	root.Traverse() // 本身的traverse
	root.Node.Traverse() // 肚子中node的traverse

	fmt.Println()
	root.postOrder()
	fmt.Println()

	//var baseRoot *tree.Node
	//baseRoot:=$root 会报错,和其他语言中的继承不相同

}

Go的依赖管理

依赖管理

  • 编译程序需要的三方库
  • 依赖管理的三个阶段:GOPATH,GOVENDOR,go mod

Gopath/Govendor

  • 默认在~/go(unix, linux)
  • 所有的项目都需要放在gopath下面,造成gopath非常的大,会导致gopath非常的大,所有的import都在下面找
  • 历史:google将20亿行代码,9百万个文件放在一个repo里
  • gopath下面必须要有个目录为src,source,存放项目的包代码
  • 一个目录下面可以创建一个vendor目录,放置本项目需要特定的第三方库
  • 大量的第三方依赖管理工具:glide,dep,go dep,…
gopath管理
go env -w 目录 # 将整个系统目录转化为这个
export GOPATH = 具体目录  在gopath下面创建src  必须有一个这个目录,在这个下面放各种的代码,项目目录
采用gopaht的方式而不用gomoduel: GO111MODULE="off"


第三方库的查找和安装:
1.建立的每个目录可以有一个project gopath, 也可以公用一个global gopath
2.第三方库的查找:goroot的src下面,gopath下面的src下面查找
3.每个项目目录下面创建一个vendor目录
引入包的语言: go get 

God mod

使用gomod进行文件管理,下载的第三方包在制定的gopath下面的pkg里面,但是有go.mod和其下面的go.sum文件进行管理;

 go get -u go.uber.org/zap@1.11 #获取指定版本的库, 在需要使用的目录下面,下载的包在上级目录的pkg中
 
 go mod tidy # 清洁go.sum下面的多余的包
 
 go get -u go.uber.org/zap #升级到最新的版本
 
 go mod init name #创建gomod文件,如果有用其他管理工具,直接用这个就可以,不用下面那个了
 
 go build ./... # build当前目录和所有子目录文件,加载所有需要的包
  • go命令统一管理,用户不需要关心目录结构
  • 初始化:go mod init 项目名称
  • 增加依赖:go get 类似pip
  • 更新依赖 go get [@v…]
  • 清洁go mod文件: go mod tidy;查看go对应包直接在github中
  • 项目迁移go mod: go mod init, go build ./…

目录整理

一个目录只有一个main文件,意思是只有一个main包,如果多个main文件,需要放到不同的子目录下

go build ./... # 编译所有目录和子目录文件
go install ./... # 在src同级目录bin下查看编译文件
  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值