天天看點

Go 分布式學習利器(12)-- Go語言的擴充和複用

Go語言無法天然支援繼承,但是又想要實作面向對象的特性。

即父類對象 使用子類對象初始化,那麼該父類對象調用的函數就是子類實作的函數 ,進而滿足LSP(子類交換原則)。

案例一: Go語言 支援擴充父類的功能,如下代碼:

package oriented_test

import (
  "fmt"
  "testing"
)

// Pet 類
type Pet struct {
}

func (p *Pet) Speak(){ // Pet類的函數成員
  fmt.Print("Pet speak.\n")
}

func (p *Pet) SpeakTo(host string) { // Pet類的函數成員
  p.Speak()
  fmt.Println("Pet SpeakTo ", host)
}

// Dog 擴充Pet的功能
type Dog struct {
  p *Pet
}

// 擴充父類的方法
func (d *Dog) Speak(){
  d.p.Speak()
}

// 擴充父類的方法
func (d *Dog) SpeakTo(host string) {
  d.Speak()
  fmt.Println("Dog Speakto ", host)
}

func TestDog(t *testing.T) {
  dog := new(Dog)
  dog.SpeakTo("Test dog")
}      

以上測試代碼的輸出如下:

Pet speak.
Dog Speakto  Test dog      

dog的SpeakTo中調用了dog 的Speak,其中調用了Pet的Speak,是以輸出正常。

Pet 和 Dog調用不會互相影響,完全由使用者決定。

但是這和我們所想需要的不同,Pet類有自己的方法,Dog類有自己的方法,兩者作用域完全不同。

這裡Go語言推出了匿名嵌套類型,即Dog類不用實作自己的和Pet類同名的方法即可,通過在Dog類的聲明中變更Pet成員。

案例二: Go語言支援匿名函數類型

// Dog 擴充Pet的功能
type Dog struct {
  Pet
}      

這樣即不需要Dog聲明自己的同名函數成員,預設的調用即為Pet成員函數的調用

package oriented_test

import (
  "fmt"
  "testing"
)

type Pet struct {
}

func (p *Pet) Speak(){
  fmt.Print("Pet speak.\n")
}

func (p *Pet) SpeakTo(host string) {
  p.Speak()
  fmt.Println("Pet SpeakTo ", host)
}

// Dog 擴充Pet的功能
type Dog struct {
  Pet // 支援匿名嵌套類型,
}

func TestDog(t *testing.T) {
  var dog Dog

  dog.Speak()
  dog.SpeakTo("Test dog")
}      

最終的輸出如下:

=== RUN   TestDog
Pet speak.
Pet speak.
Pet SpeakTo  Test dog
--- PASS: TestDog (0.00s)      

調用的都是Pet的成員函數,感覺像是繼承了,因為繼承預設就是子類能夠使用父類的公有成員。

在匿名嵌套類型下,我們想要完整嘗試一下Go語言是否真正支援繼承,可以像之前的代碼一樣在Dog中實作Pet的同名函數,且能夠通過父類對象調用子類的成員方法,像C++/Java這樣進行向上類型轉換(本身是不可能的,Go語言不支援顯式類型轉換)。

案例三: Go語言不支援繼承,如下代碼:

package oriented_test

import (
  "fmt"
  "testing"
)

type Pet struct {
}

func (p *Pet) Speak(){
  fmt.Print("Pet speak.\n")
}

func (p *Pet) SpeakTo(host string) {
  p.Speak()
  fmt.Println("Pet SpeakTo ", host)
}

// Dog 擴充Pet的功能
type Dog struct {
  //p *Pet
  Pet // 支援匿名嵌套類型
}

// 重載父類的方法
func (d *Dog) Speak(){
  fmt.Print("Dog speak.\n")
}

// 重載父類的方法
func (d *Dog) SpeakTo(host string) {
  d.Speak()
  fmt.Println("Dog Speakto ", host)
}

func TestDog(t *testing.T) {
  var dog Pet = new(Dog)

  dog.Speak()
  dog.SpeakTo("Test dog")
}      

在最後的輸出會編譯出錯,不支援将Pet類型轉換為Dog類型:

./oriented_test.go:38:6: cannot use new(Dog) (type *Dog) as type Pet in assignment