Golang教程第28节--多态-Go面向对象编程

原文来自:https://golangbot.com/polymorphism/

Go语言通过接口interface来实现多态。我们前面已经学过,接口可以在Go中隐式实现。如果一个类型为接口中声明的所有方法提供了定义,则实现一个接口。让我们来看看在接口的帮助下Go如何实现多态。

接口实现多态

任何一种类型,定义了接口所声明的所有方法,那么就称隐式实现了接口。

接口类型的变量可以保存实现该接口的任何值。接口的这个特性用于实现GO的多态性。

让我们通过一个计算公司的净收入的程序来了解Go的多态。让我们假设,该公司有两种收入途径:固定材料和按时间与材料计费。公司净收入时这两种途径之和。为了方便起见,我们使用整型变量。

首先定义一个接口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结构有三个字段名称projectNamenoOfHours 和 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 = 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函数中实现了多态。

将来如果组织增加了一种新的收入来源, 这个函数仍然可以正确计算总收入, 而不需要单行代码更改:)。
程序还剩下一个主函数:

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)
}

在主程序,我们创建三个项目,其中2中时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)
}

在playground运行

程序执行结果:

Income From Project 1 = $5000  
Income From Project 2 = $10000  
Income From Project 3 = $4000  
Net income of organisation = $19000  

程序添加新的收入流

假设公司发现一个新的收入来源--广告。我们可以发现新增加一个收入来源并计算总的收入是很容易实现的,我们不需要修改任何calculateNetIncom函数。这是通过多态来实现的。

我们先定义一个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)
}

我们创建了两个广告, 即bannerAdpopupAdincomeStreams切片包括我们刚刚创建的两个广告。

这是添加广告后的完整程序:

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)
}

在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 organisation = $23750  

您可能会注意到, 我们没有对calculateNetIncome函数进行任何更改, 尽管我们添加了新的收入流。它只是因为多态性而起作用。由于新的Advertisement类型还实现了Income接口, 因此我们能够将其添加到incomeStreams切片中。calculateNetIncome函数还可以在不进行任何更改的情况下工作, 因为它能够调用Advertisement类型的calculate()和source()方法。

本节教程到此结束。祝您愉快。
ps:新手翻译,欢迎大家指正!

下一节 Golang教程第29节--defer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值