介面與多型
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元