学习Golang的接口

前言

接口是一种限制一种约定
要求对象类型实现了某些方法  

第一个例子 

文件1:infra.go

这是自定义的infra包

在Go语言中 结构体其实就是(对象)类的载体

在文件1中

实现了结构体 Retriever

并且实现了方法 Get

        Get方法符合规定的参数要求:一个传入string参数 返回一个string参数

package infra

import (
	"io/ioutil"
	"net/http"
)

type Retriever struct {
}

func (Retriever) Get(url string) string {
	resp, err := http.Get(url)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	bytes, _ := ioutil.ReadAll(resp.Body)
	return string(bytes)
}

文件2: main.go

调用我们定义的infra包

在文件2中

type retriever interface{

        Get(string)string

}

这是定义了一个接口

观察发现 infra包中的 Retriever结构体确实按照要求实现了Get方法

因此 可以用接口类型 retriever 定义变量 something

var something retriever

这里要求 something 按照要求实现了Get方法

package main

import (
	"fmt"
	"infra"
)

func getRetriever() infra.Retriever {
	return infra.Retriever{}
}

//接口是一种限制一种约定
//要求接收的对象类型实现了某些方法
type retriever interface {
	// Get 在这里指定要求实现哪些具体的方法
	Get(string) string
}

func main() {
	//Something that can "Get"
	var something retriever = getRetriever()
	fmt.Println(something.Get("https://www.baidu.com"))
}

运行效果 

深入分析

关于接口的讨论:
应当关注 实现类 的外部行为而非内部结构

认识Duck Typing的概念

duck typing
一个东西究竟是不是鸭子

取决于它的能力

游泳起来像鸭子(不同的场景有不同的要求

叫起来也像鸭子(不同的场景有不同的需要

那么就可以是鸭子

对比不同语言

python 只有在运行时才能知道
使用的对象是否按照接口的要求实现了某些方法
需要通过注释约定接口的要求
对传入的对象参数没有类型检查

C++ 只有在编译时才能知道
使用的对象是否按照接口的要求实现了某些方法
需要通过注释约定接口的要求
对传入的对象参数没有类型检查

JAVA 在写代码时就能发现
使用的对象是否按照接口的要求实现了某些方法
传入的参数类型被确定 有参数类型检查 不必通过注释说明
但并不是duck typing的理想概念 不够灵活
例如JAVA在更新拓展接口时 往往需要大量的代码修改

Golang是面向接口的编程语言
接口由使用者定义而非实现者定义
实现了指定的方法就能直接被接口类型引用(调用)

结论:Go语言的接口是一组方法的集合

Go语言颠覆性的接口设计

认识侵入式接口与非侵入式接口
侵入式:JAVA C++
非侵入式:Golang
侵入式:实现类需要明确指出自己实现了某个接口
C++     实现的类名 : public 接口名 (虚继承)
JAVA   实现的类名 implements 接口名

非侵入式:只要实现了接口规定的一组方法 就可以直接被接口类型调用

实现类不需要显示声明自己实现了某些接口

第二个例子 

package main

import (
	"fmt"
)

//定义一个对象 实现接口
type implement struct {
	Contents string
}

func (i implement) Get(url string) string {
	return i.Contents
}

type Retriever interface {
	Get(url string) string
}

func download(r Retriever) string {
	return r.Get("https:// www.baidu.com")
}

//download是使用者
//r是实现者
func main() {
	var r Retriever = implement{"Hello World"}
	fmt.Println(download(r))
}

分析程序的执行过程

1.定义了一个implement对象

2.为implement对象绑定了方法Get

        传入一个string参数 返回一个string参数

3.定义一个Retriever的接口类型

        要求实现Get方法

        传入一个string参数 返回一个string参数

4.实现download函数 传入Retriever接口类型的变量

        返回Get方法的调用结果

5.在main函数中 初始化了一个implement对象

6.将初始化的implement对象赋值给接口类型(Retriever)变量(r)

7.download函数调用 打印Hello World

有趣之处

Get方法的具体实现 并不是去获取www.baidu.com的相关信息

而是直接打印Hello World

这就印证了:

1.只要实现接口的方法 就可以直接被接口类型调用

2.关注对象的能力 而非内部构造与具体实现

第三个例子

接口类型模拟队列

interface{}在Go语言中可以表示任何类型
类似于C++的模板编程 区别在于 这个Queue切片可以同时存不同的类型
缺点:只有在运行时才能知道是否发生错误

package main

import "fmt"

//interface{}在Go语言中可以表示任何类型
//类似于C++的模板编程 区别在于这个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
}

func main() {
	var q Queue
	q.Push("ABC")
	q.Push(999)
	q.Push(13.14)
	fmt.Println(q.Pop())
	fmt.Println(q.Pop())
	fmt.Println(q.Pop())
}

运行效果

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没伞的男孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值