Go 面向对象 - Go中的多态
Go 面向对象中的多态性是在接口(Interface)的帮助下实现的。 正如我们已经讨论过的,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
项目有两个字段: projectName
表示项目名称;biddedAmount
表示组织为项目投标的金额。
TimeAndMaterial
结构体将代表时间材料类型的项目。
type TimeAndMaterial struct {
projectName string
noOfHours int
hourlyRate 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
的乘积。 该值是从具有接收器类型 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 organization = $%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 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)
}
运行上面程序,结果如下
为上述程序添加新的收入来源
假设该公司通过广告找到了新的收入来源。 让我们看看添加这个新的收入流并计算总收入而不对 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(点击次数)。 广告的总收入是 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。 Income 切片包括我们刚刚创建的两个广告。
下面是添加广告后的完整程序。
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)
}
运行上面的程序,结果如下
可能已经注意到,尽管我们添加了新的收入流,但我们并未对 calculateNetIncome 函数进行任何更改。 它因为多态而起作用。 由于新的 Advertisement 类型也实现了 Income 接口,我们可以将其添加到incomeStreams 切片中。 calculateNetIncome 函数也无需任何更改即可工作,因为它能够调用 Advertisement 类型的 calculate() 和 source() 方法。
相关文章
使用 C 语言中的 goto 语句
发布时间:2023/05/07 浏览次数:79 分类:C语言
-
本文介绍了如何在 C 语言中使用 goto 语句。使用 goto 语句在 C 语言中实现循环 goto 关键字是 C 语言的一部分,它提供了一个做无条件跳转的结构。
Django 中的 Slug
发布时间:2023/05/04 浏览次数:173 分类:Python
-
本篇文章旨在定义一个 slug 以及我们如何使用 slug 字段在 Python 中使用 Django 获得独特的帖子。
在 Django 中按降序过滤查询集中的项目
发布时间:2023/05/04 浏览次数:157 分类:Python
-
在这个讲解中,学习如何借助 Django 中的 order_by() 方法按降序过滤出查询集中的项目。
Django ALLOWED_HOSTS 介绍
发布时间:2023/05/04 浏览次数:181 分类:Python
-
本文展示了如何创建您的 Django 网站,为公开发布做好准备,如何设置 ALLOWED_HOSTS 以及如何在使用 Django 进行 Web 部署期间修复预期的主要问题。
Django 中的 Select_related 方法
发布时间:2023/05/04 浏览次数:129 分类:Python
-
本文介绍了什么是查询集,如何处理这些查询以及我们如何利用 select_related() 方法来过滤 Django 中相关模型的查询。
使用 Post 请求将数据发送到 Django 服务器
发布时间:2023/05/04 浏览次数:159 分类:Python
-
在这篇关于Django的讲解中,我们简要介绍了post和get请求以及如何在Django中用post实现CSRF token。
Django 返回 JSON
发布时间:2023/05/04 浏览次数:106 分类:Python
-
在与我们的讨论中,我们简要介绍了 JSON 格式,并讨论了如何借助 Django 中的 JsonResponse 类将数据返回为 JSON 格式。
在 Django 中创建对象
发布时间:2023/05/04 浏览次数:59 分类:Python
-
本文的目的是解释什么是模型以及如何使用 create() 方法创建对象,并了解如何在 Django 中使用 save() 方法。