iT邦幫忙

2022 iThome 鐵人賽

DAY 6
1
Software Development

30天學會Golang系列 第 6

Day06 - Go的 interface (下)

  • 分享至 

  • xImage
  •  

在 Go 中,interface與其他語言的寫法稍有不同,除了上一篇提到的可以處理各種資料型別的功能以外,另一個功能就是可以定義行為,也就是說 interface 中可以定義一些方法來表示一個對象的行為,而當我們有自定義的型態假設想要擁有這些行為,就是去實踐 interface 裡面的方法。比方說今天我們想要計算動物園裡頭某種動物的每年開銷,我們可以用以下的方式算得結果

type ZooAnimal interface {
	Feed() int
	Rest() int
	Place() string
}

type Lion struct {
	AnimalInfo Animal
}

type Animal struct {
	Amount    int
	FeedDaily int
	RestDaily int
	Place     string
}

func (lion *Lion) Feed() int {
	return lion.AnimalInfo.Amount * lion.AnimalInfo.FeedDaily
}

func (lion *Lion) Rest() int {
	return lion.AnimalInfo.Amount * lion.AnimalInfo.RestDaily
}

func (lion *Lion) Place() string {
	return lion.AnimalInfo.Place
}

func ShowFeedYear(animal ZooAnimal) int {
	feed_daily := animal.Feed()
	var ratio int
	switch animal.Place() {
	case "熱帶":
		ratio = 1
	case "寒帶":
		ratio = 2
	}
	return feed_daily * ratio * 365

}

func ShowRestYear(animal ZooAnimal) int {
	rest_daily := animal.Rest()
	return rest_daily * 365
}

func ShowTotalYear(animal ZooAnimal, kind string) {
	feed_year := ShowFeedYear(animal)
	rest_year := ShowRestYear(animal)
	fmt.Printf("%s 每年花費為 %d 元 (此為毫無根據的數字) \n", kind, feed_year+rest_year)
}

func main() {
	lion := &Lion{AnimalInfo: Animal{Amount: 2, FeedDaily: 500, RestDaily: 50, Place: "熱帶"}}
	ShowTotalYear(lion, "獅子")
}

輸出結果為:

獅子 每年花費為 401500 元 (此為毫無根據的數字) 

目前還看不出他的好處,反而覺得相對冗長,但是假設動物園的動物又突然多了企鵝,那我們只需要定義企鵝的基本資訊

// type ZooAnimal interface {
// 	Feed() int
// 	Rest() int
// 	Place() string
// }

// type Lion struct {
// 	AnimalInfo Animal
// }

// type Animal struct {
// 	Amount    int
// 	FeedDaily int
// 	RestDaily int
// 	Place     string
// }

// func (lion *Lion) Feed() int {
// 	return lion.AnimalInfo.Amount * lion.AnimalInfo.FeedDaily
// }

// func (lion *Lion) Rest() int {
// 	return lion.AnimalInfo.Amount * lion.AnimalInfo.RestDaily
// }

// func (lion *Lion) Place() string {
// 	return lion.AnimalInfo.Place
// }

// func ShowFeedYear(animal ZooAnimal) int {
// 	feed_daily := animal.Feed()
// 	var ratio int
// 	switch animal.Place() {
// 	case "熱帶":
// 		ratio = 1
// 	case "寒帶":
// 		ratio = 2
// 	}
// 	return feed_daily * ratio * 365

// }

// func ShowRestYear(animal ZooAnimal) int {
// 	rest_daily := animal.Rest()
// 	return rest_daily * 365
// }

// func ShowTotalYear(animal ZooAnimal, kind string) {
// 	feed_year := ShowFeedYear(animal)
// 	rest_year := ShowRestYear(animal)
// 	fmt.Printf("%s 每年花費為 %d 元 (此為毫無根據的數字) \n", kind, feed_year+rest_year)
// }

// func main() {
// 	lion := &Lion{AnimalInfo: Animal{Amount: 2, FeedDaily: 500, RestDaily: 50, Place: "熱帶"}}
// 	ShowTotalYear(lion, "獅子")
	penguin := &Penguin{AnimalInfo: Animal{Amount: 10, FeedDaily: 100, RestDaily: 100, Place: "寒帶"}}
	ShowTotalYear(penguin, "企鵝")
// }

func (penguin *Penguin) Feed() int {
	return penguin.AnimalInfo.Amount * penguin.AnimalInfo.FeedDaily
}

func (penguin *Penguin) Rest() int {
	return penguin.AnimalInfo.Amount * penguin.AnimalInfo.RestDaily
}

func (penguin *Penguin) Place() string {
	return penguin.AnimalInfo.Place
}

type Penguin struct {
	AnimalInfo Animal
}

輸出的結果為:

獅子 每年花費為 401500 元 (此為毫無根據的數字) 
企鵝 每年花費為 1095000 元 (此為毫無根據的數字) 

實際上還可以再優化一下,透過 switch 來做物種的分類,那就可以修改成如下資訊

// type ZooAnimal interface {
// 	Feed() int
// 	Rest() int
// 	Place() string
// }

// type Lion struct {
// 	AnimalInfo Animal
// }

