Go 中的 OOP - 多态性

Go 中的多态性是通过接口实现的。正如我们已经讨论过的,接口在 Go 中是隐式实现的。如果类型为接口中声明的所有方法提供定义,则该类型实现了接口。让我们看看 Go 中如何借助接口实现多态性。

使用接口的多态性

任何为接口的所有方法提供定义的类型都被称为隐式实现该接口。当我们稍后讨论多态性的示例时,这一点将会更加清楚。

接口类型的变量可以保存实现该接口的任何值。Go 中利用接口的这一特性来实现多态性。

让我们借助一个计算组织净收入的程序来了解 Go 中的多态性。为了简单起见,我们假设这个虚构的组织有来自两种项目的收入,即。固定计费时间和材料。该组织的净收入是根据这些项目的收入之和计算的。为了使本教程简单,我们假设货币是美元。它将使用 来表示int

我们首先定义一个接口Income

type Income interface {  
    calculate() int
    source() string
}

上面定义的接口Income包含两个方法calculate(),分别计算并返回来源的收入和source()返回来源的名称。

接下来,让我们为FixedBilling项目类型定义一个结构体。

type FixedBilling struct {  
    projectName string
    biddedAmount int
}

FixedBilling项目有两个字段projectNamebiddedAmount,分别代表项目名称和组织对该项目投标的金额。

TimeAndMaterial结构将代表时间和材料类型的项目。

type TimeAndMaterial struct {  
    projectName string
    noOfHours  int
    hourlyRate int
}

TimeAndMaterial结构体具有三个字段名称projectNamenoOfHourshourlyRate

下一步是在这些结构类型上定义方法,计算并返回实际收入和收入来源。

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()方法返回的

就时间和材料类型的项目,收入是noOfHourshourlyRate的乘积。该值是从接收者类型为TimeAndMaterial 的calculate()方法返回的

我们返回项目名称作为该方法的收入来源source()

由于 FixedBillingTimeAndMaterial结构都提供了接口的方法的定义,因此两个结构都实现了该接口。calculate()source()

让我们声明一个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 organization = $%d", netincome)
}

calculateNetIncome 上面的函数接受一个接口片段Income作为参数。它通过迭代切片并调用calculate()其每个项目的方法来计算总收入。它还通过调用source()方法显示收入来源。根据Income接口的具体类型,将调用不同的calculate()source()方法。这样我们就实现了函数的多态性

将来,如果组织添加了一种新的收入来源,该函数仍然可以正确计算总收入,而无需更改任何代码:)。

程序中唯一剩下的部分是主函数。

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的切片中。最后,我们调用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 organization = $%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)
}

Run program in playground

该程序将输出

Income From Project 1 = $5000  
Income From Project 2 = $10000  
Income From Project 3 = $4000  
Net income of organization = $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(点击次数)。广告总收入是CPCnoOfClicks的乘积。

让我们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)
}

我们制作了两个广告,即bannerAdpopupAd。该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 organization = $%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)
}

Run program in playground

上述程序将输出,

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 organization = $23750  

calculateNetIncome您可能已经注意到,尽管我们添加了新的收入来源,但我们并未对该功能进行任何更改。它只是因为多态性而起作用。由于新Advertisement类型也实现了该Income接口,因此我们能够将其添加到incomeStreams切片中。该calculateNetIncome函数无需任何更改即可运行,因为它能够调用该类型的calculate()source()方法。

本教程到此结束。祝你有美好的一天。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值