接口像是一个公司里面的领导,他会定义一些通用规范,只设计规范,而不实现规范。

go语言的接口,是一种新的类型定义,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。

语法格式和方法非常类似。

接口的语法格式

/* 定义接口 */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   method_name3 [return_type]
   ...
   method_namen [return_type]
}

/* 定义结构体 */
type struct_name struct {
   /* variables */
}

/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
   /* 方法实现*/
}

在接口定义中定义,若干个空方法。这些方法都具有通用性。

接口实例

下面我定义一个USB接口,有读read和写write两个方法,再定义一个电脑Computer和一个手机Mobile来实现这个接口。

USB接口

type USB interface {
    read()
    write()
}

Computer结构体

type Computer struct {
}

Mobile结构体

type Mobile struct {
}

Computer实现USB接口方法

func (c Computer) read() {
    fmt.Println("computer read...")
}

func (c Computer) write() {
    fmt.Println("computer write...")
}

Mobile实现USB接口方法

func (c Mobile) read() {
    fmt.Println("mobile read...")
}

func (c Mobile) write() {
    fmt.Println("mobile write...")
}

测试

func main() {
    c := Computer{}
    m := Mobile{}

    c.read()
    c.write()
    m.read()
    m.write()
}

运行结果

func main() {
    c := Computer{}
    m := Mobile{}

    c.read()
    c.write()
    m.read()
    m.write()
}

实现接口必须实现接口中的所有方法

下面我们定义一个OpenClose接口,里面有两个方法open和close,定义个Door结构体,实现其中一个方法。

package main

import "fmt"

type OpenClose interface {
    open()
    close()
}

type Door struct {
}

func (d Door) open() {
    fmt.Println("open door...")
}

func main() {
    var oc OpenClose
    oc = Door{} // 这里编译错误,提示只实现了一个接口
}

golang接口值类型接收者和指针类型接收者

这个话题,本质上和方法的值类型接收者和指针类型接收者,的思考方法是一样的,值接收者是一个拷贝,是一个副本,而指针接收者,传递的是指针。

实例演示

定义一个Pet接口

type Pet interface {
    eat()
}

定义一个Dog结构体

type Dog struct {
    name string
}

实现Pet接口(接收者是值类型)

func (dog Dog) eat() {
    fmt.Printf("dog: %p\n", &dog)
    fmt.Println("dog eat..")
    dog.name = "黑黑"
}

测试

func main() {
    dog := Dog{name: "花花"}
    fmt.Printf("dog: %p\n", &dog)
    dog.eat()
    fmt.Printf("dog: %v\n", dog)
}

运行结果

dog: 0xc000046240
dog: 0xc000046250
dog eat..
dog: {花花}

从运行结果,我们看出dog的地址变了,说明是复制了一份,dog的name没有变说明,外面的dog变量没有被改变。

将Pet接口改为指针接收者

func (dog *Dog) eat() {
    fmt.Printf("dog: %p\n", dog)
    fmt.Println("dog eat..")
    dog.name = "黑黑"
}

再测试

func main() {
    dog := &Dog{name: "花花"}
    fmt.Printf("dog: %p\n", dog)
    dog.eat()
    fmt.Printf("dog: %v\n", dog)
}

运行结果

dog: 0xc00008c230
dog: 0xc00008c230
dog eat..
dog: &{黑黑}

golang接口和类型的关系

  1. 一个类型可以实现多个接口
  2. 多个类型可以实现同一个接口(多态)

一个类型实现多个接口

一个类型实现多个接口,例如:有一个Player接口可以播放音乐,有一个Video接口可以播放视频,一个手机Mobile实现这两个接口,既可以播放音乐,又可以播放视频。

定义一个Player接口

type Player interface {
    playMusic()
}

定义一个Video接口

type Video interface {
    playVideo()
}

定义Mobile结构体

type Mobile struct {
}

实现两个接口

func (m Mobile) playMusic() {
    fmt.Println("播放音乐")
}

func (m Mobile) playVideo() {
    fmt.Println("播放视频")
}

测试

func main() {
    m := Mobile{}
    m.playMusic()
    m.playVideo()
}

运行结果

播放音乐
播放视频

多个类型实现同一个接口

比如,一个宠物接口Pet,猫类型Cat和狗类型Dog都可以实现该接口,都可以把猫和狗当宠物类型对待,这在其他语言中叫做多态

定义一个Pet接口

type Pet interface {
    eat()
}

定义一个Dog结构体

type Dog struct {
}

定义一个Cat结构体

type Cat struct {
}

实现接口

func (cat Cat) eat() {
    fmt.Println("cat eat...")
}

func (dog Dog) eat() {
    fmt.Println("dog eat...")
}

测试

func main() {
    var p Pet
    p = Cat{}
    p.eat()
    p = Dog{}
    p.eat()
}

运行结果

cat eat...
dog eat...

golang接口嵌套

接口可以通过嵌套,创建新的接口。例如:飞鱼,既可以飞,又可以游泳。我们创建一个飞Fly接口,创建一个游泳接口Swim,飞鱼接口有这两个接口组成。

飞Flyer接口

type Flyer interface {
    fly()
}

创建Swimmer接口

type Swimmer interface {
    swim()
}

组合一个接口FlyFish

type FlyFish interface {
    Flyer
    Swimmer
}

创建一个结构体Fish

type Fish struct {
}

实现这个组合接口

func (fish Fish) fly() {
    fmt.Println("fly...")
}

func (fish Fish) swim() {
    fmt.Println("swim...")
}

测试

func main() {
    var ff FlyFish
    ff = Fish{}
    ff.fly()
    ff.swim()
}

运行结果

fly...
swim...

golang 通过接口实现OCP设计原则

而面向对象的可复用设计的第一块基石,便是所谓的”开-闭“原则(Open-Closed Principle,常缩写为OCP)。虽然,go不是面向对象语言,但是也可以模拟实现这个原则。对扩展是开放的,对修改是关闭的。

OCP设计原则实例

下面通过一个人养宠物的例子,来解释OCP设计原则。

定义一个宠物接口Pet

type Pet interface {
    eat()
    sleep()
}

该接口有吃和睡两个方法。

定义个Dog结构体

type Dog struct {
    name string
    age  int
}

Dog实现接口方法

func (dog Dog) eat() {
    fmt.Println("dog eat...")
}

func (dog Dog) sleep() {
    fmt.Println("dog sleep...")
}

定义一个Cat结构体

type Cat struct {
    name string
    age  int
}

Cat实现接口方法

func (cat Cat) eat() {
    fmt.Println("cat eat...")
}

func (cat Cat) sleep() {
    fmt.Println("cat sleep...")
}

定义个Person结构体

type Person struct {
    name string
}

为Person添加一个养宠物方法

func (per Person) care(pet Pet) {
    pet.eat()
    pet.sleep()
}

最后测试一下

func main() {

    dog := Dog{}
    cat := Cat{}
    per := Person{}

    per.care(dog)
    per.care(cat)

}

运行结果

dog eat...
dog sleep...
cat eat...
cat sleep...

使用接口的这种设计方法,可以很好的解耦合代码,实现软件设计的OCP原则(即开闭原则)

这样设计,如果再添加一个宠物,例如:一个鸟Bird,原有的代码不用修改,直接添加就可以。