sunwenfei
  • 关于我
  • Golang
    • Goglang基础教程【译】
      • 介绍
        • 安装
        • Hello World
      • 变量、基本类型以及常量
        • 变量
        • 基本类型
        • 常量
      • 函数和包
        • 函数
        • 包
      • 条件、循环流程控制语句
        • if else条件语句
        • switch语句
        • 循环语句
      • 数组、切片、变参函数
        • 数组(Array)
        • 切片(Slice)
        • 变参函数
      • 其他数据类型
        • 映射(Map)
        • 字符串
      • 指针、结构体和方法
        • 指针
        • 结构体
        • 方法
      • 面向对象编程
        • 结构体 vs 类
        • 组合 vs 继承
        • 接口
        • 多态
      • 并发
        • 并发介绍
        • 协程(goroutine)
        • 管道(channel)
        • 带缓存的管道(buffered channel)
        • 协程池
        • 管道选择器(select)
        • 互斥锁(Mutex)
      • Defer
      • 一等公民函数
      • 反射
      • 错误
        • 错误处理
        • 自定义错误类型
        • panic和recover
      • 文件读写
        • 读文件
        • 写文件
    • Golang面向对象编程
    • Golang函数式编程
    • Golang并发编程
    • Golang web服务编程
    • Golang数据结构与算法
  • Shell编程
    • Find命令
  • JavaScript
    • browser
    • Node.JS
    • Deno
  • TypeScript
  • HTTP
    • 【译】通过信鸽理解HTTPS交互原理
  • React
    • React16
      • Hooks
        • 使用React Hooks拉取数据
  • 移动端开发
    • 原生
    • Flutter
    • ReactNative
    • 小程序
  • 前端测试
Powered by GitBook
On this page

Was this helpful?

  1. Golang
  2. Goglang基础教程【译】
  3. 面向对象编程

多态

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
Previous接口Next并发

Last updated 5 years ago

Was this helpful?