Go语言编程思想1——面向接口
点击阅读更多查看文章内容
Go语言编程思想1——面向接口
示例
以下示例中包含有两个Retriever分别是实际使用的infra.Retriever以及测试用的testing.Retriever,再两者互换时,传统的方法需要修改多处变量,较为繁琐。
分析本例可以发现Retriever只是一个具有Get方法的变量,即getRetriever只需要返回一个可以调用Get方法的变量即可
这里的retriever即为一个具有Get方法的接口,GetRetriever返回接口,这样在修改变量时只需要修改getRetriever中的返回值即可,而不需每个变量都做修改。
1 | func getRetriever() retriever { |
一、接口的概念
duck typing
传统的鸭子指的是真正的有生命的鸭子,必须属于脊索动物门,脊椎动物亚门…
duck typing则认为下图中的大黄鸭也是鸭子,只要像鸭子就认为是鸭子
描述事物的外部行为而非内部结构
严格说go属于结构化类型系统,类似duck typing
在download前写一个注释说明必须实现了get方法才能作为函数的参数,如果参数没有实现get方法则会报错
python中的duck typing非常灵活,不管retriever是什么,只要实现了get方法,就可以传入download使用
灵活性与python类似
java中没有duck typing,必须实现Retriever接口,只有get方法也无法调用
(与前面说的鸭子必须是指有生命的类似)
缺点:无法实现多个接口
同时需要两个接口、具有灵活性、具有类型检查(无需注释说明)
二、接口定义
定义接口
接口由使用者定义
download是使用者,使用者要get,因此我们要在Retriever中定义get方法
接口中定义方法不需要加func关键字,它里面本身就全是函数
1 | type Retriever interface { |
实现接口
接口的实现是隐式的
只要实现了Retriever接口中的方法就认为实现了Retriever接口
1 | type Retriever struct { |
三、接口的值类型
接口变量中包含有实现者的类型和实现者的值
- 接口变量自带指针
- 接口变量同样采用值传递,几乎不需要使用接口的指针,因为它可以包含一个指针
- 指针接受者只能用指针方式使用,值接受者两者都可以
查看接口变量
- 表示任何类型:interface{}
Queue可以传入任何类型如要将Push()和Pop()的值都限定为int1
2
3
4
5
6
7
8
9
10
11
12
13//将类型设为interface后,队列可以存放任何类型
type Queue []interface{}
func (q *Queue) Push(v interface{}) {
fmt.Println(&q)
*q = append(*q, v)
}
func (q *Queue) Pop() interface{} {
head := (*q)[0]
*q = (*q)[1:]
return head
}
Push的输入限定为int
Pop的输出为int,返回时通过.()将interface{}转换为int
1 | //将类型设为interface后,队列可以存放任何类型 |
- Type Assertion
1 | r = &real.Retriever{ |
- Type Switch
1 | func inspect(r Retriever) { |
四、接口的组合
1 |
|
实现接口只需要把所有方法都定义即可
1 | type Retriever struct { |
五、常用系统接口
- Stringer
任何类型只要定义了String()方法,进行Print输出时,就可以得到定制输出。1
2
3type Stringer interface {
String() string
}1
2
3
4func (r *Retriever) String() string {
return fmt.Sprintf(
"Retriever:{Contents=%s}",r.Contents)
}
1 | func main() { |
Reader
Writer
Go语言编程思想1——面向接口