二十七.golang的多态
Go 通过[接口]来实现多态。我们已经讨论过,在 Go 语言中,我们是隐式地实现接口。一个类型如果定义了接口所声明的全部[方法],那它就实现了该接口。现在我们来看看,利用接口,Go 是如何实现多态的。
一个类型如果定义了接口的所有方法,那它就隐式地实现了该接口。
所有实现了接口的类型,都可以把它的值保存在一个接口类型的变量中。在 Go 中,我们使用接口的这种特性来实现多态。
通过一个程序我们来理解 Go 语言的多态,它会计算一个组织机构的净收益。为了简单起见,我们假设这个虚构的组织所获得的收入来源于两个项目:fixed billing 和 time and material
我们首先定义一个接口 Income。
type Income interface {
calculate() int
source() string
}
上面定义了接口 Interface,它包含了两个方法:calculate() 计算并返回项目的收入,而 source()
下面我们定义一个表示 FixedBilling 项目的结构体类型。
type FixedBilling struct {
projectName string
biddedAmount int
}
项目 FixedBillin 有两个字段:projectName 表示项目名称,而 biddedAmount 表示组织向该项目投标的金额。
TimeAndMaterial
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 }
在项目
而在项目 TimeAndMaterial 里面,收入等于 noOfHours 和 hourlyRate 的乘积,作为 TimeAndMaterial 类型的 calculate() 方法的返回值。
我们还通过 source() 方法返回了表示收入来源的项目名称。
由于 FixedBilling 和 TimeAndMaterial 两个结构体都定义了 Income 接口的两个方法:calculate() 和 source(),因此这两个结构体都实现了 Income 接口。
我们来声明一个 calculateNetIncome 函数,用来计算并打印总收入。
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) }
上面的[函数]接收一个 Income 接口类型的[切片]作为参数。该函数会遍历这个接口切片,并依个调用 calculate() 方法,计算出总收入。该函数同样也会通过调用 source() 显示收入来源。根据 Income 接口的具体类型,程序会调用不同的 calculate() 和 source() 方法。于是,我们在 calculateNetIncome
如果在该组织以后增加了新的收入来源,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 类型。接着我们创建了一个 Income 类型的切片,存放了这三个项目。由于这三个项目都实现了 Interface 接口,因此可以把这三个项目放入 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
假设前面的组织通过广告业务,建立了一个新的收益流(Income Stream)。我们可以看到添加它非常简单,并且计算总收益也很容易,我们无需对 calculateNetIncome
我们首先定义 Advertisement 类型,并在 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
现在我们稍微修改一下 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) }
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