Go语言学习第八课-结构体与包(Go语言的面向对象)

      接下来讲解一下Go语言中的面向对象思想编程。在Go语言面向对象与其它面向对象语言有着很大的差别。首先Go语言的不存在继承和多态,而且不存在构造函数。并且Go语言不采用class来实现类,而是采用结构体加指针实现。不得不说,这让类的定义变得很复杂,但是又不失合理性。当具体操作起来后,我也只能慢慢适应Go语言的这种做法,具体优点缺点,将在以后使用中慢慢总结。

类(结构体)的定义

在Go语言中我们不要把类与结构体割裂开看,结构体加方法,就可以是一个类,可以定义结构体变量(对象)。

//go语言仅支持封装,不支持继承和多态
//面向接口编程
//没有class
//不论地址还是结构本身,一律使用.来访问成员

type Node struct {
	//无构造函数
	Value       int
	Left, Right *Node
}
//接收者,相当于this
func (node Node) Print() {
	fmt.Println(node.Value)
}
//指针接受者,编译器会自动识别指针和值,只有指针才能改变结构内容
//nil指针也可以调用方法
//一致性:如果有指针接收者,最好使用指针接受者
//值接收者是Go语言特有
//值/指针接收者均可接收值/指针
func (node *Node) SetValue(value int)  {
	if node==nil{
		fmt.Println("setting value  to nil")
		return
	}
	node.Value = value
}

func CreateNode( value int) *Node {
	//工厂函数
	//返回局部变量的地址
	return &Node{Value:value}
}

//要改变内容必须使用指针接收者
//结构过大也要考虑指针接收者

 看了上面的代码差不多就明白Go语言的面向对象设计理念了,与java,c++等强面向对象的语言不同,Go语言的成员变量与方法分开定义。struct中放成员变量,方法通过指定接收者来定义。其中怎么区分变量和方法是私有还是公有的呢?

就是通过变量名和函数名的大小写:

Go语言学习第八课-结构体与包(Go语言的面向对象)_第1张图片

那么在外部怎么去定义一个类的对象并调用方法呢?

在这里我们先引入包的概念。到目前为止,我们看到的 Go 程序都只有一个文件,文件里包含一个 main 函数和几个其他的函数。在实际中,这种把所有源代码编写在一个文件的方法并不好用。以这种方式编写,代码的重用和维护都会很困难。而包(Package)解决了这样的问题。

包用于组织 Go 源代码,提供了更好的可重用性与可读性。由于包提供了代码的封装,因此使得 Go 应用程序易于维护。

Go语言学习第八课-结构体与包(Go语言的面向对象)_第2张图片Go语言学习第八课-结构体与包(Go语言的面向对象)_第3张图片

我们定义了树的节点这个类。我们工程如此建立

Go语言学习第八课-结构体与包(Go语言的面向对象)_第4张图片

node节点中为上面的代码块,我们把遍历方法单独拿出来定义。entry作为主函数所在包,为程序入口。

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"
	"awesomeProject/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)
	nodes := []tree.Node{
		{Value:3},
		{},
		{6,nil,&root},
	}
	fmt.Println(nodes)
	root.Right.Left.SetValue(4)
	root.Right.Left.Print()
	var pRoot *tree.Node
	//空指针安全机制
	pRoot.SetValue(200)
	//pRoot.print()
	//root.Traverse()
	myroot := myTreeNode{&root}
	myroot.postOrder()

}

这里发现Go语言并没有那么严格的要求引用的包名,不一定是文件名。

如果我们需要扩展某个类的方法或者变量,在java或c++中直接进行继承和派生就可以。在Go语言中,主要有两种方法进行操作

Go语言学习第八课-结构体与包(Go语言的面向对象)_第5张图片

在上面entry.go中,我们对Node扩展了中序遍历树节点的方法。即对Node的方法扩充。这是组合方法。至于定义别名就是将已有结构体(类)当作变量,重新定义出自己的类。不做赘述,比如将切片扩展成队列。即将切片作为变量,自定义函数作为方法。整体成为一个新的数据结构类型

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
}

 

在以后的学习过程会遇到更多的问题,未完待续..........

你可能感兴趣的:(Golang专区,Go语言开发与实战)