# 接口
# 一. 概念
由于 go 是强类型语言,所以需要有接口用于抽象及解耦。
# 二. 示例
下面我们一步步引入接口的概念:
假设我们现在抓取一个页面的内容
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
// 抓取一个页面
resp, _ := http.Get("https://www.baidu.com")
// 当页面请求完成时,关闭连接
defer resp.Body.Close()
// 读取内容
bytes, _ := ioutil.ReadAll(resp.Body)
fmt.Printf("%s\n", bytes)
}
我们来把 main 函数里的内容抽象成一个函数
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func retrieve(url string) string {
resp, _ := http.Get(url)
defer resp.Body.Close()
bytes, _ := ioutil.ReadAll(resp.Body)
return string(bytes)
}
func main() {
fmt.Println(retrieve("https://www.baidu.com"))
}
上述 main 函数还是依赖 retrieve 函数,而这个 retrieve 可以由多个来源,假设我们项目中 infra 中实现了一个 retriever 结构体
package infra
type Retriever struct {}
// 定义结构体方法
func (Retriever) Get(url string) string {
resp, _ := http.Get(url)
defer resp.Body.Close()
bytes, _ := ioutil.ReadAll(resp.Body)
return string(bytes)
}
package main
import (
"fmt"
"go-learning/infra"
)
func getRetriever() infra.Retriever {
return infra.Retriever{}
}
func main() {
var retriever infra.Retriever = getRetriever()
fmt.Println(retriever.Get("https://www.baidu.com"))
}
那现在我们的函数还是依赖 infra 中的 retriever, 假设我们需要改成其他结构体的 Retriever,就会让我们将所有 infra.retriever 改成其他,改动非常大。所以我们需要引入接口,来增强我们代码的健壮性
package main
import (
"fmt"
"go-learning/infra"
)
type retriever interface {
Get(string) string
}
func getRetriever() retriever {
// 假如我们修改了其他结构体,那么我们无需把所有推断都改一遍了
return testing.Retriever{}
}
func main() {
var r retriever = getRetriever()
fmt.Println(r.Get("https://www.baidu.com"))
}
# 三、接口的定义
像 java 定义接口是由实现者定义,而 go 语言接口的特点是 go 语言接口由使用者定义。
举例:
package main
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("https://www.baidu.com")
}
func main() {
var r Retriever = mock.Retriever{"this is a demo"}
fmt.Println(download(r))
}
上述代码我们定义一个 Retriever 接口, 接口内部定义 Get 方法,但我们并没有实现这个真正的接口,所以下面我们去真实的实现它。
package mock
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
从上述可以看到,当我们真实的实现这个接口的时候,并没有显示的告诉这是一个接口,这里要记住:Go 接口的实现是隐式的,只要实现接口里的方法就可以了。
# 四、struct 和 interface的区别
struct 结构体: 需要声明字段类型和实现函数逻辑, 描述的是一个类
interface 接口: 只用来描述方法(名,参数,返回值即可),不需要实现里面的逻辑
所以struct和interface根本不在一个纬度上,这块要好好理解下。
根据上述例子区分:
// 定义结构体方法
func (Retriever) Get(url string) string {
resp, _ := http.Get(url)
defer resp.Body.Close()
bytes, _ := ioutil.ReadAll(resp.Body)
return string(bytes)
}
// interface实现的方法
type Retriever interface {
Get(url string) string
}
# 五、接口的组合
type Retriever interface {
Get(url string) string
}
type Poster interface {
Post(url string, form map[string]string) string
}
// 组合
type RetrieverPoster interface {
Retriever,
Poster
}