- 原文地址:Part 28: Polymorphism - OOP in Go
- 原文作者:Naveen R
- 譯者:咔嘰咔嘰 轉載請註明出處。
Go 中的多型性是在介面的幫助下實現的。正如我們已經討論過的,介面可以在 Go 中隱式實現。如果型別為介面中宣告的所有方法提供定義,則型別實現介面。讓我們看看在介面的幫助下如何在 Go 中實現多型性。
使用介面實現多型
任何定義了介面的所有方法的型別都被稱為隱式實現了該介面。
一個介面型別的變數可以承載任何實現該介面的值。介面的這個屬性用於在 Go 中實現多型。
讓我們在一個計算組織淨收入的程式的幫助下來理解 Go 中的多型性。為簡單起見,我們假設這個想象中的組織有兩種專案的收入,即固定賬單,時間和材料。該組織的淨收入按這些專案的收入總和計算。為了簡化本教程,我們假設貨幣是美元,我們不會處理美分。它將使用int
表示。 (我建議閱讀該文章以瞭解如何表示美分。感謝 Andreas Matuschek 在評論部分指出了這一點。)
我們定義一個Income
結構,
type Income interface {
calculate() int
source() string
}
複製程式碼
上面定義的Income
介面包含兩個方法calculate()
,它計算並返回專案來源收入的和,source()
返回專案的名稱。
下一步定義FixedBilling
的結構
type FixedBilling struct {
projectName string
biddedAmount int
}
複製程式碼
FixedBilling
專案有兩個欄位projectName
,表示專案名稱,biddedAmount
是組織為專案出價的金額。
TimeAndMaterial
結構表示按時間計算收益的專案
type TimeAndMaterial struct {
projectName string
noOfHours int
hourlyRate int
}
複製程式碼
TimeAndMaterial
結構有三個欄位projectName
,noOfHours
和hourlyRate
。
下一步是定義這些結構型別的方法,這些方法計算並返回實際收入和收入來源。
func (fb FixedBilling) calculate() int {
return fb.biddedAmount
}
func (fb FixedBilling) source() string {
return fb.projectName
}
func (tm TimeAndMaterial) calculate() int {
return tm.noOfHours * tm.hourlyRate
}
func (tm TimeAndMaterial) source() string {
return tm.projectName
}
複製程式碼
對於FixedBilling
專案,收入只是專案的投標金額。因此我們從FixedBilling
型別的calculate()
方法返回它。
對於TimeAndMaterial
專案,收入是noOfHours
和hourlyRate
的乘積。返回值來自於使用接收者型別為TimeAndMaterial
的calculate()
方法。
我們用source()
方法來返回收入來源專案的名字。
由於FixedBilling
和TimeAndMaterial
結構都為Income
介面的calculate()
和source()
方法提供了定義,因此兩個結構都實現了Income
介面。
func calculateNetIncome(ic []Income) {
var netincome int = 0
for _, income := range ic {
fmt.Printf("Income From %s = $%d\n", income.source(), income.calculate())
netincome += income.calculate()
}
fmt.Printf("Net income of organisation = $%d", netincome)
}
複製程式碼
上面的calculateNetIncome
函式接受Income
介面的切片作為引數。它通過迭代切片並在每個專案上呼叫calculate()
方法來計算總收入。它還通過呼叫source()
方法顯示收入來源。根據Income
介面的具體型別,將呼叫不同的calculate()
和source()
方法。因此,我們在calculateNetIncome
函式中實現了多型性。
在將來,如果新增了一種新的收入來源,這個功能仍然可以正確計算總收入而無需一行程式碼更改:)。
接下來我們看看 main 函式的內容。
func main() {
project1 := FixedBilling{projectName: "Project 1", biddedAmount: 5000}
project2 := FixedBilling{projectName: "Project 2", biddedAmount: 10000}
project3 := TimeAndMaterial{projectName: "Project 3", noOfHours: 160, hourlyRate: 25}
incomeStreams := []Income{project1, project2, project3}
calculateNetIncome(incomeStreams)
}
複製程式碼
在上面的 main 函式中,我們建立了三個專案,兩個型別為FixedBilling
,另一個型別為TimeAndMaterial
。接下來,我們使用這 3 個專案建立一個型別為Income
的切片。由於這些專案中的每一個都實現了Income
介面,因此可以將所有三個專案新增到一個型別為Income
的片段中。最後,我們用這個切片呼叫calculateNetIncome
函式,它將顯示各種收入和收入來源。
整個程式如下,
package main
import (
"fmt"
)
type Income interface {
calculate() int
source() string
}
type FixedBilling struct {
projectName string
biddedAmount int
}
type TimeAndMaterial struct {
projectName string
noOfHours int
hourlyRate int
}
func (fb FixedBilling) calculate() int {
return fb.biddedAmount
}
func (fb FixedBilling) source() string {
return fb.projectName
}
func (tm TimeAndMaterial) calculate() int {
return tm.noOfHours * tm.hourlyRate
}
func (tm TimeAndMaterial) source() string {
return tm.projectName
}
func calculateNetIncome(ic []Income) {
var netincome int = 0
for _, income := range ic {
fmt.Printf("Income From %s = $%d\n", income.source(), income.calculate())
netincome += income.calculate()
}
fmt.Printf("Net income of organisation = $%d", netincome)
}
func main() {
project1 := FixedBilling{projectName: "Project 1", biddedAmount: 5000}
project2 := FixedBilling{projectName: "Project 2", biddedAmount: 10000}
project3 := TimeAndMaterial{projectName: "Project 3", noOfHours: 160, hourlyRate: 25}
incomeStreams := []Income{project1, project2, project3}
calculateNetIncome(incomeStreams)
}
複製程式碼
程式輸出,
Income From Project 1 = $5000
Income From Project 2 = $10000
Income From Project 3 = $4000
Net income of organisation = $19000
複製程式碼
在上述程式碼中新增新的收入來源
假設該組織通過做廣告有了新的收入來源。讓我們看看新增這個新的收入來源並計算總收入是多麼簡單,不需要對calculateNetIncome
函式進行任何更改。由於多型性,這成為可能。
讓我們首先定義一個Advertisement
的收入型別,然後給該型別加上calculate()
以及source()
方法。
type Advertisement struct {
adName string
CPC int
noOfClicks int
}
func (a Advertisement) calculate() int {
return a.CPC * a.noOfClicks
}
func (a Advertisement) source() string {
return a.adName
}
複製程式碼
Advertisement
型別有三個欄位:adName
,CPC
(每次點選費用)和noOfClicks
(點選次數)。廣告的總收入是CPC
和noOfClicks
的乘積。
讓我們稍微修改一下 main 函式,以包含這個新的收入來源。
func main() {
project1 := FixedBilling{projectName: "Project 1", biddedAmount: 5000}
project2 := FixedBilling{projectName: "Project 2", biddedAmount: 10000}
project3 := TimeAndMaterial{projectName: "Project 3", noOfHours: 160, hourlyRate: 25}
bannerAd := Advertisement{adName: "Banner Ad", CPC: 2, noOfClicks: 500}
popupAd := Advertisement{adName: "Popup Ad", CPC: 5, noOfClicks: 750}
incomeStreams := []Income{project1, project2, project3, bannerAd, popupAd}
calculateNetIncome(incomeStreams)
}
複製程式碼
我們建立了兩個名為bannerAd
和popupAd
的廣告收入。 incomeStreams
切片包含我們剛剛建立的兩個廣告。
加上後的整個程式碼如下,
package main
import (
"fmt"
)
type Income interface {
calculate() int
source() string
}
type FixedBilling struct {
projectName string
biddedAmount int
}
type TimeAndMaterial struct {
projectName string
noOfHours int
hourlyRate int
}
type Advertisement struct {
adName string
CPC int
noOfClicks int
}
func (fb FixedBilling) calculate() int {
return fb.biddedAmount
}
func (fb FixedBilling) source() string {
return fb.projectName
}
func (tm TimeAndMaterial) calculate() int {
return tm.noOfHours * tm.hourlyRate
}
func (tm TimeAndMaterial) source() string {
return tm.projectName
}
func (a Advertisement) calculate() int {
return a.CPC * a.noOfClicks
}
func (a Advertisement) source() string {
return a.adName
}
func calculateNetIncome(ic []Income) {
var netincome int = 0
for _, income := range ic {
fmt.Printf("Income From %s = $%d\n", income.source(), income.calculate())
netincome += income.calculate()
}
fmt.Printf("Net income of organisation = $%d", netincome)
}
func main() {
project1 := FixedBilling{projectName: "Project 1", biddedAmount: 5000}
project2 := FixedBilling{projectName: "Project 2", biddedAmount: 10000}
project3 := TimeAndMaterial{projectName: "Project 3", noOfHours: 160, hourlyRate: 25}
bannerAd := Advertisement{adName: "Banner Ad", CPC: 2, noOfClicks: 500}
popupAd := Advertisement{adName: "Popup Ad", CPC: 5, noOfClicks: 750}
incomeStreams := []Income{project1, project2, project3, bannerAd, popupAd}
calculateNetIncome(incomeStreams)
}
複製程式碼
上述程式輸出,
Income From Project 1 = $5000
Income From Project 2 = $10000
Income From Project 3 = $4000
Income From Banner Ad = $1000
Income From Popup Ad = $3750
Net income of organisation = $23750
複製程式碼
您會注意到雖然我們新增了新的收入來源,但我們沒有對calculateNetIncome
函式進行任何更改。這就是多型性的作用。由於新的Advertisement
型別也實現了Income
介面,我們可以將它新增到incomeStreams
切片中。 calculateNetIncome
函式也沒有任何變化,因為它能夠呼叫Advertisement
型別的calculate()
和source()
方法。