# 组合和嵌入

组合是继承的一种代替方案。结构体在声明时，可以包含任何**其他**类型的名字：

```go
type Request struct {
	Resource string
}

type AuthenticatedRequest struct {
	Request
	Username, Password string
}
```

在上述例子中，`AuthenticatedRequest` 包含4个公有成员：`Resource`，`Request`，`Username`，`Password`。

组合的结构体的初始化方式跟正常结构体一样：

{% tabs %}
{% tab title="Go" %}
```go
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)
}
```
{% endtab %}

{% tab title="Output" %}
```
&main.AuthenticatedRequest{Request:main.Request{Resource:"example.com/request"}, Username:"bob", Password:"P@ssw0rd"}
```
{% endtab %}
{% endtabs %}

:point\_right: [点击此处](https://play.golang.org/p/qjFMmw5lhvs) :point\_left: 亲自在编程操场试运行上述代码。

## 嵌入

在上述例子中，`Request`就是一个嵌入字段。还可以通过嵌入不同的类型来实现组合。这很有用，我们可以通过装饰的方式让一个结构体具备更多功能。继续看上述`Resource`的例子，我们想要一个新功能来格式化`Resource`字段，为它添加`http://`或`https://`前缀。我们有两种选择：一是给`AuthenticatedRequest`添加新方法，二是在它里面嵌入另一个结构体

```go
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()`函数就变成下面这样：

```go
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`中的成员。
