接下来讲解一下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 程序都只有一个文件,文件里包含一个 main 函数和几个其他的函数。在实际中,这种把所有源代码编写在一个文件的方法并不好用。以这种方式编写,代码的重用和维护都会很困难。而包(Package)解决了这样的问题。
包用于组织 Go 源代码,提供了更好的可重用性与可读性。由于包提供了代码的封装,因此使得 Go 应用程序易于维护。
我们定义了树的节点这个类。我们工程如此建立
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语言中,主要有两种方法进行操作
在上面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
}
在以后的学习过程会遇到更多的问题,未完待续..........