Golang中的多态是基于接口实现的。我们前面接口部分已经介绍过,Golang中的接口是隐式实现的。如果某个类型实现了某个接口中的所有方法,我们就说该类型实现了这个接口。本节我们就来看下Golang是如果基于接口实现多态的。
基于接口实现多态
再重申下类型于接口的关系:如果某个类型实现了某个接口中的所有方法,我们就说该类型实现了这个接口。
而一个接口类型的变量可以赋值为任何一个实现了该接口类型变量,Golang中的这个特性用来实现多态,建议暂停几秒钟仔细理解下这句话。
下面我们通过例子来理解下Golang中是如何实现多态的。
其实前面接口一节"接口的实际应用"部分我们已经体验过多态的优势了,只是当时我没有指出来,现在我们再回顾下那个例子(注意,原文是用的一个新的计算企业收入的例子,我个人感觉上一节的例子更加简单易懂,因此还用这个例子)。
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
属性了。
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