一、duck typing的概念
1.1、什么是 duck typing
“像鸭子走路,像鸭子叫(长得像鸭子),那么就是鸭子”
描述事物的外部行为而非内部结构
严格说 go 属于结构化类型系统,类似 duck typing
1.2、python 中的 duck typing
def download(retriever):
return retriever.get("lulublog.cn")
运行时才知道传入的 retriever 有没有 get
需要注释来说明接口
1.3、C++中的 duck typing
template
string download ( const R& retriever) {
return retriever.get( "lulublog.cn") ;
}
编译时才知道传入的 retriever 有没有 get
需要注释来说明接口
1.4、java 中的类似代码
String download(R r) {
return r.get ( "lulublog.cn" ) ;
}
传入的参数必须实现 Retriever 接口
不是 duck typing
二、接口的定义和实现
2.1、接口的定义
接口是一类规范,是某一些方法的集台
type Animal interface {
Eat()
Run()
}
2.2、接口的实现
接口的实现是隐式的
只要实现接口里的方法
2.3、例1
实现1
package main
import "fmt"
type Animal interface {
Eat()
Run()
}
type Cat struct {
Name string
}
func (c Cat) Run() {
fmt.Println(c.Name, "开始跑")
}
func (c Cat) Eat() {
fmt.Println(c.Name, "开始吃")
}
func main() {
var a Animal
c := Cat{
Name: "Tom",
}
a = c
a.Eat()
a.Run()
}
实现2
package main
import "fmt"
type Animal interface {
Eat()
Run()
}
type Cat struct {
Name string
}
func (c Cat) Run() {
fmt.Println(c.Name, "开始跑")
}
func (c Cat) Eat() {
fmt.Println(c.Name, "开始吃")
}
func main() {
var a Animal
a = Cat{
Name: "Tom",
}
a.Eat()
a.Run()
}
实现3:泛型
package main
import "fmt"
type Animal interface {
Eat()
Run()
}
type Cat struct {
Name string
}
func (c Cat) Run() {
fmt.Println(c.Name, "开始跑")
}
func (c Cat) Eat() {
fmt.Println(c.Name, "开始吃")
}
func main() {
c := Cat{
Name: "Tom",
}
MyFunc(c)
}
func MyFunc(a Animal) {
a.Eat()
a.Run()
}
实现4:解耦合,先明一个接口,然后一切方法都用这个接口来操作
package main
import "fmt"
type Animal interface {
Eat()
Run()
}
type Cat struct {
Name string
}
func (c Cat) Run() {
fmt.Println(c.Name, "开始跑")
}
func (c Cat) Eat() {
fmt.Println(c.Name, "开始吃")
}
var L Animal
func main() {
c := Cat{
Name: "Tom",
}
MyFunc(c)
L.Eat()
L.Run()
}
func MyFunc(a Animal) {
L = a
}
2.4、例2
main.go
package main
import (
"fmt"
"learn/retriever/mock"
)
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("lulublog.cn")
}
func main() {
var r Retriever
r = mock.Retriever{"this is a fake lulublog.cn"}
fmt.Println(download(r))
}
mockretriever.go
package mock
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
目录
运行结果
go run /mnt/go/src/learn/retriever/main.go
this is a fake lulublog.cn
2.5、例3
main.go
package main
import (
"fmt"
"learn/retriever/real"
)
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("http://lulublog.cn")
}
func main() {
var r Retriever
r = real.Retriever{}
fmt.Println(download(r))
}
retriever.go
package real
import (
"net/http"
"net/http/httputil"
)
type Retriever struct {
}
func (r Retriever) Get(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
result, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err != nil {
panic(err)
}
return string(result)
}
目录
运行结果
三、接口的值类型
3.1、接口变量里有什么
实现者的类型
实现者的指针
接口变量自带指针
接口变量同样采用值传递,几乎不需要使用接口的指针
指针接收者实现只能以指针方式使用;值接收者都可
3.2、查看接口变量
3.2.1、Type
Type Assertion
Type Switch
main.go
package main
import (
"fmt"
"learn/retriever/mock"
"learn/retriever/real"
)
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("http://lulublog.cn")
}
//查看接口变量:Type Switch
func inspect(r Retriever) {
fmt.Printf("%T %v\n", r, r)
switch v := r.(type) {
case mock.Retriever:
fmt.Println("Contents:", v.Contents)
case *real.Retriever:
fmt.Println("UserAgent:", v.UserAgent)
}
}
func main() {
var r Retriever
r = mock.Retriever{
Contents: "this is a fake lulublog.cn",
}
inspect(r)
r = &real.Retriever{
UserAgent: "Mozilla/5.0",
}
inspect(r)
//查看接口变量:Type assertion
if mockRetriever, ok := r.(mock.Retriever); ok {
fmt.Println(mockRetriever.Contents)
}else{
fmt.Println("not a mock retriever")
}
realRetriever := r.(*real.Retriever)
fmt.Println(realRetriever.UserAgent)
// fmt.Println(download(r))
}
mockretriever.go
package mock
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
retriever.go
package real
import (
"net/http"
"net/http/httputil"
)
type Retriever struct {
UserAgent string
}
func (r *Retriever) Get(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
result, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err != nil {
panic(err)
}
return string(result)
}
目录结构
运行结果
go run /mnt/go/src/learn/retriever/main.go
mock.Retriever {this is a fake lulublog.cn}
Contents: this is a fake lulublog.cn
*real.Retriever &{Mozilla/5.0}
UserAgent: Mozilla/5.0
not a mock retriever
Mozilla/5.0
3.2.2、表示任何类型: interfacef{}
queue.go
package queue
type Queue []interface{}
func (q *Queue) Push(v interface{}) {
*q = append(*q, v)
}
func (q *Queue) Pop() interface{} {
head := (*q)[0]
*q = (*q)[1:]
return head
}
func (q *Queue) IsEmpty() bool {
return len(*q) == 0
}
entry.go
package main
import (
"fmt"
"learn/queue"
)
func main() {
q := queue.Queue{1}
q.Push(2)
q.Push(3)
fmt.Println(q.Pop())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
q.Push("abc")
fmt.Println(q.Pop())
}
目录结构
运行结果
go run /mnt/go/src/learn/queue/entry/entry.go
1
2
false
3
true
abc
四、接口的组合
main.go
package main
import (
"fmt"
"learn/retriever/mock"
)
const url = "http://lulublog.cn";
type Retriever interface {
Get(url string) string
}
type Poster interface {
Post(url string, form map[string]string) string
}
type RetrieverPost interface {
Retriever
Poster
}
func session(r RetrieverPost) string {
r.Post(url, map[string]string{
"contents": "another fake lulublog.cn",
})
return r.Get(url)
}
func main() {
mockRetriever := mock.Retriever{
Contents: "this is a fake lulublog.cn",
}
fmt.Println("try a session with mockRetriever")
fmt.Println(session(&mockRetriever))
}
mockretriever.go
package mock
type Retriever struct {
Contents string
}
func (r *Retriever) Get(url string) string {
return r.Contents
}
func (r *Retriever) Post(url string, form map[string]string) string {
r.Contents = form["contents"]
return "ok"
}
目录结构
运行结果
go run /mnt/go/src/learn/retriever/main.go
try a session with mockRetriever
another fake lulublog.cn
五、常用系统接口
5.1、Stringer
main.go
package main
import (
"fmt"
"learn/retriever/mock"
)
type Retriever interface {
}
func main() {
var r Retriever
r = mock.Retriever{
Contents: "this is a fake lulublog.cn",
}
fmt.Printf(" > Type:%T Value:%v\n", r, r)
}
mockretriever.go
package mock
import "fmt"
type Retriever struct {
Contents string
}
func (r Retriever) String() string {
return fmt.Sprintf(
"Retriever: {Contents=%s}", r.Contents)
}
目录结构
运行结果
go run /mnt/go/src/learn/retriever/main.go
> Type:mock.Retriever Value:Retriever: {Contents=this is a fake lulublog.cn}
//注释 String() 方法
> Type:mock.Retriever Value:{this is a fake lulublog.cn}
5.2、Reader/Writer
loop.go
package main
import (
"bufio"
"fmt"
"io"
"strings"
)
func printFileContents(reader io.Reader) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
func main() {
fmt.Println("printing a string:")
s := `abc"d"
kkkk
123
p`
printFileContents(strings.NewReader(s))
}
运行结果
go run /mnt/go/src/learn/loop/loop.go
printing a string:
abc"d"
kkkk
123
p