介绍

  接口类型是一种抽象类型,包含方法名,参数,返回值的没有具体实现的方法集合.接口只定义行为,而怎么去实现行为就取决于对象.例如定义一个动物接口包含说的方法,再创建小动物去实现这个方法发出不同的叫声.

定义接口

  通过type 接口名 interface{}的方式定义接口.如下

// 定义视频接口
type videoer interface {
    // 定义方法名的接受值 与 返回值
    Play() (code int, msg string)
    Closs() (code int, msg string)
}

实现接口

  只要实现了接口内的方法,就叫是实现了接口.如下play和closs方法都实现了接口内的方法.

// 定义视频结构体
type Video struct {
    v_id,v_number int
    v_name string
}

// 定义视频方法实现播放方法
func (v *Video) Play() (code int,msg string){
    v.v_number += 1
    return 0,"ok"
}
// 定义视频方法实现关闭方法
func (v *Video) Closs() (code int,msg string){
    return 0,"ok"
}

func main() {
    video := Video{}
    //_,msg :=video.Play()
    // 因为在方法中是引用结构体,所以这里使用的时候要带上&
    var inter videoer = &video
    _, msg := inter.Play()
    fmt.Print(msg)
}

  在实现接口例子中可以看到调用play方法是通过实例化结构体后通过接受结构体的变量调用的

video := Video{}
_,msg :=video.Play()

  而使用了接口后,定义变量为videoer接口类型后,将接口体传入到变量中,通过接口调用的方法的结果是一样的.

video := Video{}
var inter videoer = &video
_, msg := inter.Play()

理解接口

  接口是一个比较抽象的东西,为了方便理解,从0开始运用我的高中物理知识进行讲解.首先拿开小黄车举例,求小黄车从开始到结束总共运行的距离.

阶段:

  1. 启动:匀加速运动:0(米/秒) 加速到 n(米/秒)
  2. 匀速:匀速运动 n(米/秒)
  3. 刹车:匀减速运动 n(米/秒) 减速到 0(米/秒)
func main(){
      // 匀加速运动    
    add_x := a * t * t / 2
      // 匀速运动
    constant_x := v * t
      // 匀减速运动
    reduce_x := (v * t) + (a * t * t / 2)
      // 总位移
    x := add_x + constant_x + reduce_x
    fmt.Printf("一共运行了 %g 米\n",x)
}

  由于需要用计算三个阶段位移,都挤在mai方法中不太方便阅读与维护,所以将计算位移的逻辑定义在不同函数中.如下

func Getx(v,t float64, c_type int) float64{
    var x float64
    a := v / t * float64(c_type)
    switch {
    case c_type == 0: // 匀速
        x = v * t
    case c_type > 0: // 匀加速
        x = a * t * t / 2
    case c_type < 0: // 匀减速
        x = (v * t) + (a * t * t / 2)
    }
    return x
}

func main(){
    car_add := Getx(10,4,1)
    car_uni := Getx(10,2,0)
    car_les := Getx(10,5,-1)
    x := car_add + car_uni + car_les
    fmt.Print(x)
}

   现在main函数就只需要执行一行代码,看起来清爽很多.但是在main中必须要调用阶段的方法才行.耦合性很高.接下来降低接口的耦合,先将获取位移函数改为方法,再创建函数获取方法名,main通过调用新的方法来获取位移.内容如下:

// 定义匀变速结构体
type Uvs struct {}

// 获取位移方法 v最大速度,t运动时长,c_type:-1.匀减速;0.匀速;1.匀加速
func (Uvs) Getx(v, t float64, c_type int) float64 {
    var x float64
    a := v / t * float64(c_type)
    switch {
    case c_type == 0: // 匀速
        x = v * t
    case c_type > 0: // 匀加速
        x = a * t * t / 2
    case c_type < 0: // 匀减速
        x = (v * t) + (a * t * t / 2)
    }
    return x
}

// 返回Uvs载体
func getName() Uvs{
    return Uvs{}
}

func main(){
    // car := Uvs{}
    car := getName()
    car_add := car.Getx(10,4,1)
    car_uni := car.Getx(10,2,0)
    car_les := car.Getx(10,5,-1)
    x := car_add + car_uni + car_les
    fmt.Print(x)
}

  接口引入:假设我们在添加一个不动王八方法.在获取构造体的时候就需要改两处地方.如下

// 定义不动王八载体和方法
type Kingeight struct {}
func (Kingeight) Getx(v, t float64, c_type int) float64{
    return 0.0
}

// 省略Uvs构造体和方法

// 返回类型改为了不动王八
func getName() Kingeight{
      // 返回不动王八
    return Kingeight{}
}

  非常麻烦,既然都有Getx方法.那定义一个接口并定义Getx方法.在getName() 接口名函数中就可以将返回类型改为接口名,return 结构体名{} .如下:

type myInter interface {
    Getx(v, t float64, c_type int) float64
}

func getName() myInter{
    // 返回王八体
    // return Kingeight{}
    // 返回匀变速运动体
    return Uvs{}
}

  完整代码如下:

package main

import "fmt"

// 定义不动王八载体和方法
type Kingeight struct{}

func (Kingeight) Getx(v, t float64, c_type int) float64 {
    return 0.0
}

// 定义匀变速结构体
type Uvs struct{}

