Golang the Fun Part - Struct and Interfacce

之前說要寫一篇 Go 簡介…不過網路上 Go 的資料已經很豐富,把一些我喜歡 Go 的點記錄下來好了。

Go 是物件導向的語言嗎?是,也不是。

他沒有類別,也沒有繼承。我們來用實例來看看 Go 如何實現物件導向的特性。

Struct

有接觸過 c-like 語言的人應該都對 struct 不陌生,我們可以定義一組結構,裡面包含各種資料型態的變數。

舉例來說我們可以定義一個叫 Humanstruct

1
2
3
4
type Human struct {
  name string
  age int
}

然後我們就可以這樣來使用:

1
2
3
4
5
6
person := Human{"Ash", 18}
//或者
person := Human{name:"Ash", age:18}

person.name
person.age

不一樣的地方是,我們可以給這個 struct 定義方法:

1
2
3
4
5
func (human *Human)Eat() {
  fmt.Println("Eating")
}

person.Eat()

我們可以定義另一個 struct 來「繼承」Human 的屬性和方法,例如我們定義一個 F2E

1
2
3
4
5
type F2E struct {
  Human
  cssLevel int
  javascriptLevel int
}

然後我們就可以這樣使用 F2E

1
2
3
4
5
6
aar0n := F2E{Human{name:"aar0n", age:35}, 80, 90}
aar0n.Eat()
// 當然也可以 access Human 的屬性
aar0n.name
// 或者
aar0n.Human.name

我們也可以讓 F2E override Human 的屬性跟方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
type F2E struct {
  Human
  cssLevel int
  javascriptLevel int
  name int
}

func (f2e *F2E)Eat() {
  fmt.Println("F2E does not eat!")
}
// 還是可以 access Human 的屬性跟方法
aar0n.Human.name
aar0n.Human.Eat()

Type 其他技巧

type 基本上是一個 alias 資料型態的關鍵字,不只可以使用在 struct 上。例如我們可以定義一個 Value Object 叫 Money

1
2
3
4
5
6
7
8
9
type Money int

// Money 也可以有方法
func (money Money)Disappear() {
  fmt.Println("Magic!")
}

money := Money(100)
money.Disappear()

Interface

Go 另外一個很棒的設計是 interface 來實現多型。基本上 interface 的概念是,假設你會作某些事,我就把你當這個對象。

例如我們定義一個 interfaceRD,條件是要會 Coding

1
2
3
type RD interface {
  Coding()
}

然後我們幫剛剛的 F2E 加一個 Coding() 的方法,他就滿足了 RD 這個 interface

1
2
3
4
5
6
7
8
9
10
func (f2e *F2E)Coding() {
  fmt.Println("I write cool css and javascript!")
}

// RD(會 Coding) 可以工作
func work(rd RD) {
  rd.Coding()
}

work(&aar0n)

所以我們可以再從 Human 繼承出一個 Backend 出來,一樣實作 Coding() 方法,他也就符合了 RD 這個 interface,一樣可以丟去工作。

1
2
3
4
5
6
7
8
9
10
11
type Backend struct {
  Human
}
func (backend *Backend)Coding() {
  fmt.Println("I write Rails applications!")
}

// 凡是 RD(會 Coding) 就給我去工作
ilake := Backend{Human{"ilake", 30}}
work(&aar0n)
work(&ilake)

更有趣的地方是,interface 也可以組合(繼承)。例如我們再定義一個 interfaceDesigner 條件是會 Design()

1
2
3
type Designer interface {
  Design()
}

那我們就可以稱「又會 Design 又會 Coding」的人叫全端工程師(FullStack):

1
2
3
4
5
6
7
8
9
10
11
12
// 同時滿足 RD 跟 Designer 兩個 interface
type FullStack interface {
  RD
  Designer
}

// 既是全端工程師,又會唱歌跳舞,那你肯定是 CTO 了
type CTO interface {
  FullStack
  Dance()
  Sing()
}

Interface 其他技巧

在 Go 裡面,所有的資料型態都滿足「空的 interface」 interface{}。所以如果我們真的有一個 slice 或方法,裡面要塞可能是任何型態的變數,我們就可以使用「空 interface」:

1
2
3
4
5
6
7
8
9
// 隨便你傳
func DoSomething(obj interface{}) {
  //...
}

// 隨便你塞
ary := make([]interface{}, 2)
ary[0] = 1
ary[1] = "string"

Comments