Golang——接口interface使用

        概念

        在Go语言中接口(interface)是一种抽象的类型,更具体地说它是一组方法的集合,用于代表实现了某些方法的对象集合。

        使用场景

        要具体地去理解go语言中接口的含义,那我们就需要了解一下接口的使用场景(场景的假设肯定存在许多问题,但是重在方便理解)。假设现在有这样一个需求:某交通工具售卖品平台的甲方爸爸让我们做一个获取价格的功能,而为了让这个功能更好地对接入平台,他给了我们一个标准,就是要只我们用一个GetPrice()函数就能获取所有类型交通工具的价格,这样子甲方平台在使用的时候就可以直接往GetPrice()函数内传入各种类型交通工具获取价格而不需要做更多处理,降低对接成本。

        面对这样一个问题,我们来逐步分析。

        首先我们需要先在代码里定义好平台上售卖的各类交通工具,这里先只定义两类工具,飞机和汽车。

//定义飞机结构体
type Airplane struct {
	name  string
	price int
}

//定义汽车结构体
type Car struct {
	name  string
	price int
}

        在正常情况下,我们可以通过定义结构体的方法(方法与函数有区别,有具体的作用对象,这里不做展开,不太了解的可以去看一下go语言的方法)来获取各交通工具的价格,代码如下:

//展示飞机价格
func (a Airplane) ShowPrice() int {
	return a.price
}

//展示汽车价格
func (c Car) ShowPrice() int {
	return c.price
}

        这样子我们就可以通过分别调用这两个方法来分别获取飞机和汽车的价格,调用代码如下:

	a := Airplane{name: "空军1号", price: 100}	//飞机实例化
	fmt.Println(a.name, "的价格为:", a.ShowPrice())	//打印飞机价格

	c := Car{name: "红旗", price: 999}	//汽车实例化
	fmt.Println(c.name, "的价格为:", c.ShowPrice())	//打印汽车价格

        但这样获取价格的方式与甲方的要求不符,因为甲方要求我们只向他们提供一个GetPrice()函数,然后他们就可以通过向这个函数传入各交通工具结构体的实例化对象来直接获取该对象的价格,而不需要像上面的代码一样在获取不同交通工具架构体的实例化对象的价格时去调用各自的方法,这不利于与平台的代码对接(这种情况下平台可能需要改动更多的代码)。

        因此在这种情况下我们还需要在实现一个GetPrice函数,这个函数的作用就是根据传入的结构体实例化对象来调用该对象的ShowPrice方法获取价格,其可能的实现代码如下:

//获取交通工具价格
func GetPrice(vehicle 变量类型) int {
	return vehicle.ShowPrice()
}

        可以看到上述的代码也很简单,就是根据传入的结构体实例化对象调用其ShowPrice方法即可,但是这有一个问题,就是这个传入参数的变量类型到底应该是什么?因为正常情况下,如果GetPrice函数需要调用汽车的ShowPrice方法,那么传入的vehicle变量的类型就应该是Car,然而若是传入的变量类型是Car的话则GetPrice就无法调用Airplane类的ShowPrice方法来获取飞机的价格,这与甲方的要求不符。

        那么,有没有一种类型在传参前既可以代表Airplane又可以代表Car,然后在传参时根据实例化对象来最终确定具体属于哪一类型呢?在go语言中这样的类型就是接口类型(interface)

        这里先看看接口是怎么定义的:

//示例
//type 接口名称 interface {
//	方法1(传入参数) 返回值
//	方法2(传入参数) 返回值
//	...
//}
//定义交通工具接口
type Transportation interface {
	ShowPrice() int
}

        前面提到,接口实际上是一种抽象的类型,它抽象地代表具有某种方法的一大类,就上面的交通工具接口而言,它实际上代表的是所有实现了ShowPrice方法的类,也就是说,如果某个结构体类型实现了ShowPrice方法,那么他就实现了Transportation接口类型,或者说他就是这个接口类型。(如果某个接口内有多个方法,那么结构体需要同时实现该接口内的所有方法才算实现了该接口类型)。这里实际上我们使用Transportation接口抽象地代表了交通工具这一大类。

        因此在上述例子中,由于Car和Airplane类都实现了ShowPrice方法,因此它们都是Transportation接口类型,在这种情况下,GetPrice函数就可以写成下述形式:

//获取交通工具价格
func GetPrice(vehicle Transportation) int {
	return vehicle.ShowPrice()
}

        这样我们就可以在GetPrice函数中传入接口类型Transportation,然后函数在运行时再根据传入的实例化对象进行精确地类型匹配。因此其获取交通工具的价格的代码就变成下列这种方式:

	a := Airplane{name: "空军1号", price: 100}	//飞机实例化
	fmt.Println(a.name, "的价格为:", GetPrice(a))	//打印飞机价格

	c := Car{name: "红旗", price: 999}	//汽车实例化
	fmt.Println(c.name, "的价格为:", GetPrice(c))	//打印汽车价格

        这样子平台在想要获取载具价格的时候便可以直接往GetPrice函数内传入实例化对象而无需考虑对象属于哪种交通工具。

        上述例子的完整代码如下:

package main

import (
	"fmt"
)

//定义飞机结构体
type Airplane struct {
	name  string
	price int
}

//展示飞机价格
func (a Airplane) ShowPrice() int {
	return a.price
}

//定义汽车结构体
type Car struct {
	name  string
	price int
}

//展示汽车价格
func (c Car) ShowPrice() int {
	return c.price
}

//定义交通工具接口
type Transportation interface {
	ShowPrice() int
}

//获取交通工具价格
func GetPrice(vehicle Transportation) int {
	return vehicle.ShowPrice()
}

func main() {
	a := Airplane{name: "空军1号", price: 100}	//飞机实例化
	fmt.Println(a.name, "的价格为:", GetPrice(a))	//打印飞机价格

	c := Car{name: "红旗", price: 999}	//汽车实例化
	fmt.Println(c.name, "的价格为:", GetPrice(c))	//打印汽车价格
}

        运行结果:

         当然,上述例子只是接口最简单的运用方式,实际上接口的使用远比上面描述的要灵活得多,比如在上面的例子中,即使Car和Airplane都实现了ShowPrice方法,但是实际上这两个类的ShowPrice方法的实现内容可以完全不同,因为接口类型只要求某个结构体具有些方法,却不对这些方法的实现做限制。

        举个例子,价格现在平台的所有飞机因为促销降价10块,所有汽车因为缺货涨价100块,那么其代码完全可以写成下列形式:

//展示飞机价格
func (a Airplane) ShowPrice() int {
	return a.price-10
}

//展示汽车价格
func (c Car) ShowPrice() int {
	return c.price+100
}

        这样平台就可以在无需更改接口的情况下直接调用GetPrice去获取最新的飞机和汽车的价格,因此我们可以利用接口类型完成很多便捷的操作(个人认为这实际上跟部分编程语言的函数重载类似,而由于go语言并没有函数重载,因此某种程度来说接口可以算是一种“平替”)

        参考资料

Golang 学习——interface 接口学习(一) | Go 技术论坛

Golang 中 Interface 的用法 - Kunlun Jun

Effective Go - The Go Programming Language

Go语言基础19 接口interface_哔哩哔哩_bilibili

接口 · Go语言中文文档

猜你喜欢

转载自blog.csdn.net/llslinliansheng/article/details/126559829