接口像是一个公司里面的领导,他会定义一些通用规范,只设计规范,而不实现规范。
go语言的接口,是一种新的类型定义,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
语法格式和方法非常类似。
接口的语法格式
/* 定义接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
/* 定义结构体 */
type struct_name struct {
/* variables */
}
/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
/* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/* 方法实现*/
}
在接口定义中定义,若干个空方法。这些方法都具有通用性。
接口实例
下面我定义一个USB接口,有读read和写write两个方法,再定义一个电脑Computer和一个手机Mobile来实现这个接口。
USB接口
type USB interface {
read()
write()
}
Computer结构体
type Computer struct {
}
Mobile结构体
type Mobile struct {
}
Computer实现USB接口方法
func (c Computer) read() {
fmt.Println("computer read...")
}
func (c Computer) write() {
fmt.Println("computer write...")
}
Mobile实现USB接口方法
func (c Mobile) read() {
fmt.Println("mobile read...")
}
func (c Mobile) write() {
fmt.Println("mobile write...")
}
测试
func main() {
c := Computer{}
m := Mobile{}
c.read()
c.write()
m.read()
m.write()
}
运行结果
func main() {
c := Computer{}
m := Mobile{}
c.read()
c.write()
m.read()
m.write()
}
实现接口必须实现接口中的所有方法
下面我们定义一个OpenClose接口,里面有两个方法open和close,定义个Door结构体,实现其中一个方法。
package main
import "fmt"
type OpenClose interface {
open()
close()
}
type Door struct {
}
func (d Door) open() {
fmt.Println("open door...")
}
func main() {
var oc OpenClose
oc = Door{} // 这里编译错误,提示只实现了一个接口
}
golang接口值类型接收者和指针类型接收者
这个话题,本质上和方法的值类型接收者和指针类型接收者,的思考方法是一样的,值接收者是一个拷贝,是一个副本,而指针接收者,传递的是指针。
实例演示
定义一个Pet接口
type Pet interface {
eat()
}
定义一个Dog结构体
type Dog struct {
name string
}
实现Pet接口(接收者是值类型)
func (dog Dog) eat() {
fmt.Printf("dog: %p\n", &dog)
fmt.Println("dog eat..")
dog.name = "黑黑"
}
测试
func main() {
dog := Dog{name: "花花"}
fmt.Printf("dog: %p\n", &dog)
dog.eat()
fmt.Printf("dog: %v\n", dog)
}
运行结果
dog: 0xc000046240
dog: 0xc000046250
dog eat..
dog: {花花}
从运行结果,我们看出dog的地址变了,说明是复制了一份,dog的name没有变说明,外面的dog变量没有被改变。
将Pet接口改为指针接收者
func (dog *Dog) eat() {
fmt.Printf("dog: %p\n", dog)
fmt.Println("dog eat..")
dog.name = "黑黑"
}
再测试
func main() {
dog := &Dog{name: "花花"}
fmt.Printf("dog: %p\n", dog)
dog.eat()
fmt.Printf("dog: %v\n", dog)
}
运行结果
dog: 0xc00008c230
dog: 0xc00008c230
dog eat..
dog: &{黑黑}
golang接口和类型的关系
- 一个类型可以实现多个接口
- 多个类型可以实现同一个接口(多态)
一个类型实现多个接口
一个类型实现多个接口,例如:有一个Player接口可以播放音乐,有一个Video接口可以播放视频,一个手机Mobile实现这两个接口,既可以播放音乐,又可以播放视频。
定义一个Player接口
type Player interface {
playMusic()
}
定义一个Video接口
type Video interface {
playVideo()
}
定义Mobile结构体
type Mobile struct {
}
实现两个接口
func (m Mobile) playMusic() {
fmt.Println("播放音乐")
}
func (m Mobile) playVideo() {
fmt.Println("播放视频")
}
测试
func main() {
m := Mobile{}
m.playMusic()
m.playVideo()
}
运行结果
播放音乐
播放视频
多个类型实现同一个接口
比如,一个宠物接口Pet,猫类型Cat和狗类型Dog都可以实现该接口,都可以把猫和狗当宠物类型对待,这在其他语言中叫做多态。
定义一个Pet接口
type Pet interface {
eat()
}
定义一个Dog结构体
type Dog struct {
}
定义一个Cat结构体
type Cat struct {
}
实现接口
func (cat Cat) eat() {
fmt.Println("cat eat...")
}
func (dog Dog) eat() {
fmt.Println("dog eat...")
}
测试
func main() {
var p Pet
p = Cat{}
p.eat()
p = Dog{}
p.eat()
}
运行结果
cat eat...
dog eat...
golang接口嵌套
接口可以通过嵌套,创建新的接口。例如:飞鱼,既可以飞,又可以游泳。我们创建一个飞Fly接口,创建一个游泳接口Swim,飞鱼接口有这两个接口组成。
飞Flyer接口
type Flyer interface {
fly()
}
创建Swimmer接口
type Swimmer interface {
swim()
}
组合一个接口FlyFish
type FlyFish interface {
Flyer
Swimmer
}
创建一个结构体Fish
type Fish struct {
}
实现这个组合接口
func (fish Fish) fly() {
fmt.Println("fly...")
}
func (fish Fish) swim() {
fmt.Println("swim...")
}
测试
func main() {
var ff FlyFish
ff = Fish{}
ff.fly()
ff.swim()
}
运行结果
fly...
swim...
golang 通过接口实现OCP设计原则
而面向对象的可复用设计的第一块基石,便是所谓的”开-闭“原则(Open-Closed Principle,常缩写为OCP)。虽然,go不是面向对象语言,但是也可以模拟实现这个原则。对扩展是开放的,对修改是关闭的。
OCP设计原则实例
下面通过一个人养宠物的例子,来解释OCP设计原则。
定义一个宠物接口Pet
type Pet interface {
eat()
sleep()
}
该接口有吃和睡两个方法。
定义个Dog结构体
type Dog struct {
name string
age int
}
Dog实现接口方法
func (dog Dog) eat() {
fmt.Println("dog eat...")
}
func (dog Dog) sleep() {
fmt.Println("dog sleep...")
}
定义一个Cat结构体
type Cat struct {
name string
age int
}
Cat实现接口方法
func (cat Cat) eat() {
fmt.Println("cat eat...")
}
func (cat Cat) sleep() {
fmt.Println("cat sleep...")
}
定义个Person结构体
type Person struct {
name string
}
为Person添加一个养宠物方法
func (per Person) care(pet Pet) {
pet.eat()
pet.sleep()
}
最后测试一下
func main() {
dog := Dog{}
cat := Cat{}
per := Person{}
per.care(dog)
per.care(cat)
}
运行结果
dog eat...
dog sleep...
cat eat...
cat sleep...
使用接口的这种设计方法,可以很好的解耦合代码,实现软件设计的OCP原则(即开闭原则)
这样设计,如果再添加一个宠物,例如:一个鸟Bird
,原有的代码不用修改,直接添加就可以。