// type Animal struct {
// 	Amount    int
// 	FeedDaily int
// 	RestDaily int
// 	Place     string
// }

// func (lion *Lion) Feed() int {
// 	return lion.AnimalInfo.Amount * lion.AnimalInfo.FeedDaily
// }

// func (lion *Lion) Rest() int {
// 	return lion.AnimalInfo.Amount * lion.AnimalInfo.RestDaily
// }

// func (lion *Lion) Place() string {
// 	return lion.AnimalInfo.Place
// }

// func ShowFeedYear(animal ZooAnimal) int {
// 	feed_daily := animal.Feed()
// 	var ratio int
// 	switch animal.Place() {
// 	case "熱帶":
// 		ratio = 1
// 	case "寒帶":
// 		ratio = 2
// 	}
// 	return feed_daily * ratio * 365

// }

// func ShowRestYear(animal ZooAnimal) int {
// 	rest_daily := animal.Rest()
// 	return rest_daily * 365
// }

func ShowTotalYear(animal ZooAnimal) {
// 	feed_year := ShowFeedYear(animal)
// 	rest_year := ShowRestYear(animal)
	fmt.Printf("每年花費為 %d 元 (此為毫無根據的數字) \n", feed_year+rest_year)
// }

// func main() {
// 	// lion := &Lion{AnimalInfo: Animal{Amount: 2, FeedDaily: 500, RestDaily: 50, Place: "熱帶"}}
// 	// ShowTotalYear(lion, "獅子")
// 	// penguin := &Penguin{AnimalInfo: Animal{Amount: 10, FeedDaily: 100, RestDaily: 100, Place: "寒帶"}}
// 	// ShowTotalYear(penguin, "企鵝")

	animals := [...]ZooAnimal{
		&Lion{AnimalInfo: Animal{Amount: 2, FeedDaily: 500, RestDaily: 50, Place: "熱帶"}},
		&Penguin{AnimalInfo: Animal{Amount: 10, FeedDaily: 100, RestDaily: 100, Place: "寒帶"}},
	}

	for _, animal := range animals {
		switch animal.(type) {
		case *Lion:
			fmt.Printf("獅子 ")
		case *Penguin:
			fmt.Printf("企鵝 ")
		default:
			fmt.Println("是誰偷偷混進來啊~!!")
		}
		ShowTotalYear(animal)
	}
// }

// func (penguin *Penguin) Feed() int {
// 	return penguin.AnimalInfo.Amount * penguin.AnimalInfo.FeedDaily
// }

// func (penguin *Penguin) Rest() int {
// 	return penguin.AnimalInfo.Amount * penguin.AnimalInfo.RestDaily
// }

// func (penguin *Penguin) Place() string {
// 	return penguin.AnimalInfo.Place
// }

// type Penguin struct {
// 	AnimalInfo Animal
// }

輸出結果如下:

獅子 每年花費為 401500 元 (此為毫無根據的數字) 
企鵝 每年花費為 1095000 元 (此為毫無根據的數字) 

可能用這樣的解釋,還是會有疑問,就是看不出優勢為何,但實際上這樣做的好處是對於大型程式碼開發上比較好管理,也比較容易劃分工作內容。以上面為例,就可以將其分成兩個工作內容,一個是專門生產各種不同動物的工程師,另一個是專門針對動物類型的會有哪些開銷,比方說,一開始的動物園要管理的動物只有五到十種動物,但我們為他們標上習慣的生活環境、需要吃的食物、休息時間等等,以方便管理並且計算最後這些動物的總開銷,當然很少隻動物時我們可以為每隻動物計算,但假設今天要管理的是一百到兩百種動物,那透過 interface 這種方式就能比較好閱讀,而且好處是,我們可以先定義一種動物,就像我們上面一開始先定義獅子,透過獅子我們就能完成整年的開銷計算,那麼無尾熊、熊貓、袋鼠或是大象等等,我們僅需定義前面的基本資訊,就可以獲得後面整年的開銷,不僅在程式碼上方便閱讀,也好管理,在未來新增的動物種類的擴編上也相較簡單,因此這種的使用時機,建議是在大型專案,以更實際的例子來說,就是電商在處理帳號登入,比方說帳號登入可能有 Google、FaceBook、自家的註冊登入方式,那麼我們今天只需要利用上面 interface 這種方式,就可以將負責帳號登入的工作獨立出來,然後成功登入的後續就可以交由其他人分配,就不需要特別為了 Google 登入的使用者寫一種購買與儲存購物清單方式,FaceBook 登入的使用者再寫另一種方式,然後自家的又要另一種,如此一來就能有效的避免不必要的人力浪費,但如果是小專案的話,就不建議這麼做,因為這樣就是用大砲打小鳥,也是另一種人力浪費,嘻嘻

第6天報到,今天來點可愛小動物,獅子跟企鵝還是很可愛,喜歡:)

參考來源

  1. https://reurl.cc/kEnVzr
  2. https://willh.gitbook.io/build-web-application-with-golang-zhtw/02.0/02.6

代碼連結

https://github.com/luckyuho/ithome30-golang/tree/main/day06


上一篇
Day05 - Go的 Array & Slice 與 interface (上)
下一篇
Day07 - Go的閉包
系列文
30天學會Golang31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言