本文正在参加金石计划
什么是接口
接口是一种标准化的约定或规范,它规定了两个个体之间如何进行通信和交互。
假设你要与一个陌生人交谈,你可能需要遵守一些基本的礼仪规范,比如问候、介绍自己、交换名片等等。这些规范可以帮助你和陌生人之间建立信任和合作关系,同时也确保了交流的顺畅和准确。
在实际编程中,实现一个接口通常需要实现接口定义的所有方法,以确保对象可以被正确使用和调用。这也意味着接口可以被用来强制规范对象的行为,以确保它们符合特定的要求和标准。
接口是一种抽象的工具,因为它并不关心具体的实现细节,而只关注于对象或类应该实现的方法或功能。
假设你正在编写一个程序,需要处理一些动物对象,比如狗、猫、鸟等等。你可以定义一个名为“动物”的接口,它规定了这些对象应该实现哪些方法,比如“吃饭”、“睡觉”、“叫声”等等。这些方法只是描述了动物应该具有的行为,而并不关心这些行为如何被实现。不同的动物可以根据自己的特点来实现这些方法,比如狗可能会发出“汪汪”声,而猫则可能会发出“喵喵”声。
总的来说,接口是一种必须遵守的约定与规范,这使得两个个体之间的交互得到了保证,并且其抽象的特性,又是规范具有灵活性而不用遵守死规矩。
怎么声明一个接口
接口类型的定义通常由一组方法签名组成,这些方法签名定义了接口的行为规范。如下:
type Greeter interface{
Greet() string
}
interface{
Add() int
}
这里我们声明了两个接口类型,其中一个名为Greeter,它里面有一个方法名为Greet,返回类型为string的方法签名;另外一个接口它没有类型名,因此它是匿名接口,其内部有一个方法名为Add,返回值类型为int的方法签名。
与结构体类似的,接口也允许嵌套,例子如下:
type Adder interface{
Add(int, int) int
}
type Subtractor interface{
Sub(int, int) int
}
type Calculator interface{
Adder
Subtractor
}
在这个例子中,我们声明了三个接口,分别是Adder、Subtractor与嵌套了前面两者的CalCulator。Adder接口中有一个名为Add,参数类型为两个int类型,返回值类型为int类型的方法签名;Subtractor接口中有一个名为Sub,参数类型为两个int类型,返回值类型为int类型的方法签名。
怎么初始化一个接口变量
实现接口
接口没有被实现,那它是没有意义的,因此使用接口的第一步应该是先实现它:
type Cal struct{}
func (c Cal) Add(i1, i2 int) int {
return i1 + i2
}
func (c Cal) Sub(i1, i2 int) int {
return i1 - i2
}
Go的接口是隐式实现的,只要具体类型的方法集是接口方法的超集,就代表该类型实现了接口。
接口初始化
单纯的声明一个接口变量没有任何意义,接口只有被初始化为具体的类型是才有意义。其初始化条件同样符合「变量」赋值条件的基本逻辑,但是接口的初始化更加简单,因为只要右值的方法集是左值的超集就能赋值:
-
用结构体赋值
var c Calculator c = Cal{} -
用接口变量赋值
var c2 Calculator = Cal{} var s Subtractor = c2
接口有什么用
举一个Gin框架路由的例子:
// IRouter defines all router handle interface includes single and group router.
type IRouter interface {
IRoutes
Group(string, ...HandlerFunc) *RouterGroup
}
// IRoutes defines all router handle interface.
type IRoutes interface {
Use(...HandlerFunc) IRoutes
Handle(string, string, ...HandlerFunc) IRoutes
Any(string, ...HandlerFunc) IRoutes
GET(string, ...HandlerFunc) IRoutes
POST(string, ...HandlerFunc) IRoutes
DELETE(string, ...HandlerFunc) IRoutes
PATCH(string, ...HandlerFunc) IRoutes
PUT(string, ...HandlerFunc) IRoutes
OPTIONS(string, ...HandlerFunc) IRoutes
HEAD(string, ...HandlerFunc) IRoutes
Match([]string, string, ...HandlerFunc) IRoutes
StaticFile(string, string) IRoutes
StaticFileFS(string, string, http.FileSystem) IRoutes
Static(string, string) IRoutes
StaticFS(string, http.FileSystem) IRoutes
}
// RouterGroup is used internally to configure router, a RouterGroup is associated with
// a prefix and an array of handlers (middleware).
type RouterGroup struct {
Handlers HandlersChain
basePath string
engine *Engine
root bool
}
api := engine.Group("/api/v1")
api.GET("/tags", tag.List)
以上代码有两个接口,分别是IRouter和IRoutes,还有一个结构体RouterGroup。engine.Group方法将会创建一个RouterGroup,并将其赋值给api。api的逻辑意义就是作为“/api/v1”则个组,后面再添加的路径,比如说“api/v1/test”或者“api/v1/t/a”之类的所有子路径都在api这个组别下。并且RouterGroup通过名字可以看出它的语义就是一个路由组,而它不但实现了IRouter,称为一个根路径,并且还实现了IRoutes,使其能够直接绑定子路由。
通过接口,不但让RouterGroup具有IRouter和IRoutes两种状态,还制定了Router的处理标准与规范,这就是接口最大的意义——多态性、标准化。