1. 程式人生 > 實用技巧 >介面與多型

介面與多型

0. 介面是什麼?

這一段摘自 Go語言中文網

在面向物件的領域裡,介面一般這樣定義:介面定義一個物件的行為。介面只指定了物件應該做什麼,至於如何實現這個行為(即實現細節),則由物件本身去確定。

在 Go 語言中,介面就是方法簽名(Method Signature)的集合。當一個型別定義了介面中的所有方法,我們稱它實現了該介面。這與面向物件程式設計(OOP)的說法很類似。介面指定了一個型別應該具有的方法,並由該型別決定如何實現這些方法

1. 如何定義介面

使用 type 關鍵字來定義介面。

如下程式碼,定義了一個電話介面,要求實現 call 方法。

type Phone interface
{ call() }

2. 如何實現介面

如果有一個型別/結構體,實現了一個介面要求的所有方法,這裡 Phone 介面只有 call方法,所以只要實現了 call 方法,我們就可以稱它實現了 Phone 介面。

意思是如果有一臺機器,可以給別人打電話,那麼我們就可以把它叫做電話。

這個介面的實現是隱式的,不像 JAVA 中要用 implements 顯示說明。

繼續上面電話的例子,我們先定義一個 Nokia 的結構體,而它實現了 call 的方法,所以它也是一臺電話。

type Nokia struct {
    name string
}

// 接收者為 Nokia
func (phone Nokia) call() {
    fmt.Println(
"我是 Nokia,是一臺電話") }

3. 介面實現多型

鴨子型別(Duck typing)的定義是,只要你長得像鴨子,叫起來也像鴨子,那我認為你就是一隻鴨子。

舉個通俗的例子

什麼樣子的人可以稱做老師呢?

不同的人標準不一,有的人認為必須有一定的學歷,有的人認為必須要有老師資格證。

而我認為只要能育人,能給傳授給其他人知識的,都可以稱之為老師。

而不管你教的什麼學科?是體育競技,還是教人烹飪。

也不管你怎麼教?是在教室裡手執教教鞭、拿著粉筆,還是追求真實,直接實戰演練。

通通不管。

這就一個介面(老師)下,在不同物件(人)上的不同表現。這就是多型。

在 Go 語言中,是通過介面來實現的多型。

這裡以商品介面來寫一段程式碼演示一下。

先定義一個商品(Good)的介面,意思是一個型別或者結構體,只要實現了settleAccount()orderInfo()兩個方法,那這個型別/結構體就是一個商品。

type Good interface {
    settleAccount() int
    orderInfo() string
}

然後我們定義兩個結構體,分別是手機和贈品。

type Phone struct {
    name string
    quantity int
    price int
}

type FreeGift struct {
    name string
    quantity int
    price int
}

然後分別為他們實現 Good 介面的兩個方法

// Phone
func (phone Phone) settleAccount() int {
    return phone.quantity * phone.price
}
func (phone Phone) orderInfo() string{
    return "您要購買" + strconv.Itoa(phone.quantity)+ "" + 
        phone.name + "計:" + strconv.Itoa(phone.settleAccount()) + ""
}

// FreeGift
func (gift FreeGift) settleAccount() int {
    return 0
}
func (gift FreeGift) orderInfo() string{
    return "您要購買" + strconv.Itoa(gift.quantity)+ "" + 
        gift.name + "計:" + strconv.Itoa(gift.settleAccount()) + ""
}

實現了 Good 介面要求的兩個方法後,手機和贈品在Go語言看來就都是商品(Good)型別了。

這裡候,我挑選了兩件商品(例項化),分別是手機和耳機(贈品,不要錢)

iPhone := Phone{
    name:     "iPhone",
    quantity: 1,
    price:    8000,
}
earphones := FreeGift{
    name:     "耳機",
    quantity: 1,
    price:    200,
}

然後建立一個購物車(也就是型別為 Good的切片),來存放這些商品。

goods := []Good{iPhone, earphones}

最後,定義一個方法來計算購物車裡的訂單金額

func calculateAllPrice(goods []Good) int {
    var allPrice int
    for _,good := range goods{
        fmt.Println(good.orderInfo())
        allPrice += good.settleAccount()
    }
    return allPrice
}

完整程式碼,我貼在下面,供你參考。

package main

import (
    "fmt"
    "strconv"
)

// 定義一個介面
type Good interface {
    settleAccount() int
    orderInfo() string
}

type Phone struct {
    name string
    quantity int
    price int
}

func (phone Phone) settleAccount() int {
    return phone.quantity * phone.price
}
func (phone Phone) orderInfo() string{
    return "您要購買" + strconv.Itoa(phone.quantity)+ "" + 
        phone.name + "計:" + strconv.Itoa(phone.settleAccount()) + ""
}

type FreeGift struct {
    name string
    quantity int
    price int
}

func (gift FreeGift) settleAccount() int {
    return 0
}
func (gift FreeGift) orderInfo() string{
    return "您要購買" + strconv.Itoa(gift.quantity)+ "" + 
        gift.name + "計:" + strconv.Itoa(gift.settleAccount()) + ""
}

func calculateAllPrice(goods []Good) int {
    var allPrice int
    for _,good := range goods{
        fmt.Println(good.orderInfo())
        allPrice += good.settleAccount()
    }
    return allPrice
}
func main()  {
    iPhone := Phone{
        name:     "iPhone",
        quantity: 1,
        price:    8000,
    }
    earphones := FreeGift{
        name:     "耳機",
        quantity: 1,
        price:    200,
    }

    goods := []Good{iPhone, earphones}
    allPrice := calculateAllPrice(goods)
    fmt.Printf("該訂單總共需要支付 %d 元", allPrice)
}

執行後,輸出如下

您要購買1個iPhone計:8000元
您要購買1個耳機計:0元