> 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/duo-tai.md).

# 多态

Golang中的多态是基于接口实现的。我们前面接口部分已经介绍过，Golang中的接口是隐式实现的。如果某个类型实现了某个接口中的所有方法，我们就说该类型实现了这个接口。本节我们就来看下Golang是如果基于接口实现多态的。

#### 基于接口实现多态

再重申下类型于接口的关系：如果某个类型实现了某个接口中的所有方法，我们就说该类型实现了这个接口。

**而一个接口类型的变量可以赋值为任何一个实现了该接口类型变量，Golang中的这个特性用来实现多态，建议暂停几秒钟仔细理解下这句话。**

下面我们通过例子来理解下Golang中是如何实现多态的。

其实前面接口一节"接口的实际应用"部分我们已经体验过多态的优势了，只是当时我没有指出来，现在我们再回顾下那个例子(注意，原文是用的一个新的计算企业收入的例子，我个人感觉上一节的例子更加简单易懂，因此还用这个例子)。

```go
package main

import "fmt"

type SalaryCalculator interface {
    CalculateSalary() int
}

type Permanent struct {
    empId    int
    basicpay int
    pf       int
}

type Contract struct {
    empId    int
    basicpay int
}

func (p Permanent) CalculateSalary() int {
    return p.basicpay + p.pf
}

func (c Contract) CalculateSalary() int {
    return c.basicpay
}

func totalExpense(s []SalaryCalculator) {
    expense := 0
    for _, v := range s {
        expense = expense + v.CalculateSalary()
    }
    fmt.Printf("Total Expense Per Month $%d", expense)
}

func main() {
    pemp1 := Permanent{1, 5000, 20}
    pemp2 := Permanent{2, 6000, 30}
    cemp1 := Contract{3, 3000}
    employees := []SalaryCalculator{pemp1, pemp2, cemp1}
    totalExpense(employees)
}
```

这里根据公司里每个员工的薪资来计算整个公司的总开销。场景其实很简单，比如公司共有两个员工，那么这两个员工的薪资总和即为公司的总开销。

我们声明了一个接口`SalaryCalculator`，该接口含一个方法签名`CalculateSalary() int`。接着声明了两种员工类型，`Permanent`和`Contract`，分别表示正式工和合同工。正式工的薪资包含`basicpay`和`pf`两部分，合同工薪资仅包含`basicpay`。`Permanent`和`Contract`均实现了`SalaryCalculator`接口。

函数`totalExpense`接收的入参是`SalaryCalculator`接口类型的切片，我们可以将实现了该接口的变量存入该切片，比如`Permanent`类型和`Contract`类型。然是在`totalExpense`函数看来，它只关心入参是`SalaryCalculator`类型的切片，因此只要该切片中存储的数据类型实现了`SalaryCalculator`接口即可。

这种程序设计的最大好处是可扩展性，假设该公司加入了一种新的员工类型`Freelancer`，即自由职业者，那么只要该类型也实现了接口`SalaryCalculator`即可，这时我们就可以将该类型的变量添加到切片`employees`中，而函数`totalExpense`完全不需要升级。下面我们就升级下前面的程序，新增员工类型`Freelancer`。

#### 新增一种员工类型

现在升级下前面的程序，新增员工类型`Freelancer`，由于是自由职业者，我们就不分配`empId`属性了。

```go
package main

import "fmt"

type SalaryCalculator interface {
    CalculateSalary() int
}

type Permanent struct {
    empId    int
    basicpay int
    pf       int
}

type Contract struct {
    empId    int
    basicpay int
}

type Freelancer struct {
    basicpay int
}

func (p Permanent) CalculateSalary() int {
    return p.basicpay + p.pf
}

func (c Contract) CalculateSalary() int {
    return c.basicpay
}

func (f Freelancer) CalculateSalary() int {
    return f.basicpay
}

func totalExpense(s []SalaryCalculator) {
    expense := 0
    for _, v := range s {
        expense = expense + v.CalculateSalary()
    }
    fmt.Printf("Total Expense Per Month $%d", expense)
}

func main() {
    pemp1 := Permanent{1, 5000, 20}
    pemp2 := Permanent{2, 6000, 30}
    cemp1 := Contract{3, 3000}
    femp1 := Freelancer{1000}
    employees := []SalaryCalculator{pemp1, pemp2, cemp1, femp1}
    totalExpense(employees)
}
```

**这里`Freelancer`类型也实现了接口`SalaryCalculator`即可，然后我们将该类型的变量添加到切片`employees`中，而函数`totalExpense`完全不需要升级，这就是多态的优势。**

该程序执行结果如下：

```
Total Expense Per Month $15050
```
