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 原始碼,提供了更好的可重用性與可讀性
我們定義了樹的節點這個類。我們工程如此建立
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
}
在以後的學習過程會遇到更多的問題,未完待續..........