接口
前言
go语言中的接口和java中的接口地位不同,而跟python中的协议类似。实际上,go语言的接口就是参考python的鸭子类型和java的接口。
python鸭子类型
鸭子类型:当看到一只鸟走过来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
以可迭代对象为例,只要类里面实现了名为__iter__()
的方法,其对象就是可迭代对象。
from typing import Iterable
# 并没有继承typing.Iterable
class Tenno():
def __init__(self, warframe_list):
self.warframe_list = warframe_list
# 实现了Iterable的__iter__方法
def __iter__(self):
return iter(self.warframe_list)
wl = Tenno(["saryn","volt","mesa","gauss"])
print(type(wl))
if isinstance(wl, Iterable):
print("iterable")
for i in wl:
print(i)
else:
print("not iterable")
# 迭代非Iterable类型会报错,提示非Iterable类型
# for i in wl:
# print(i)
入门
- go语言中,接口更像python中的协议。
- 接口是一种类型,是一种抽象类型。
- 接口的命名一般以
er
结尾。 - 通过接口可以实现面向对象三大特性之一的多态(多种实现方式)
- 在Go语言中,要实现一个接口,需要满足以下两个条件:
- 类型中添加的方法签名和接口签名完全一致,包括名称、参数列表、返回参数等。
- 接口的所有方法均被实现。
// 定义一个程序员接口,1. 可以编写代码,返回字符串。2. 可以debug,返回字符串
type Programmer interface {
Coding() string // 方法只是声明
Debug() string
}
// go程序员结构体
type Gopher struct {
}
func (g Gopher) Coding() string {
return "gopher在编程"
}
func (g Gopher) Debug() string {
return "gopher在debug"
}
// python程序员结构体
type Pythoner struct {
}
func (p Pythoner) Coding() string {
return "pythoner在编程"
}
func (p Pythoner) Debug() string {
return "pythoner在debug"
}
func main() {
var coder Programmer = Gopher{}
fmt.Println(coder.Coding())
fmt.Println(coder.Debug())
var coders []Programmer
coders = append(coders, Gopher{})
coders = append(coders, Pythoner{})
}
接口组合实现继承
类似于结构体组合,接口也可以通过组合的形式实现继承。
type DBA interface {
Sql() string
}
type Programmer interface {
Coding() string
Debug() string
}
type JiaGouShi interface {
DBA // 组合DBA的接口
Programmer // 组合Programmer的接口
Design() string // 再加一个方法
}
在上述示例中,如果要实现JiaGouShi的接口,结构体必须同时实现Sql() string
、Coding() string
、Debug() string
和Design() string
方法。
在Go 1.14之前,嵌套的接口之间不能有重名的方法。在Go 1.14之后就取消了这个约束。
error接口
go语言的error
类型实际上是一个接口,如果有需要也可以自定义error结构体。
type error interface {
Error() string
}
空接口
空接口是没有定义任何方法的接口,例如:
// i 是空接口变量
var i interface{}
- 可以把任何类型都赋值给空接口变量
i = 10
i = "gopher"
type Warframe struct {
Name string
HP int
Defence int
}
i = Warframe{}
- 空接口也可以用于函数任意传参
func print(i interface{}) {
fmt.Printf("%v\n",i)
}
var i interface{}
i = 10
print(i)
i = "gopher"
print(i)
- 字典的值类型可以是空接口
var teacherInfo = make(map[string]interface{})
teacherInfo["name"] = "Lotus"
teacherInfo["age"] = 28
teacherInfo["weight"] = 56.7
类型断言
func print(i interface{}) {
fmt.Printf("%v\n",i)
switch v := x.(type) {
case string:
fmt.Printf("%s is string\n",v)
case int:
fmt.Printf("%d is int\n",v)
}
}
var i interface{}
i = 10
print(i) // 10 is int
i = "gopher"
print(i) // gopher is string
接口实现多态
接口的多种不同实现方式即为多态。多态可以解决模块之间高耦合的问 题。模块之间尽量使用接口进行交互,这样对程序的可拓展性非常有益。
示例代码:
// 接口:goModTest/entity/iduck.go
// 方法签名为Sleep() Eat() SingGua() Type()
package entity
type IDuck interface {
Sleep()
Eat()
SingGua()
Type() string
}
// 结构体: goModTest/entity/duck.go
// 实现了IDuck接口签名的方法
package entity
import (
"fmt"
)
type Duck struct {
Color string
Age int
}
func (this *Duck) Sleep() {
fmt.Println("Duck sleep")
}
func (this *Duck) Eat() {
fmt.Println("Duck eat")
}
func (this *Duck) SingGua() {
fmt.Println("Duck singgua")
}
func (this *Duck) Type() string {
return "Duck"
}
// 结构体:goModTest/entity/goose.go
// 实现了IDuck接口签名的方法
package entity
import (
"fmt"
)
type Goose struct {
Color string
}
func (this *Goose) Sleep() {
fmt.Println("Goose sleep")
}
func (this *Goose) Eat() {
fmt.Println("Goose eat")
}
func (this *Goose) SingGua() {
fmt.Println("Goose singgua")
}
func (this *Goose) Type() string {
return "Goose, like duck"
}
// 工厂模式函数:goModTest/entity/factory.go
// 根据传入的名称来动态返回不同的类型。
// 由于Duck和Goose都实现了IDuck接口,因此对于不同的外部输入,factory函数有不同的响应
package entity
func Factory(name string) IDuck {
switch name {
case "duck":
return &Duck{Color: "White", Age: 3}
case "goose":
return &Goose{Color: "Black"}
default:
panic("No such animal")
}
}
// 主函数: goModTest/main.go
package main
import (
"fmt"
"goModTest/entity"
)
func main() {
animal := entity.Factory("duck")
animal.SingGua() // Duck singgua
fmt.Printf("animal is %s \n",animal.Type()) // animal is Duck
animal = entity.Factory("goose")
animal.SingGua() // Goose singgua
fmt.Printf("animal is %s \n",animal.Type()) // animal is Goose, like duck
}
示例1:对复杂类型进行排序
go语言标准库提供sort
模块(sort.Sort()
),可以对一些简单类型排序。但其实该模块为一个接口,通过实现该接口,便可以对复杂类型进行排序。
package main
import (
"fmt"
"sort"
)
type Course struct {
Name string
Price int
Url string
}
type Courses []Course
func (c Courses) Len() int {
return len(c)
}
func (c Courses) Less(i,j int) bool {
return c[i].Price < c[j].Price
}
func (c Courses) Swap(i,j int) {
c[i], c[j] = c[j], c[i]
}
func main() {
c := Courses{
Course{Name: "python", Price: 200, Url: "123"},
Course{Name: "linux", Price: 100, Url: "456"},
Course{Name: "go", Price: 300, Url: "789"},
}
// 排序
sort.Sort(c)
for _,v := range(c){
fmt.Printf("%v\n",v)
}
}
示例2: 面向接口编程
下面这段代码定义了一个Bird
接口,Canary
和Crow
都实现了Bird
接口
package main
import "fmt"
// 定义鸟类
type Bird interface {
Fly()
Type() string
}
type Canary struct {
Name string
}
func (c *Canary) Fly() {
fmt.Printf("我是 %s, 在飞\n", c.Name)
}
func (c *Canary) Type() string {
return c.Name
}
type Crow struct {
Name string
}
func (c *Crow) Fly() {
fmt.Printf("我是 %s, 在飞\n", c.Name)
}
func (c *Crow) Type() string {
return c.Name
}
func LetItFly(bird Bird) {
fmt.Printf("Let %s Fly\n", bird.Type())
bird.Fly()
}
func main() {
LetItFly(&Canary{"金丝雀"})
LetItFly(&Crow{"乌鸦"})
}