[译] part 28: golang 的面向对象 -- 多态

栏目: 后端 · 前端 · 发布时间: 7年前

内容简介:Go 中的多态性是在接口的帮助下实现的。正如我们已经讨论过的,接口可以在 Go 中隐式实现。如果类型为接口中声明的所有方法提供定义,则类型实现接口。让我们看看在接口的帮助下如何在 Go 中实现多态性。任何定义了接口的所有方法的类型都被称为隐式实现了该接口。一个接口类型的变量可以承载任何实现该接口的值。接口的这个属性用于在 Go 中实现多态。

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 结构有三个字段 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() 方法返回它。

对于 TimeAndMaterial 项目,收入是 noOfHourshourlyRate 的乘积。返回值来自于使用接收者类型为 TimeAndMaterialcalculate() 方法。

我们用 source() 方法来返回收入来源项目的名字。

由于 FixedBillingTimeAndMaterial 结构都为 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)
}
复制代码

Run in playground

程序输出,

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 类型有三个字段: adNameCPC (每次点击费用)和 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 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)
}
复制代码

Run 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 organisation = $23750
复制代码

您会注意到虽然我们添加了新的收入来源,但我们没有对 calculateNetIncome 函数进行任何更改。这就是多态性的作用。由于新的 Advertisement 类型也实现了 Income 接口,我们可以将它添加到 incomeStreams 切片中。 calculateNetIncome 函数也没有任何变化,因为它能够调用 Advertisement 类型的 calculate()source() 方法。


以上所述就是小编给大家介绍的《[译] part 28: golang 的面向对象 -- 多态》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Structures and Algorithm Analysis in Java

Data Structures and Algorithm Analysis in Java

Mark A. Weiss / Pearson / 2006-3-3 / USD 143.00

As the speed and power of computers increases, so does the need for effective programming and algorithm analysis. By approaching these skills in tandem, Mark Allen Weiss teaches readers to develop wel......一起来看看 《Data Structures and Algorithm Analysis in Java》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

URL 编码/解码
URL 编码/解码

URL 编码/解码