Plymorphism in Go using interface

學習 Go interface 實踐多型

什麼是多型

對於「多型 Polymorphism」的概念理解像是「要做一件事,但這件事對不同對象來說實踐的方式不同」。具體來說像是各種「形狀」都可以「算面積」,但背後實踐的方法卻不同,而在 Go 當中 interface 就是型別用於定義方法的集合。

有了多型可以讓程式擴充時輕鬆擴張,又對修改封閉。

// Before
func getArea() {
if t == "circle" {
// 算圓面積
} else if t == "rectangle" {
// 算矩形面積
}
}
// After
type geometry interface {
area() float64
}

Go 與多型

如下案例透過 Go interface 隱性的創造 interface,只要某個型別具備介面要求的方法集合,就「自動」符合該 interface。

// 1. 圓形和矩形
type rect struct {
width, height float64
}
type circle struct {
radius float64
}
// 2. 定義「形狀」的合約定義上要有的方法(算面積)
type geometry interface {
area() float64
}
// 3. 測量方法必須接收「形狀」
func measure(g geometry) {
fmt.Println(g.area())
}
// 4. 實踐矩形圓形計算面積方法
func (r rect) area() float64 {
return r.width * r.height
}
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func main() {
r := rect{width: 3, height: 4}
c := circle{radius: 5}
measure(r)
measure(c)
}

Interface Composition

也可以透過嵌套 interface 達成 composition 的目的。

package main
import (
"fmt"
"math"
)
// 1. 定義矩形和圓形
type rect struct {
width, height float64
}
type circle struct {
radius float64
}
// 2. 定義單一責任的介面
type areaCalculator interface {
area() float64
}
type perimeterCalculator interface {
perimeter() float64
}
// 3. 使用 interface composition 組合成更大的介面
type geometry interface {
areaCalculator
perimeterCalculator
}
// 4. 為矩形和圓形實作
func (r rect) area() float64 {
return r.width * r.height
}
func (r rect) perimeter() float64 {
return 2 * (r.width + r.height)
}
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (c circle) perimeter() float64 {
return 2 * math.Pi * c.radius
}
// 5. 接收複合介面
func measure(g geometry) {
fmt.Printf("Area: %.2f, Perimeter: %.2f\n", g.area(), g.perimeter())
}
func main() {
r := rect{width: 3, height: 4}
c := circle{radius: 5}
measure(r)
measure(c)
}

延伸閱讀