> For the complete documentation index, see [llms.txt](https://sunwenfei.gitbook.io/sunwenfei/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sunwenfei.gitbook.io/sunwenfei/golang/golang-ji-chu-jiao-cheng/mian-xiang-dui-xiang-bian-cheng/shi-zu-he-er-bu-shi-ji-cheng.md).

# 组合 vs 继承

Golang不支持**继承**，但支持**组合**。怎么理解组合呢？可以拿车来举例，车就是一个轮子、发动机等组件的一个组合。这个模式跟现在流行的前端开发模式有点像，比如React组件化，也是推崇组合，摒弃继承。

#### 实现组合：结构体嵌套

Golang中可以通过结构体嵌套来实现组合。我们拿博客来举个例子，一篇博客包含标题、内容以及作者等信息，这三部分组合在一起形成一篇博客。 我们首先创建一个结构体类型`author`用来表示作者。

```go
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)
}
```

这里我们首先创建了一个结构体类型`author`，该结构体类型包含三个属性`firstName`、`lastName`以及`bio`。然后我们给该结构体类型绑定了一个方法`fullName`用来获取姓名全称。

下面我们来创建博客结构体类型`post`，并绑定一个方法`details`。

```go
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`包含`title`、`content`两个属性，还包含一个匿名嵌套属性`author`。这里包含了一个嵌套结构体`author`，因此我们说`author`是`post`的一个组成部分，或者说`post`由`author`组成。这时我们可以通过`post`来访问`author`的各个属性和方法。 在`details`方法中我们通过结构体类型`post`来访问了`title`、`content`属性字段以及`author`属性的`fullName`方法和`bio`属性。

对于匿名嵌套属性，我们可以简写如下，将匿名字段略去：

```go
func (p post) details() {
    fmt.Println("Title: ", p.title)
    fmt.Println("Content: ", p.content)
    fmt.Println("Author: ", p.fullName())
    fmt.Println("Bio: ", p.bio)
}
```

到这里我们已经定义好了两个结构体类型及其绑定的方法：`author`和`post`，下面写个测试程序来测试下：

```go
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()
}
```

&#x20;该程序输出如下：

```
Title:  Inheritance in Go
Content:  Go supports composition instead of inheritance
Author:  Naveen Ramanathan
Bio:  Golang Enthusiast
```

#### 嵌套结构体切片

前面我们看到，一个`post`可以由`title`、`content`和`author`组成，这三个属性都是单个的。但是一个网站(website)肯定是包含多条博客(`post`)的，因此我们可以在`website`结构体类型下面掐套一个`post`结构体切片：

```go
type website struct {
    posts []post
}

func (w website) contents() {
    fmt.Println("Contents of website")
    for _, v := range w.posts {
        v.details()
        fmt.Println()
    }
}
```

这里我们在结构体`website`内部嵌套的是切片类型`[]post`。我们再升级下`main`函数来创建多个`post`结构体变量，并创建一个`website`结构体变量：

```go
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")
    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()
}
```

这里我们创建了三个结构体`post`类型的变量`post1`，`post2`，`post3`，然后构建了一个切片内嵌到了结构体变量`w`内。

该程序输出如下：

```
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
```
