1. 程式人生 > >Go語言學習第八課-結構體與包(Go語言的面向物件)

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 程式都只有一個檔案,檔案裡包含一個 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
}

在以後的學習過程會遇到更多的問題,未完待續..........