Go 經(jīng)典入門系列 27:組合取代繼承??
歡迎來到 Golang 系列教程[1]的第 27 篇。
Go 不支持繼承,但它支持組合(Composition)。組合一般定義為“合并在一起”。汽車就是一個(gè)關(guān)于組合的例子:一輛汽車由車輪、引擎和其他各種部件組合在一起。
通過嵌套結(jié)構(gòu)體進(jìn)行組合
在 Go 中,通過在結(jié)構(gòu)體內(nèi)嵌套結(jié)構(gòu)體,可以實(shí)現(xiàn)組合。
組合的典型例子就是博客帖子。每一個(gè)博客的帖子都有標(biāo)題、內(nèi)容和作者信息。使用組合可以很好地表示它們。通過學(xué)習(xí)本教程后面的內(nèi)容,我們會(huì)知道如何實(shí)現(xiàn)組合。
我們首先創(chuàng)建一個(gè) author 結(jié)構(gòu)體。
package?main
import?(
????"fmt"
)
type?author?struct?{
????firstName?string
????lastName??string
????bio???????string
}
func?(a?author)?fullName()?string?{
????return?fmt.Sprintf("%s?%s",?a.firstName,?a.lastName)
}
在上面的代碼片段中,我們創(chuàng)建了一個(gè) author 結(jié)構(gòu)體,author 的字段有 firstname、lastname 和 bio。我們還添加了一個(gè) fullName() 方法,其中 author 作為接收者類型,該方法返回了作者的全名。
下一步我們創(chuàng)建 post 結(jié)構(gòu)體。
type?post?struct?{
????title?????string
????content???string
????author
}
func?(p?post)?details()?{
????fmt.Println("Title:?",?p.title)
????fmt.Println("Content:?",?p.content)
????fmt.Println("Author:?",?p.author.fullName())
????fmt.Println("Bio:?",?p.author.bio)
}
post 結(jié)構(gòu)體的字段有 title 和 content。它還有一個(gè)嵌套的匿名字段 author。該字段指定 author 組成了 post 結(jié)構(gòu)體?,F(xiàn)在 post 可以訪問 author 結(jié)構(gòu)體的所有字段和方法。我們同樣給 post 結(jié)構(gòu)體添加了 details() 方法,用于打印標(biāo)題、內(nèi)容和作者的全名與簡介。
一旦結(jié)構(gòu)體內(nèi)嵌套了一個(gè)結(jié)構(gòu)體字段,Go 可以使我們訪問其嵌套的字段,好像這些字段屬于外部結(jié)構(gòu)體一樣。所以上面第 11 行的 p.author.fullName() 可以替換為 p.fullName()。于是,details() 方法可以重寫,如下所示:
func?(p?post)?details()?{
????fmt.Println("Title:?",?p.title)
????fmt.Println("Content:?",?p.content)
????fmt.Println("Author:?",?p.fullName())
????fmt.Println("Bio:?",?p.bio)
}
現(xiàn)在,我們的 author 和 post 結(jié)構(gòu)體都已準(zhǔn)備就緒,我們來創(chuàng)建一個(gè)博客帖子來完成這個(gè)程序。
package?main
import?(
????"fmt"
)
type?author?struct?{
????firstName?string
????lastName??string
????bio???????string
}
func?(a?author)?fullName()?string?{
????return?fmt.Sprintf("%s?%s",?a.firstName,?a.lastName)
}
type?post?struct?{
????title???string
????content?string
????author
}
func?(p?post)?details()?{
????fmt.Println("Title:?",?p.title)
????fmt.Println("Content:?",?p.content)
????fmt.Println("Author:?",?p.fullName())
????fmt.Println("Bio:?",?p.bio)
}
func?main()?{
????author1?:=?author{
????????"Naveen",
????????"Ramanathan",
????????"Golang?Enthusiast",
????}
????post1?:=?post{
????????"Inheritance?in?Go",
????????"Go?supports?composition?instead?of?inheritance",
????????author1,
????}
????post1.details()
}
在 playground 上運(yùn)行[2]
在上面程序中,main 函數(shù)在第 31 行新建了一個(gè) author 結(jié)構(gòu)體變量。而在第 36 行,我們通過嵌套 author1 來創(chuàng)建一個(gè) post。該程序輸出:
Title:??Inheritance?in?Go
Content:??Go?supports?composition?instead?of?inheritance
Author:??Naveen?Ramanathan
Bio:??Golang?Enthusiast
結(jié)構(gòu)體切片的嵌套
我們可以進(jìn)一步處理這個(gè)示例,使用博客帖子的切片來創(chuàng)建一個(gè)網(wǎng)站。:)
我們首先定義 website 結(jié)構(gòu)體。請?jiān)谏鲜龃a里的 main 函數(shù)中,添加下面的代碼,并運(yùn)行它。
type?website?struct?{
????????[]post
}
func?(w?website)?contents()?{
????fmt.Println("Contents?of?Website\n")
????for?_,?v?:=?range?w.posts?{
????????v.details()
????????fmt.Println()
????}
}
在你添加上述代碼后,當(dāng)你運(yùn)行程序時(shí),編譯器將會(huì)報(bào)錯(cuò),如下所示:
main.go:31:9:?syntax?error:?unexpected?[,?expecting?field?name?or?embedded?type
這項(xiàng)錯(cuò)誤指出了嵌套的結(jié)構(gòu)體切片 []post。錯(cuò)誤的原因是結(jié)構(gòu)體不能嵌套一個(gè)匿名切片。我們需要一個(gè)字段名。所以我們來修復(fù)這個(gè)錯(cuò)誤,讓編譯器順利通過。
type?website?struct?{
????????posts?[]post
}
可以看到,我給帖子的切片 []post 添加了字段名 posts。
現(xiàn)在我們來修改主函數(shù),為我們的新網(wǎng)站創(chuàng)建一些帖子吧。
修改后的完整代碼如下所示:
package?main
import?(
????"fmt"
)
type?author?struct?{
????firstName?string
????lastName??string
????bio???????string
}
func?(a?author)?fullName()?string?{
????return?fmt.Sprintf("%s?%s",?a.firstName,?a.lastName)
}
type?post?struct?{
????title???string
????content?string
????author
}
func?(p?post)?details()?{
????fmt.Println("Title:?",?p.title)
????fmt.Println("Content:?",?p.content)
????fmt.Println("Author:?",?p.fullName())
????fmt.Println("Bio:?",?p.bio)
}
type?website?struct?{
?posts?[]post
}
func?(w?website)?contents()?{
????fmt.Println("Contents?of?Website\n")
????for?_,?v?:=?range?w.posts?{
????????v.details()
????????fmt.Println()
????}
}
func?main()?{
????author1?:=?author{
????????"Naveen",
????????"Ramanathan",
????????"Golang?Enthusiast",
????}
????post1?:=?post{
????????"Inheritance?in?Go",
????????"Go?supports?composition?instead?of?inheritance",
????????author1,
????}
????post2?:=?post{
????????"Struct?instead?of?Classes?in?Go",
????????"Go?does?not?support?classes?but?methods?can?be?added?to?structs",
????????author1,
????}
????post3?:=?post{
????????"Concurrency",
????????"Go?is?a?concurrent?language?and?not?a?parallel?one",
????????author1,
????}
????w?:=?website{
????????posts:?[]post{post1,?post2,?post3},
????}
????w.contents()
}
在 playground 中運(yùn)行[3]
在上面的主函數(shù)中,我們創(chuàng)建了一個(gè)作者 author1,以及三個(gè)帖子 post1、post2 和 post3。我們最后通過嵌套三個(gè)帖子,在第 62 行創(chuàng)建了網(wǎng)站 w,并在下一行顯示內(nèi)容。
程序會(huì)輸出:
Contents?of?Website
Title:??Inheritance?in?Go
Content:??Go?supports?composition?instead?of?inheritance
Author:??Naveen?Ramanathan
Bio:??Golang?Enthusiast
Title:??Struct?instead?of?Classes?in?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
本教程到此結(jié)束。祝你愉快。
下一教程 - 多態(tài)[4]
via: https://golangbot.com/inheritance
作者:Nick Coghlan[5]譯者:Noluye[6]校對:polaris1119[7]
本文由 GCTT[8] 原創(chuàng)編譯,Go 中文網(wǎng)[9] 榮譽(yù)推出
參考資料
Golang 系列教程: https://studygolang.com/subject/2
[2]在 playground 上運(yùn)行: https://play.golang.org/p/sskWaTpJgr
[3]在 playground 中運(yùn)行: https://play.golang.org/p/gKaa0RbeAE
[4]多態(tài): https://studygolang.com/articles/12681
[5]Nick Coghlan: https://golangbot.com/about/
[6]Noluye: https://github.com/Noluye
[7]polaris1119: https://github.com/polaris1119
[8]GCTT: https://github.com/studygolang/GCTT
[9]Go 中文網(wǎng): https://studygolang.com/
推薦閱讀
