@phper
2018-03-13T10:06:46.000000Z
字数 6213
阅读 5278
Golang
原文:https://golangbot.com/polymorphism/
欢迎访问Golang 系列教程中的第28章。
Go中的多态性是通过接口实现的。正如我们已经讨论过的,接口可以在Go中隐式实现。如果一个类型为接口中声明的所有方法提供了定义,则实现一个接口。让我们来看看在接口的帮助下Go如何实现多态。
定义接口的所有方法的任何类型都表示隐式实现该接口。
类型接口的变量可以保存实现该接口的任何值。接口的这个属性用于实现GO的多态性。
让我们了解多态性在一个程序的帮助下, 计算一个组织的净收入。为了简单起见, 我们假设这个假想的组织从两种项目 (即固定计费和时间和材料) 中得到收入。该组织的净收入由这些项目的收入总和计算。为了使本教程简单, 我们将假设货币是美元, 我们不会处理美分。它将使用int表示。(我建议您阅读https://forum.golangbridge.org/t/what-is-the-proper-golang-equivalent-to-decimal-when-dealing-with-money/413以了解如何表示美分。
让我们先定义一个接口Income.
type Income interface {calculate() intsource() string}
上面定义的Income接口包含两个方法calculate() , 它计算并返回和source的收入。source()它返回source的名称。
接下来, 让我们为FixedBilling项目类型定义一个结构。
type FixedBilling struct {projectName stringbiddedAmount int}
FixedBilling项目有两个字段projectName, 它代表项目的名称, biddedAmount, 它表示组织为项目出价的金额。
TimeAndMaterial结构将表示改项目的时间和材料类型。
type TimeAndMaterial struct {projectName stringnoOfHours inthourlyRate 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的产品。此值从calculate()方法返回, 其接收器类型为TimeAndMaterial.
我们将项目的名称作为来自source()方法的收入来源返回。
由于FixedBilling和TimeAndMaterial结构都提供Income接口的calculate()和source()方法的定义, 因此,这两个结构都实现了Income接口。
让我们声明一个计算和打印总收入的函数calculateNetIncome。
func calculateNetIncome(ic []Income) {var netincome int = 0for _, 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函数中实现了多态。
将来如果组织增加了一种新的收入来源, 这个函数仍然可以正确计算总收入, 而不需要单行代码更改:)。
程序中唯一剩下的部分是主函数。
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 mainimport ("fmt")type Income interface {calculate() intsource() string}type FixedBilling struct {projectName stringbiddedAmount int}type TimeAndMaterial struct {projectName stringnoOfHours inthourlyRate 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 = 0for _, 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)}
在playground上运行
该程序将输出
Income From Project 1 = $5000Income From Project 2 = $10000Income From Project 3 = $4000Net income of organisation = $19000
比方说, 该组织通过广告找到了新的收入来源。让我们看看如何简单地添加新的收入流, 并计算总收入而不对calculateNetIncome函数进行任何更改。这成为可能, 因为多态性。
让我们首先定义Advertisement类型上的Advertisement类型和calculate()和source()方法。
type Advertisement struct {adName stringCPC intnoOfClicks 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 mainimport ("fmt")type Income interface {calculate() intsource() string}type FixedBilling struct {projectName stringbiddedAmount int}type TimeAndMaterial struct {projectName stringnoOfHours inthourlyRate int}type Advertisement struct {adName stringCPC intnoOfClicks 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 = 0for _, 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 = $5000Income From Project 2 = $10000Income From Project 3 = $4000Income From Banner Ad = $1000Income From Popup Ad = $3750Net income of organisation = $23750
您可能会注意到, 我们没有对calculateNetIncome函数进行任何更改, 尽管我们添加了新的收入流。它只是因为多态性而起作用。由于新的Advertisement类型还实现了Income接口, 因此我们能够将其添加到incomeStreams切片中。calculateNetIncome函数还可以在不进行任何更改的情况下工作, 因为它能够调用Advertisement类型的calculate()和source()方法。
这使我们和本教程结束。祝你今天开心。