概念与用法
接口是一个go语言中特有的概念,我们该如何理解呢?
我们从现实生活中的例子着手来看,现实生活中有USB接口,USB是一种通用的接口协议,我们可以在手机、台式电脑、笔记本电脑等各种场景下方便使用,它和具体硬件无关。
go中的接口和上面USB接口有异曲同工之妙,只不过它约定的是结构体需要实现哪些方法,只要满足了这个接口中需要实现的方法,那么就可以归类为这个接口。
接口的基本语法如下:
type ReadWriter interface {
Read(b Buffer) bool // 这里约定接口需要实现的方法 参数 返回值
Write(b Buffer) bool
}
另外接口也是一种类型,定义好后我们可以像正常变量一样做为参数类型
PS: 接口名称在命名上有一个隐形规则,用xxer来命名,当然如果不太能用er结尾,用其它也是可以的
场景一:多态
多态是面向对象语言中一种常见用法
package main
import "fmt"
// 定义一个动物接口
type Animal interface {
Speak() string // 约定需要有speak方法
}
// 狗结构体
type Dog struct{}
// dog 实现Speak方法
func (d *Dog) Speak() string {
return "汪汪"
}
// 猫结构体
type Cat struct{}
// cat 实现Speak方法
func (c *Cat) Speak() string {
return "喵喵"
}
func main() {
animals := []Animal{&Dog{}, &Cat{}}
for i, a := range animals {
fmt.Println(i, a.Speak())
}
}
// 输出
// 0 汪汪
// 1 喵喵
场景二:依赖注入
依赖注入指的是,底层的实现和高层的使用分开,通常用于使用层,实现层的解耦,真实项目中用的非常多
比如:
- 数据库可以使用mysql、pg、sqlite,但是对于上层数据存储并不关心使用具体是哪一个,可以将底层数据库抽象出来形成一个接口,底层每个数据库的代码实现相关接口就可以
- 又比如,发短信有很多商家阿里/云片等,但是上层对于发短信而言,不用关心具体使用哪一种去发短信,可以将发短信抽象成一个接口,底层去实现具体每家的相关接口
说起来比较抽象,我们看代码
package main
import "fmt"
// 信息发送接口
type MessageSender interface {
SendMessage(message string) // 都需要有一个发送信息方法
}
// 接口公用通知函数
func Notify(sender MessageSender, message string) {
sender.SendMessage(message) //
}
// 邮箱
type EmailSender struct{}
func (s *EmailSender) SendMessage(message string) {
fmt.Println("send email: ", message)
}
// 短信
type SmsSender struct{}
func (s *SmsSender) SendMessage(message string) {
fmt.Println("send sms: ", message)
}
func main() {
smsSender := &SmsSender{}
emailSender := &EmailSender{}
// 将邮箱和 短信底层实发送进行了抽离
Notify(smsSender, "这是短信")
Notify(emailSender, "这是邮件")
}
// 输出
// send sms: 这是短信
// send email: 这是邮件
场景三:空接口
空接口也是项目中经常会遇到的,它主要用于代表参数可以是任意类型,这有点动态语言的味道了。
看代码
package main
import "fmt"
// 这里代表v可以是任意类型
func ProcessValue(val interface{}) {
switch val.(type) {
case string:
fmt.Println("string:", val)
case float64:
fmt.Println("float64:", val)
case bool:
fmt.Println("bool:", val)
case *Person:
fmt.Println("*person:", val)
default: // 注意这里是default 而不是else
fmt.Println("unknow:", val)
}
}
type Person struct{}
func main() {
// 这里切片中存了任意类型值
values := []interface{}{
"hello world",
234.355,
false,
12,
&Person{},
}
for _, val := range values {
ProcessValue(val)
}
}
// 输出
// string: hello world
// float64: 234.355
// bool: false
// unknow: 12
// *person: &{}
场景四:接口嵌套
一个接口中可以嵌套另外一个接口,通常可以用于接口的扩展
package main
import "fmt"
// 定义基本接口
type Reader interface {
Read() string
}
// 在基本接口的基础上定义扩展接口
type ReaderWithInfo interface {
Reader // 这里嵌套了另外一个接口
Info() string
}
// 实现基本接口
type MyDevice struct{}
func (d MyDevice) Read() string {
return "Reading data..."
}
// 实现扩展接口
func (d MyDevice) Info() string {
return "MyDevice is ready."
}
func main() {
device := MyDevice{}
// device 由于同时实现了Read和Info 因此可以同时即属于Reader接口 也属于 ReaderWithInfo接口
var reader Reader = device
var readerWithInfo ReaderWithInfo = device
fmt.Println(reader.Read())
fmt.Println(readerWithInfo.Read())
fmt.Println(readerWithInfo.Info())
}
// 输出
// Reading data...
// Reading data...
// MyDevice is ready.
网友评论