组合和嵌入

组合是继承的一种代替方案。结构体在声明时,可以包含任何其他类型的名字:
type Request struct {
Resource string
}
type AuthenticatedRequest struct {
Request
Username, Password string
}
在上述例子中,AuthenticatedRequest 包含4个公有成员:ResourceRequestUsernamePassword
组合的结构体的初始化方式跟正常结构体一样:
Go
Output
package main
import (
"fmt"
)
type Request struct {
Resource string
}
type AuthenticatedRequest struct {
Request
Username, Password string
}
func main() {
ar := new(AuthenticatedRequest)
ar.Resource = "example.com/request"
ar.Username = "bob"
ar.Password = "P@ssw0rd"
fmt.Printf("%#v", ar)
}
&main.AuthenticatedRequest{Request:main.Request{Resource:"example.com/request"}, Username:"bob", Password:"P@ssw0rd"}
👉
点击此处
👈
亲自在编程操场试运行上述代码。

嵌入

在上述例子中,Request就是一个嵌入字段。还可以通过嵌入不同的类型来实现组合。这很有用,我们可以通过装饰的方式让一个结构体具备更多功能。继续看上述Resource的例子,我们想要一个新功能来格式化Resource字段,为它添加http://https://前缀。我们有两种选择:一是给AuthenticatedRequest添加新方法,二是在它里面嵌入另一个结构体
type ResourceFormatter struct {}
func(r *ResourceFormatter) FormatHTTP(resource string) string {
return fmt.Sprintf("http://%s", resource)
}
func(r *ResourceFormatter) FormatHTTPS(resource string) string {
return fmt.Sprintf("https://%s", resource)
}
type AuthenticatedRequest struct {
Request
Username, Password string
ResourceFormatter
}
然后,我们的main()函数就变成下面这样:
func main() {
ar := new(AuthenticatedRequest)
ar.Resource = "www.example.com/request"
ar.Username = "bob"
ar.Password = "P@ssw0rd"
fmt.Println(ar.FormatHTTP(ar.Resource))
fmt.Println(ar.FormatHTTPS(ar.Resource))
fmt.Printf("%#v", ar)
}
看一下,AuthenticatedRequest就具有了ResourceFormatter这个嵌入式的结构体。
缺点是被嵌入的对象无法访问组合它的对象。因此,ResourceFormatter无法访问AuthenticatedRequest中的成员。