文章目录
1、duck typing的概念
1.1 接口
相比较传统语言来说,go语言的接口更加的灵活
type Traversal interface{
Traverse()
}
func main(){
traversal := getTraversal()
traversal.Traverse()
}
1.2 duck typing概念
-
“像鸭子走路,像鸭子叫(长的像鸭子),那么就是鸭子”
-
描述事物的外部行为而非内部结构
-
要从使用者的角度来看
-
严格说go属于结构化类型系统,类似duck typing
1.2.1 python中的duck typing
def download(retriever):
return retriever.get("pfeiwang.blog.csdn.net")
- 运行时才知道传入的retriver有没有get方法
- 需要注释来说明接口
1.2.2 c++中的duck typing
通过模版来支持
template <class R>
string download(const R& retriever){
return retriever.get("pfeiwang.blog.csdn.net");
}
- 编译时才知道传入的retriever有没有get
- 徐阿哟注释来说明接口
1.2.3 java中的类似代码
<R extends Retriever>
String download(R r){
return r.get("pfeiwang.blog.csdn.net");
}
- 传入的参数必须实现Retriever接口
- 不是duck typing
1.2.4 go中的duck typing
- 同时需要Readable、Appendable怎么办?(apache polygene)
- 同时具有python、c++的duck typing的灵活性
- 又具有java的类型检查
2、接口的定义和实现
- 接口由使用者定义
2.1 接口的实现
- 接口的实现是隐式的
- 只需要实现接口里的方法
目录结构如下:
retriever
mock
mockretriever.go
real.go
main.go
// main.go
package main
import (
"fmt"
"retriever/mock"
)
type Retriever interface{
Get(url string) string
}
func download(r Retriever) string{
return r.Get("http://pfeiwang.blog.csdn.net")
}
func main(){
// Retriever_struct来自于mockretriever.go,Retriever来自于real.go,一个是可以真实下载的,一个是fake
//r := mock.Retriever_struct{"hello world"}
retriever := mock.Retriever{}
fmt.Println(download(retriever))
}
// mock/mockretriever.go
package mock
type Retriever_struct struct{
Constents string
}
func (r Retriever_struct) Get(url string) string{
return r.Constents
}
// mock/real.go
package mock
import (
"net/http"
"net/http/httputil"
"time"
)
type Retriever struct{
UserAgent string
TimeOut time.Duration
}
func (r Retriever) Get(url string) string{
resp, err := http.Get(url)
if err != nil{
panic(err)
}
request, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err != nil{
panic(err)
}
return string(request)
}
3、接口的值类型
3.1 接口变量里面有什么?
- 接口变量自带指针
- 接口变量同时采用值传递,几乎不需要使用接口的指针
- 指针接收者实现只能以指针方式使用;值接收者都可
文件内容同2.1那么var r Retriever
的r是值类型,但是他的内部是什么样子的呢?
fmt.Printf("%T %v\n", r, r)
其中%T表示输出r的类型,%v表示输出r的值
mock.Retriever {this is a fake pfeiwang.blog.csdn.cn}
real.Retriever { 0s}
此时,如果将Retriever的接收者改为指针接收者
func (r *Retriever) Get(url string) string{
resp, err := http.Get(url)
if err != nil{
panic(err)
}
request, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err != nil{
panic(err)
}
return string(request)
}
那么,声明变成
var r Retriever
r = &real.Retriever{
UserAgent: "mother",
TimeOut: time.Minute,
}
此时使用fmt.Printf("%T %v\n", r, r)
,得到如下输出
*real.Retriever &{mother 1m0s}
3.2 获取接口里面的类型
switch v := r.(type){
case mock.Retriever:
fmt.Println("contents", v.Contents)
case *real.Retriever:
fmt.Println("UserAgent", v.UserAgent)
}
3.2.1 type assertion(类型断言)
上面的r.(type)就是一个类型断言
还有一种使用方式https://segmentfault.com/a/1190000012495480
realRetriever := r.(*real.Retriever)
如果r是*real.Retriever类型的返回这个类型,不然的话会panic,但是要用下面更加合理的方式进行coding
if curRetriever, ok := r.(mock.Retriever); ok{
fmt.Println(curRetriever.Contents)
} else{
fmt.Println("not a mock retriever")
}
3.2.2 查看接口变量
- 表示任何类型: interface{}
- Type Assertion
- Type Switch
例子:下面的queue可以推入任何类型的元素
package queue
type Queue []interface{} // 定义一个任意类型的切片
func (q *Queue) Push(v interface{}){
*q = append(*q, v)
// *q = append(*q, v.(int))
}
func (q *Queue) Pop() interface{}{
head := (*q)[0]
*q = (*q)[1:]
// return head.(int)
return head
}
func (q *Queue) IsEmpty() bool{
return len(*q) == 0
}
4、接口的组合
有Retriever、Poster两个接口,然后进行接口的组合,之后可以在session中调用他们。
type Retriever interface {
Get(url string) string
}
type Poster interface {
Post(url string,
form map[string]string) string
}
func download(r Retriever) string{
return r.Get("pfeiwang.blog.csdn.net")
}
func post(poster Poster){
poster.Post("pfeiwang.blog.csdn.net",
map[string]string {
"name": "Golang",
"course": "go"
})
}
// 组合interface
type RetrieverPoster interface{
Retriever
Poster
Connect(host string)// 定义其他方法
}
func Session(s RetrieverPoster){
// 在传入的Retriever、Poster接口中要同时实现Get和Post
s.Get()
s.Post()
}