Go(8) - interface and polymorphism

interface 定義一些需要具備的函式,讓滿足 interface 規範的物件具有特定功能。


1. 定義一個 interface:

定義一個 interface 叫 IInfomation,具有 Info() 簽名的物件可以被當成是具有 IInfomation 的能力。

定義一個 struct 叫 Computer 並添加 Info(),即 Computer has-a Info()。

type IInfomation interface {
    Info() string // (1) 宣告需具備的函式,不包含實作 (可宣告多個函式)
}

type Computer struct {
    prodictID string
}

func (c Computer) Info() string {
    // (2) 實作 Info() string,所以 Computer has-a Info
    return fmt.Sprintf("ProductID: %s", c.prodictID)
}

func GetInfo(i IInfomation) {
    // (3) 呼叫物件 i,i 具有 IInfomation 的功能所以有 i.Info()
    fmt.Println(i.Info())
}

func main() {
    computer := Computer{"12345"}
    GetInfo(computer)// (4) computer.Info()
}

2. 什麼是 Polymorphism:

參考 Object-Oriented Software Engineering 對 Polymorphism 的解釋。

If an instance sends a stimulus to another instance, but does not have to be aware of which class the receiving instance belongs to, we say that we have polymorphism.

白話的來說我們宣告函式時只在乎物件是否具有我們要求的能力,這樣可以讓程式碼較為簡潔。

考慮下面的例子:

func (c Computer) Info() string {
    // Computer 實作 Info() string
}

func (c Cat) Info() string {
    // Cat 實作 Info() string
}

!! 注意 !! Go 實際上不支援 overloading

func GetInfo(c Computer) {
    fmt.Println(i.Info())
}

func GetInfo(c Cat) {
    fmt.Println(i.Info())
}

有 IInfomation 的 polymorphism 我們確保物件具有 Info(),所以變成

func GetInfo(i IInfomation) {
    fmt.Println(i.Info())
}

// Computer 和 Cat 都有 Info
GetInfo(computer)
GetInfo(cat)

3. 滿足 Polymorphism 的情形:

有兩種情形我們可以確保物件具有特定的能力。

  • 透過繼承實現的 is-a 關係
  • 透過介面實現的 has-a 關係

Go 中無法透過組合實現 Polymorphism (組合不會讓 Go 有 is-a 關係)。

一般有繼承能力的語言 (如: Java, Scala),繼承的同時會形成 is-a 關係。例如 Dog 繼承 Animal,所以 Dog is a Animal。繼承後子類 Dog 可以使用所有父類 Animal 的函式。

組合 Animal 可以確定物件具有某些功能,但 Go 沒有轉型的能力。考慮下面例子:

package main

import "fmt"

type Animal struct {
    name string
    age  int
}

func (a *Animal) WhoAmI() string {
    return fmt.Sprintf("Name: %s, age: %d", a.name, a.age)
}

type Dog struct {
    Animal
    Breed string
}

func Introduction(a Animal) {
    a.WhoAmI()
}

func main() {
    d := Dog{Animal{"White", 10}, "Beagles"}
    fmt.Println(d.WhoAmI())
    Introduction(d) // cannot use d (type Dog) as type Animal
}

由上例可知 Go 實現了類似 prototype chain 的查找但無法把 Dog 轉型成 Animal。

通常語言容許滿足多個介面但只允許單一繼承,所以實務上最好透過介面實現 Polymorphism。


留言

熱門文章