介绍
接口类型是一种抽象类型,包含方法名,参数,返回值的没有具体实现的方法集合.接口只定义行为,而怎么去实现行为就取决于对象.例如定义一个动物接口包含说的方法,再创建小动物去实现这个方法发出不同的叫声.
定义接口
通过type 接口名 interface{}
的方式定义接口.如下
// 定义视频接口
type videoer interface {
// 定义方法名的接受值 与 返回值
Play() (code int, msg string)
Closs() (code int, msg string)
}
实现接口
只要实现了接口内的方法,就叫是实现了接口.如下play和closs方法都实现了接口内的方法.
// 定义视频结构体
type Video struct {
v_id,v_number int
v_name string
}
// 定义视频方法实现播放方法
func (v *Video) Play() (code int,msg string){
v.v_number += 1
return 0,"ok"
}
// 定义视频方法实现关闭方法
func (v *Video) Closs() (code int,msg string){
return 0,"ok"
}
func main() {
video := Video{}
//_,msg :=video.Play()
// 因为在方法中是引用结构体,所以这里使用的时候要带上&
var inter videoer = &video
_, msg := inter.Play()
fmt.Print(msg)
}
在实现接口例子中可以看到调用play
方法是通过实例化结构体后通过接受结构体的变量调用的
video := Video{}
_,msg :=video.Play()
而使用了接口后,定义变量为videoer
接口类型后,将接口体传入到变量中,通过接口调用的方法的结果是一样的.
video := Video{}
var inter videoer = &video
_, msg := inter.Play()
理解接口
接口是一个比较抽象的东西,为了方便理解,从0开始运用我的高中物理知识进行讲解.首先拿开小黄车举例,求小黄车从开始到结束总共运行的距离.
阶段:
- 启动:匀加速运动:0(米/秒) 加速到 n(米/秒)
- 匀速:匀速运动 n(米/秒)
- 刹车:匀减速运动 n(米/秒) 减速到 0(米/秒)
func main(){
// 匀加速运动
add_x := a * t * t / 2
// 匀速运动
constant_x := v * t
// 匀减速运动
reduce_x := (v * t) + (a * t * t / 2)
// 总位移
x := add_x + constant_x + reduce_x
fmt.Printf("一共运行了 %g 米\n",x)
}
由于需要用计算三个阶段位移,都挤在mai方法中不太方便阅读与维护,所以将计算位移的逻辑定义在不同函数中.如下
func Getx(v,t float64, c_type int) float64{
var x float64
a := v / t * float64(c_type)
switch {
case c_type == 0: // 匀速
x = v * t
case c_type > 0: // 匀加速
x = a * t * t / 2
case c_type < 0: // 匀减速
x = (v * t) + (a * t * t / 2)
}
return x
}
func main(){
car_add := Getx(10,4,1)
car_uni := Getx(10,2,0)
car_les := Getx(10,5,-1)
x := car_add + car_uni + car_les
fmt.Print(x)
}
现在main
函数就只需要执行一行代码,看起来清爽很多.但是在main
中必须要调用阶段的方法才行.耦合性很高.接下来降低接口的耦合,先将获取位移函数改为方法,再创建函数获取方法名
,main
通过调用新的方法来获取位移.内容如下:
// 定义匀变速结构体
type Uvs struct {}
// 获取位移方法 v最大速度,t运动时长,c_type:-1.匀减速;0.匀速;1.匀加速
func (Uvs) Getx(v, t float64, c_type int) float64 {
var x float64
a := v / t * float64(c_type)
switch {
case c_type == 0: // 匀速
x = v * t
case c_type > 0: // 匀加速
x = a * t * t / 2
case c_type < 0: // 匀减速
x = (v * t) + (a * t * t / 2)
}
return x
}
// 返回Uvs载体
func getName() Uvs{
return Uvs{}
}
func main(){
// car := Uvs{}
car := getName()
car_add := car.Getx(10,4,1)
car_uni := car.Getx(10,2,0)
car_les := car.Getx(10,5,-1)
x := car_add + car_uni + car_les
fmt.Print(x)
}
接口引入
:假设我们在添加一个不动王八方法.在获取构造体的时候就需要改两处地方.如下
// 定义不动王八载体和方法
type Kingeight struct {}
func (Kingeight) Getx(v, t float64, c_type int) float64{
return 0.0
}
// 省略Uvs构造体和方法
// 返回类型改为了不动王八
func getName() Kingeight{
// 返回不动王八
return Kingeight{}
}
非常麻烦,既然都有Getx
方法.那定义一个接口并定义Getx
方法.在getName() 接口名
函数中就可以将返回类型改为接口名,return 结构体名{}
.如下:
type myInter interface {
Getx(v, t float64, c_type int) float64
}
func getName() myInter{
// 返回王八体
// return Kingeight{}
// 返回匀变速运动体
return Uvs{}
}
完整代码如下:
package main
import "fmt"
// 定义不动王八载体和方法
type Kingeight struct{}
func (Kingeight) Getx(v, t float64, c_type int) float64 {
return 0.0
}
// 定义匀变速结构体
type Uvs struct{}
// 获取位移方法 v最大速度,t运动时长,c_type:-1.匀减速;0.匀速;1.匀加速
func (Uvs) Getx(v, t float64, c_type int) float64 {
var x float64
a := v / t * float64(c_type)
switch {
case c_type == 0: // 匀速
x = v * t
case c_type > 0: // 匀加速
x = a * t * t / 2
case c_type < 0: // 匀减速
x = (v * t) + (a * t * t / 2)
}
return x
}
type myInter interface {
Getx(v, t float64, c_type int) float64
}
func getName() myInter {
// 返回王八体
// return Kingeight{}
// 返回匀变速运动体
return Uvs{}
}
func Countx(xs []float64) float64 {
var x float64
for _, v := range xs {
x += v
}
return x
}
func main() {
cName := getName()
//inter := car.Getx()
var inter myInter = cName
car_add := inter.Getx(10, 4, 1)
car_uni := inter.Getx(10, 5, 0)
car_les := inter.Getx(10, 3, -0)
xs := []float64{car_add, car_uni, car_les}
x := Countx(xs)
fmt.Printf("共运行:%g 米", x)
}
组合接口
接口可以包含一个或多个其他的接口,只要外部接口所有方法都被实现,就可以使用这个借口中所有嵌套的组合.
声明读写接口
type IWriter interface {
// 传入写文件的名字以及内容
write(filename, content string) string
}
type IReader interface {
// 传入要读取文件的内容
read(filename string) string
}
实现接口
// 定义结构体
type qvbilam struct{}
// 实现读接口
func (qvb qvbilam) read(filename string) string {
content, rErr := ioutil.ReadFile(filename)
if rErr != nil {
fmt.Println(rErr)
return "读取文件错误"
}
return string(content)
}
// 实现写接口
func (qvb qvbilam) write(filename, content string) string {
// 创建文件
file, err := os.Create(filename)
if err != nil {
fmt.Println(err)
return "创建文件出错"
}
// 打开文件
_, writeErr := io.WriteString(file, content)
if writeErr != nil {
fmt.Println(writeErr)
return "写入文件出错"
}
return "写入文件成功"
}
使用接口
通过IReadeer
接口调用读的方法读取文件内容.定义变量接受读IReader
接口,值为结构体qvbilam{}
.
func main() {
// 实例化结构
qstr := qvbilam{}
// 定义读接口的变量.结构体为qvbilam
var qint IReader = qstr
// 调用读接口
res :=qint.read("basic/1.txt")
fmt.Println(res)
}
因为读接口只有读的放法.所以要想再调用写的话还要定义一个IWriter
才可调用写的方法.非常不方便.如下所示:
func main() {
// 实例化结构
qstr := qvbilam{}
// 定义读接口的变量.结构体为qvbilam
var qint IReader = qstr
// 调用读接口
res :=qint.read("basic/1.txt")
fmt.Println("读取文件内容为:",res)
// 写文件
var qint1 IWriter = qstr
res1 := qint1.write("basic/2.txt","欢迎光临")
fmt.Println("执行结果:",res1)
}
引进组合接口
定义一个新的接口,新接口内包含接口IWriter
和IReader
.这样通过定义组合接口就能即使用读方法又能使用写的方法
type IAdminer interface {
// 包含两个接口
IWriter
IReader
}
同时读写文件修改为如下,即可通过组合接口调用IWriter
和IReader
接口下的实现方法.功能读取2.txt内容并写入到新文件3.txt中.
func main() {
// 实例化结构
qstr := qvbilam{}
// 定义读接口的变量.结构体为qvbilam
var qint IAdminer = qstr
// 进行读
res := qint.read("basic/2.txt")
// 将读的内容写入
result := qint.write("basic/3.txt",res)
fmt.Println(result)
}
完整代码
package main
import (
"fmt"
"io"
"io/ioutil"
"os"
)
type IWriter interface {
// 传入写文件的名字以及内容
write(filename, content string) string
}
type IReader interface {
// 传入要读取文件的内容
read(filename string) string
}
type IAdminer interface {
// 包含两个接口
IWriter
IReader
}
type qvbilam struct{}
func (qvb qvbilam) read(filename string) string {
content, rErr := ioutil.ReadFile(filename)
if rErr != nil {
fmt.Println(rErr)
return "读取文件错误"
}
return string(content)
}
func (qvb qvbilam) write(filename, content string) string {
// 创建文件
file, err := os.Create(filename)
if err != nil {
fmt.Println(err)
return "创建文件出错"
}
// 打开文件
_, writeErr := io.WriteString(file, content)
if writeErr != nil {
fmt.Println(writeErr)
return "写入文件出错"
}
return "写入文件成功"
}
func main() {
// 实例化结构
qstr := qvbilam{}
// 定义读接口的变量.结构体为qvbilam
var qint IAdminer = qstr
// 进行读
res := qint.read("basic/2.txt")
// 将读的内容写入
result := qint.write("basic/3.txt",res)
fmt.Println(result)
}
太牛了大哥