// 获取位移方法 v最大速度,t运动时长,c_type:-1.匀减速;0.匀速;1.匀加速
func (Uvs) Getx(v, t float64, c_type int) float64 {
    var x float64
    a := v / t * float64(c_type)
    switch {
    case c_type == 0: // 匀速
        x = v * t
    case c_type > 0: // 匀加速
        x = a * t * t / 2
    case c_type < 0: // 匀减速
        x = (v * t) + (a * t * t / 2)
    }
    return x
}

type myInter interface {
    Getx(v, t float64, c_type int) float64
}

func getName() myInter {
    // 返回王八体
    // return Kingeight{}
    // 返回匀变速运动体
    return Uvs{}
}

func Countx(xs []float64) float64 {
    var x float64
    for _, v := range xs {
        x += v
    }
    return x
}

func main() {
    cName := getName()
    //inter := car.Getx()
    var inter myInter = cName
    car_add := inter.Getx(10, 4, 1)
    car_uni := inter.Getx(10, 5, 0)
    car_les := inter.Getx(10, 3, -0)
    xs := []float64{car_add, car_uni, car_les}
    x := Countx(xs)
    fmt.Printf("共运行:%g 米", x)
}

组合接口

  接口可以包含一个或多个其他的接口,只要外部接口所有方法都被实现,就可以使用这个借口中所有嵌套的组合.

声明读写接口

type IWriter interface {
    // 传入写文件的名字以及内容
    write(filename, content string) string
}

type IReader interface {
    // 传入要读取文件的内容
    read(filename string) string
}

实现接口

// 定义结构体
type qvbilam struct{}

// 实现读接口
func (qvb qvbilam) read(filename string) string {
    content, rErr := ioutil.ReadFile(filename)
    if rErr != nil {
        fmt.Println(rErr)
        return "读取文件错误"
    }
    return string(content)
}

// 实现写接口
func (qvb qvbilam) write(filename, content string) string {
    // 创建文件
    file, err := os.Create(filename)
    if err != nil {
        fmt.Println(err)
        return "创建文件出错"
    }
    // 打开文件
    _, writeErr := io.WriteString(file, content)
    if writeErr != nil {
        fmt.Println(writeErr)
        return "写入文件出错"
    }
    return "写入文件成功"
}

使用接口

  通过IReadeer接口调用读的方法读取文件内容.定义变量接受读IReader接口,值为结构体qvbilam{}.

func main() {
    // 实例化结构
    qstr := qvbilam{}
    // 定义读接口的变量.结构体为qvbilam
    var qint IReader = qstr
    // 调用读接口
    res :=qint.read("basic/1.txt")
    fmt.Println(res)
}

  因为读接口只有读的放法.所以要想再调用写的话还要定义一个IWriter才可调用写的方法.非常不方便.如下所示:

func main() {
    // 实例化结构
    qstr := qvbilam{}
    // 定义读接口的变量.结构体为qvbilam
    var qint IReader = qstr
    // 调用读接口
    res :=qint.read("basic/1.txt")
    fmt.Println("读取文件内容为:",res)

    // 写文件
    var qint1 IWriter = qstr
    res1 := qint1.write("basic/2.txt","欢迎光临")
    fmt.Println("执行结果:",res1)
}

引进组合接口

  定义一个新的接口,新接口内包含接口IWriterIReader.这样通过定义组合接口就能即使用读方法又能使用写的方法

type IAdminer interface {
    // 包含两个接口
    IWriter
    IReader
}

  同时读写文件修改为如下,即可通过组合接口调用IWriterIReader接口下的实现方法.功能读取2.txt内容并写入到新文件3.txt中.

func main() {
    // 实例化结构
    qstr := qvbilam{}
    // 定义读接口的变量.结构体为qvbilam
    var qint IAdminer = qstr
    // 进行读
    res := qint.read("basic/2.txt")
    // 将读的内容写入
    result := qint.write("basic/3.txt",res)
    fmt.Println(result)
}

完整代码

package main

import (
    "fmt"
    "io"
    "io/ioutil"
    "os"
)

type IWriter interface {
    // 传入写文件的名字以及内容
    write(filename, content string) string
}

type IReader interface {
    // 传入要读取文件的内容
    read(filename string) string
}

type IAdminer interface {
    // 包含两个接口
    IWriter
    IReader
}

type qvbilam struct{}

func (qvb qvbilam) read(filename string) string {
    content, rErr := ioutil.ReadFile(filename)
    if rErr != nil {
        fmt.Println(rErr)
        return "读取文件错误"
    }
    return string(content)
}


func (qvb qvbilam) write(filename, content string) string {
    // 创建文件
    file, err := os.Create(filename)
    if err != nil {
        fmt.Println(err)
        return "创建文件出错"
    }
    // 打开文件
    _, writeErr := io.WriteString(file, content)
    if writeErr != nil {
        fmt.Println(writeErr)
        return "写入文件出错"
    }
    return "写入文件成功"
}

func main() {
    // 实例化结构
    qstr := qvbilam{}
    // 定义读接口的变量.结构体为qvbilam
    var qint IAdminer = qstr
    // 进行读
    res := qint.read("basic/2.txt")
    // 将读的内容写入
    result := qint.write("basic/3.txt",res)
    fmt.Println(result)
}
Last modification:March 26th, 2020 at 01:17 pm