您现在的位置是:首页 >学无止境 >go破冰之旅·15·go语言interface之综合玩法网站首页学无止境

go破冰之旅·15·go语言interface之综合玩法

ProblemTerminator 2024-06-12 12:01:02
简介go破冰之旅·15·go语言interface之综合玩法

相信一次5-10分钟即可搞定,实用+效率!

上文中,我们重点看了go语言的interface、方法及其接收者的各种玩法:go接口、方法及接收者重点说明

本文我们重点变换Interface看看接口的其它玩法及注意事项。

目录

接口类型作为参数、接口类型的变量

实现多个接口

接口嵌套与综合应用

接口类型的变量.方法


接口类型作为参数、接口类型的变量

接口类型也可以作为参数进行传递,传递过程中可以自然而然的使用其方法,这里贴一下基本定义:

// 定义一个动物的基准接口,定义一个“吃”的行为
type IAnimals interface {
	Eat()
}

// 对于本案例来说要不要Animals 结构体都可
// 为了一起把“组合”模式结合进来,这样看起来更丰富
type Animals struct {
	Name string
}

// Cat将Animals组合了进来
type Cat struct {
	Animals
}

type Bird struct {
	Animals
}

func (a Cat) Eat() {
	fmt.Printf("I'm %s, eating a fish.
", a.Name)
}

func (a Bird) Eat() {
	fmt.Printf("I'm %s, eating a insect.
", a.Name)
}

单独再给出一个函数,将接口类型作为参数传入:

// 用i来“吃”,调用之前不知道谁在“吃”
func Eating(i IAnimals) {
	i.Eat()
}

定义一个接口类型的变量(准备好函数参数):

	var animal IAnimals
	// 接口类型的变量可以存储任何实现了该接口的类型的值,这里为Cat
	animal = Cat{Animals{Name: "cat_john"}}
    // cat_john在吃
	Eating(animal)   // I'm cat_john, eating a fish.

结果在注释中,可以看到这样也可以玩,但万变不离其宗,核心主线还是实例化的对象能调用它实现的接口的方法。

实现多个接口

对于上述这个典型的案例,鸟除过吃还可以有自己独特的要素,那就是“飞”;

猫除过吃还有自己的专属“喵喵叫”,我们来实现下:

// Cat独有的要素/行为接口
type ICatCall interface {
	Meow() // 喵喵叫
}

// Bird独有的要素/行为接口
type IBirdFly interface {
	Fly() // 飞
}

// Bird除过实现Eat()方法,还实现了Fly()
func (a Bird) Fly() {
	fmt.Printf("I'm %s, i can fly.
", a.Name)
}

// Cat实现了Meow()
func (a Cat) Meow() {
	fmt.Printf("I'm %s, i can miaomiaojiao.
", a.Name)
}

调用各自特有方法,一块贴出结果:

	var animalCat = new(Cat)
	animalCat.Name = "cat_john"
	animalCat.Meow()
	animalCat.Eat()

	var animalBird = new(Bird)
	animalBird.Name = "bird_frank"
	animalBird.Eat()
	animalBird.Fly()
	
	/*
	I'm cat_john, i can miaomiaojiao.
	I'm cat_john, eating a fish.
	I'm bird_frank, eating a insect.
	I'm bird_frank, i can fly.
	*/

可以看到不论是Cat、Bird都实现了两个接口,其中有一个定义的是各自独有的行为,你还可以基于此继续扩展。

接口嵌套与综合应用

我们再考虑这种情况,鸟除过飞之外,还可以游泳,对于鸟来说我们将飞、游泳分开设计了接口,但这些都属于鸟自有的运动行为,能不能综合起来呢?

我们增加一个游泳接口,并将已有的飞接口和游泳接口合并起来,形成一个鸟的运动接口:

// Bird独有的要素/行为接口
type IBirdSwim interface {
	Swim() // 游泳
}

// swim
type IBirdSport interface {
	IBirdFly
	IBirdSwim
}

// 让Bird也实现IBirdSwim接口
func (a Bird) Swim() {
	fmt.Printf("I'm %s, i can swim.
", a.Name)
}

来让Bird飞舞一下:

	// 先这么玩,实例化一个Bird对象,并依次调用各个方法
	var animalBird = new(Bird)
	animalBird.Name = "bird_frank"
	animalBird.Eat()
	animalBird.Fly()
	animalBird.Swim()

	// 再这么玩,声明一个IBirdSport接口变量
	var animal IBirdSport
	// 实例化为Bird
	animal = Bird{Animals{Name: "bird_frank"}}
	
	animal.Fly()
	animal.Swim()

speed running:

I'm bird_frank, eating a insect.
I'm bird_frank, i can fly.
I'm bird_frank, i can swim.
I'm bird_frank, i can fly.
I'm bird_frank, i can swim.

没什么问题,但第二个玩法中怎么调用不了Eat()这个基本行为了呢?

animal变量和第一种中的animalBird不同,第一种是Bird类型的对象,它实现了IAnimals、IBirdFly、IBirdSwim三个接口,因此它可以调用这三个方法;

而在第二种调用中,只能调用Fly()和Swim()方法,因为IBirdSport仅嵌套了IBirdFly、IBirdSwim两个接口,如果也要保证能Eat(),则将IAnimals 这个基本接口也嵌入IBirdSport中就可以了。

接口类型的变量.方法

在上述的各种案例中,不止一次的出现了这种情况:

// 用i来“吃”,调用之前不知道谁在“吃”
func Eating(i IAnimals) {
	i.Eat()
}

再进行简单的变换:
var  i IAnimals
// other opts
i.Eat()

也就是使用接口变量直接调用了方法,当出现这种情况时需满足两个条件,否则接口变量无法完成调用其方法:

1,变量 i 一定不为nil,否则就是空指针调用;

2,一定是已经基于实现该接口的类型实例化后的对象赋值给了i,这时i就具有了实际意义,并非仅使用IAnimals进行了声明。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。