多态

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。接着声明了两种员工类型,PermanentContract,分别表示正式工和合同工。正式工的薪资包含basicpaypf两部分,合同工薪资仅包含basicpayPermanentContract均实现了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

Last updated