1. 程式人生 > >Golang學習筆記1-組合和介面

Golang學習筆記1-組合和介面

主要內容

1.組合
2.介面
3.常見問題
4.總結

1.組合

Golang中沒有OOP中繼承的概念,取而代之的是組合,同樣起到屬性和方法繼承 的作用,特別是匿名組合,其表現形式和繼承的行為類似,但是卻有很多不同的地方,相對於繼承、多型這些典型的OOP思想,組合顯得更加簡潔靈活。

1.1.一般組合

type Wifi struct {
    Name string
}

type Phone struct {
     WifiMdl Wifi
}

func Test_Group(t *testing.T) {
    phone := Phone{}
    phone.WifiMdl = Wifi{
        Name: "..."
} ... }

這是一個簡單的示例,直接將其他的型別作為自己的內部成員;那麼新型別的例項就可以直接對此成員進行賦值和初始化;

1.2.匿名組合

當我們嵌入一個型別,這個型別的方法就變成了外部型別的方法,但是當它被呼叫時,方法的接受者是內部型別(嵌入型別),而非外部型別。— Effective Go

理解了這句話也就理解匿名組合:簡單地可以理解為,內部型別的成員名可理解為內部型別名,在外部型別未定義相同名稱的屬性和方法時,內部方法可提升為外部型別的屬性和方法,但實際上,屬性和方法的所有者和呼叫的接受仍然為內部型別

// 內部型別
type People struct
{ Name string } func (p *People) GetName() { log.Print(p.Name) } // 外部型別 type Man struct { People } func Test_Group(t *testing.T) { man := Man{} man.Name = "villare" man.GetName() }

匿名組合,即嵌入的型別沒有自己的屬性名,這種方式的組合和一般的組合最大的不同在於,外部型別的例項可以直接通過自身訪問內部型別的屬性和方法,“看上去外部型別作為屬性和方法的所有者”,形式上與繼承的類似;

特別的,如果內部型別也為指標

// 內部型別
type People struct {
    Name string
}

func (p *People) GetName() {
    log.Print(p.Name)
}

// 外部型別
type Man struct {
    *People
}

func Test_Group(t *testing.T) {
    man := Man{}
    //以下程式碼報錯(invalid memory address or nil pointer dereference...)
    //先初始化 man := Man{&People{},} 
    man.Name = "villare"
    man.GetName()
}

指標型別的匿名組合在直接呼叫內部型別的方法時,會出現地址未分配的錯誤;可見,指標型別的匿名組合需要在外部型別初始化的同時進行手動初始化,不會自動初始化內部型別;

由此可見,匿名組合中內部方法的接受者實際上還是內部型別;

2.介面

2.1.介面的定義

type People interface {
    Sex()
}

介面的定義和其他語言有相似之處,直接定義介面方法,不過不支援靜態成員和預設方法的定義形式;

由於golang介面的實現形式使得它沒必要支援這些特性

2.2.介面的實現

type People interface {
    Sex()
}

type Man struct {}

func (m *Man) Sex() {
    log.Print("Man")
}

type Woman struct {}

func (m *Woman) Sex() {
    log.Print("Woman")
}

func Test_Inter(t *testing.T) {
    var t1 People

    t1 = new(Man)
    t1.Sex()

    t1 = new(Woman)
    t1.Sex()
} 

golang的介面實現遵循一個原則:如果一個型別實現了介面定義的所有方法,那麼就認為此型別實現了這個介面

golang中方法不能重名,不支援過載

golang不使用介面指標,直接使用介面型別,注意和java、c++的區別

3.常見問題

3.1.組合中的訪問控制

golang預設的訪問控制規則是:首字母大寫(Public),首字母小寫(private)

對於組合來說,內部型別的私有成員在外部型別中可見,但是仍然為私有成員(不可通過例項直接訪問)

3.2.嵌入衝突

type Time struct {
    Name string
}

// 正確示例
type UTC struct {
    time.Time
    T2 Time
}

// 錯誤示例
type UTC struct {
    time.Time
    Time
}

在嵌入多個型別時,保證成員名不同,匿名組合的成員名為型別名稱

即使不同包路徑不同,只要型別名相同,匿名組合均有衝突

3.2.匿名組合的衝突

注意一點,成員和方法的優先順序:
外部型別 > 內部型別 > 內部型別的內部型別 > …

如果出現歧義(Ambiguous),通過加型別名的形式呼叫,明確一點“屬性和方法的所有者和呼叫者均屬於內部方法”

關注下面一個例項

type People struct {
    Name string
}

func (p *People) GetSex() {
    log.Print("Man Or Woman")
}

type Man struct {
    People
}

func (p *Man) GetSex() {
    log.Print("Man")
}

type Child struct {
    Man
}


func Test_Group(t *testing.T) {
    cld := Child{}
    cld.GetSex()
    cld.Man.GetSex()
    cld.People.GetSex()
}

//輸出為
Man
Man
Man Or Woman

4.總結

總的來說,golang組合和介面的兩個概念還是相對比較好理解的,規則也更加契合主觀認知,減少了理解和學習的成本;但在簡潔的同時也並沒有減弱golang語言本身的表達和設計能力;