go教程8
1. 合取代继承
Go 不支持继承,但它支持组合(Composition)。组合一般定义为“合并在一起”。汽车就是一个关于组合的例子:一辆汽车由车轮、引擎和其他各种部件组合在一起。
通过嵌套结构体进行组合
在 Go 中,通过在结构体内嵌套结构体,可以实现组合。
组合的典型例子就是博客帖子。每一个博客的帖子都有标题、内容和作者信息。使用组合可以很好地表示它们。通过学习本教程后面的内容,我们会知道如何实现组合。
我们首先创建一个 author
结构体。
package main
)
author
firstName
lastName
bio
}
a author
fmt
a
firstName
a
lastName
}
在上面的代码片段中,我们创建了一个 author
结构体,author
的字段有 firstname
、lastname
和 bio
。我们还添加了一个 fullName()
方法,其中 author
作为接收者类型,该方法返回了作者的全名。
下一步我们创建 post
结构体。
post
title
content
author
}
p post
fmt
p
title
fmt
p
content
fmt
p
author
fmt
p
author
bio
}
post
结构体的字段有 title
和 content
。它还有一个嵌套的匿名字段 author
。该字段指定 author
组成了 post
结构体。现在 post
可以访问 author
结构体的所有字段和方法。我们同样给 post
结构体添加了 details()
方法,用于打印标题、内容和作者的全名与简介。
一旦结构体内嵌套了一个结构体字段,Go 可以使我们访问其嵌套的字段,好像这些字段属于外部结构体一样。所以上面第 11 行的 p.author.fullName()
可以替换为 p.fullName()
。于是,details()
方法可以重写,如下所示:
p post
fmt
p
title
fmt
p
content
fmt
p
fmt
p
bio
}
现在,我们的 author
和 post
结构体都已准备就绪,我们来创建一个博客帖子来完成这个程序。
package main
)
author
firstName
lastName
bio
}
a author
fmt
a
firstName
a
lastName
}
post
title
content
author
}
p post
fmt
p
title
fmt
p
content
fmt
p
fmt
p
bio
}
author1
author
post1
post
author1
post1
}
在上面程序中,main 函数在第 31 行新建了一个 author
结构体变量。而在第 36 行,我们通过嵌套 author1
来创建一个 post
。该程序输出:
Title
Inheritance
Go
Content
Go supports composition instead of inheritance
Author
Naveen Ramanathan
Bio
Golang Enthusiast
结构体切片的嵌套
我们可以进一步处理这个示例,使用博客帖子的切片来创建一个网站。
我们首先定义 website
结构体。请在上述代码里的 main 函数中,添加下面的代码,并运行它。
website
post
}
w website
fmt
v
w
posts
v
fmt
}
在你添加上述代码后,当你运行程序时,编译器将会报错,如下所示:
main
go
syntax error
unexpected
expecting field name or embedded
这项错误指出了嵌套的结构体切片 []post
。错误的原因是结构体不能嵌套一个匿名切片。我们需要一个字段名。所以我们来修复这个错误,让编译器顺利通过。
website
posts
post
}
可以看到,我给帖子的切片 []post
添加了字段名 posts
。
现在我们来修改主函数,为我们的新网站创建一些帖子吧。
修改后的完整代码如下所示:
package main
)
author
firstName
lastName
bio
}
a author
fmt
a
firstName
a
lastName
}
post
title
content
author
}
p post
fmt
p
title
fmt
p
content
fmt
p
fmt
p
bio
}
website
posts
post
}
w website
fmt
v
w
posts
v
fmt
}
author1
author
post1
post
author1
post2
post
author1
post3
post
author1
w
website
posts
post
post1
post2
post3
w
}
在上面的主函数中,我们创建了一个作者 author1
,以及三个帖子 post1
、post2
和 post3
。我们最后通过嵌套三个帖子,在第 62 行创建了网站 w
,并在下一行显示内容。
程序会输出:
Contents of Website
Title
Inheritance
Go
Content
Go supports composition instead of inheritance
Author
Naveen Ramanathan
Bio
Golang Enthusiast
Title
Struct instead of Classes
Go
Content
Go does not support classes but methods can be added to structs
Author
Naveen Ramanathan
Bio
Golang Enthusiast
Title
Concurrency
Content
Go is a concurrent language and not a parallel one
Author
Naveen Ramanathan
Bio
Golang Enthusiast
本教程到此结束。祝你愉快。
2. 多态
Go 通过接口来实现多态。我们已经讨论过,在 Go 语言中,我们是隐式地实现接口。一个类型如果定义了接口所声明的全部方法,那它就实现了该接口。现在我们来看看,利用接口,Go 是如何实现多态的。
使用接口实现多态
一个类型如果定义了接口的所有方法,那它就隐式地实现了该接口。
所有实现了接口的类型,都可以把它的值保存在一个接口类型的变量中。在 Go 中,我们使用接口的这种特性来实现多态。
通过一个程序我们来理解 Go 语言的多态,它会计算一个组织机构的净收益。为了简单起见,我们假设这个虚构的组织所获得的收入来源于两个项目:fixed billing
和 time and material
。该组织的净收益等于这两个项目的收入总和。同样为了简单起见,我们假设货币单位是美元,而无需处理美分。因此货币只需简单地用 int
来表示。(我建议阅读 https://forum.golangbridge.org/t/what-is-the-proper-golang-equivalent-to-decimal-when-dealing-with-money/413 上的文章,学习如何表示美分。感谢 Andreas Matuschek 在评论区指出这一点。)
我们首先定义一个接口 Income
。
Income
}
上面定义了接口 Interface
,它包含了两个方法:calculate()
计算并返回项目的收入,而 source()
返回项目名称。
下面我们定义一个表示 FixedBilling
项目的结构体类型。
FixedBilling
projectName
biddedAmount
}
项目 FixedBillin
有两个字段:projectName
表示项目名称,而 biddedAmount
表示组织向该项目投标的金额。
TimeAndMaterial
结构体用于表示项目 Time and Material。
TimeAndMaterial
projectName
noOfHours
hourlyRate
}
结构体 TimeAndMaterial
拥有三个字段名:projectName
、noOfHours
和 hourlyRate
。
下一步我们给这些结构体类型定义方法,计算并返回实际收入和项目名称。
fb FixedBilling
fb
biddedAmount
}
fb FixedBilling
fb
projectName
}
tm TimeAndMaterial
tm
noOfHours
tm
hourlyRate
}
tm TimeAndMaterial
tm
projectName
}
在项目 FixedBilling
里面,收入就是项目的投标金额。因此我们返回 FixedBilling
类型的 calculate()
方法。
而在项目 TimeAndMaterial
里面,收入等于 noOfHours
和 hourlyRate
的乘积,作为 TimeAndMaterial
类型的 calculate()
方法的返回值。
我们还通过 source()
方法返回了表示收入来源的项目名称。
由于 FixedBilling
和 TimeAndMaterial
两个结构体都定义了 Income
接口的两个方法:calculate()
和 source()
,因此这两个结构体都实现了 Income
接口。
我们来声明一个 calculateNetIncome
函数,用来计算并打印总收入。
ic
Income
netincome
income
ic
fmt
income
income
netincome
income
fmt
netincome
}
上面的函数接收一个 Income
接口类型的切片作为参数。该函数会遍历这个接口切片,并依个调用 calculate()
方法,计算出总收入。该函数同样也会通过调用 source()
显示收入来源。根据 Income
接口的具体类型,程序会调用不同的 calculate()
和 source()
方法。于是,我们在 calculateNetIncome
函数中就实现了多态。
如果在该组织以后增加了新的收入来源,calculateNetIncome
无需修改一行代码,就可以正确地计算总收入了。
最后就剩下这个程序的 main
函数了。
project1
FixedBilling
projectName
biddedAmount
project2
FixedBilling
projectName
biddedAmount
project3
TimeAndMaterial
projectName
noOfHours
hourlyRate
incomeStreams
Income
project1
project2
project3
incomeStreams
}
在上面的 main
函数中,我们创建了三个项目,有两个是 FixedBilling
类型,一个是 TimeAndMaterial
类型。接着我们创建了一个 Income
类型的切片,存放了这三个项目。由于这三个项目都实现了 Interface
接口,因此可以把这三个项目放入 Income
切片。最后我们将该切片作为参数,调用了 calculateNetIncome
函数,显示了项目不同的收益和收入来源。
以下完整的代码供你参考。
package main
)
Income
}
FixedBilling
projectName
biddedAmount
}
TimeAndMaterial
projectName
noOfHours
hourlyRate
}
fb FixedBilling
fb
biddedAmount
}
fb FixedBilling
fb
projectName
}
tm TimeAndMaterial
tm
noOfHours
tm
hourlyRate
}
tm TimeAndMaterial
tm
projectName
}
ic
Income
netincome
income
ic
fmt
income
income
netincome
income
fmt
netincome
}
project1
FixedBilling
projectName
biddedAmount
project2
FixedBilling
projectName
biddedAmount
project3
TimeAndMaterial
projectName
noOfHours
hourlyRate
incomeStreams
Income
project1
project2
project3
incomeStreams
}
该程序会输出:
Income From Project 1 = $5000
Income From Project 2 = $10000
Income From Project 3 = $4000
Net income of organisation = $19000
新增收益流
假设前面的组织通过广告业务,建立了一个新的收益流(Income Stream)。我们可以看到添加它非常简单,并且计算总收益也很容易,我们无需对 calculateNetIncome
函数进行任何修改。这就是多态的好处。
我们首先定义 Advertisement
类型,并在 Advertisement
类型中定义 calculate()
和 source()
方法。
Advertisement
adName
CPC
noOfClicks
}
a Advertisement
a
CPC
a
noOfClicks
}
a Advertisement
a
adName
}
Advertisement
类型有三个字段,分别是 adName
、CPC
(每次点击成本)和 noOfClicks
(点击次数)。广告的总收益等于 CPC
和 noOfClicks
的乘积。
现在我们稍微修改一下 main
函数,把新的收益流添加进来。
project1
FixedBilling
projectName
biddedAmount
project2
FixedBilling
projectName
biddedAmount
project3
TimeAndMaterial
projectName
noOfHours
hourlyRate
bannerAd
Advertisement
adName
CPC
noOfClicks
popupAd
Advertisement
adName
CPC
noOfClicks
incomeStreams
Income
project1
project2
project3
bannerAd
popupAd
incomeStreams
}
我们创建了两个广告项目,即 bannerAd
和 popupAd
。incomeStream
切片包含了这两个创建的广告项目。
package main
)
Income
}
FixedBilling
projectName
biddedAmount
}
TimeAndMaterial
projectName
noOfHours
hourlyRate
}
Advertisement
adName
CPC
noOfClicks
}
fb FixedBilling
fb
biddedAmount
}
fb FixedBilling
fb
projectName
}
tm TimeAndMaterial
tm
noOfHours
tm
hourlyRate
}
tm TimeAndMaterial
tm
projectName
}
a Advertisement
a
CPC
a
noOfClicks
}
a Advertisement
a
adName
}
ic
Income
netincome
income
ic
fmt
income
income
netincome
income
fmt
netincome
}
project1
FixedBilling
projectName
biddedAmount
project2
FixedBilling
projectName
biddedAmount
project3
TimeAndMaterial
projectName
noOfHours
hourlyRate
bannerAd
Advertisement
adName
CPC
noOfClicks
popupAd
Advertisement
adName
CPC
noOfClicks
incomeStreams
Income
project1
project2
project3
bannerAd
popupAd
incomeStreams
}
上面程序会输出:
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
切片添加 Advertisement
。calculateNetIncome
无需修改,因为它能够调用 Advertisement
类型的 calculate()
和 source()
方法。
本教程到此结束。祝你愉快。
本文来自博客园,作者:易先讯,转载请注明原文链接:https://www.cnblogs.com/gongxianjin/p/17981810
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具