Go中的多态
go中的多态是通过接口实现的,在了解接口之前先引出方法这一概念。
在go中,方法区别于函数:函数通常指为了实现某些目的,打包的一系列有明确逻辑关系的语句;而方法则是一个需要绑定特殊类型的函数。这种“拼接”的方式,让方法有了类似其他面向对象语言中类方法的作用,即某种功能和目的的实现依赖于实例化后的对象。
有如下参考代码:
package main
import (
"fmt"
)
type Person struct {
Name string
Age int
}
func (P Person) Eat() {
fmt.Println(P.Name, "可以吃肉和菜")
}
func main() {
person := Person{Name: "李华"}
person.Eat() // 李华 可以吃肉和菜
}
可以看到方法的使用:在实例化一个Person对象之后,通过“对象.方法名”的形式,让“李华”这一对象执行了“Eat”方法,和C++、Python等语言中的类操作具有高度的相似性,go通过“方法”的这种形式,让自己拥有了面向对象的基础。
接下来讨论go中的多态。
假设,在实际场景中,除了Person类可以做出Eat的动作,Dog类和Cat类也可以做出Eat的动作,我们需要他们分别做出Eat的动作,来记录他们吃的东西。
因此有:
package main
import (
"fmt"
)
type Person struct {
Name string
Age int
}
func (P Person) Eat() {
fmt.Println(P.Name, "可以吃肉和菜")
}
type Dog struct {
Name string
Age int
}
func (D Dog) Eat() {
fmt.Println(D.Name, "可以吃骨头")
}
type Cat struct {
Name string
Age int
}
func (C Cat) Eat() {
fmt.Println(C.Name, "可以吃鱼")
}
func main() {
person := Person{Name: "李华"}
person.Eat() // 李华 可以吃肉和菜
dog := Dog{Name: "旺财"}
dog.Eat() // 旺财 可以吃骨头
cat := Cat{Name: "叮当"}
cat.Eat() // 叮当 可以吃鱼
}
可以看到这里实例化了三个对象,执行了三次“对象.方法”的操作。这里只存在Person、Dog、Cat三种类型,但是后续如果增加更多的类型,那么每种类型都需要经过”实例化、对象.方法”的步骤。
引入接口,接口的定义为:接口是一组方法的集合,定义对象的行为规范,只定义不实现,实现规范的细节由对象绑定的方法决定。
加入如下代码:
type Eater interface {
Eat()
} // 定义一个Eater接口,拥有Eat函数
func Eat_Some(E Eater ) {
E.Eat()
} // 定义一个以Eater接口为入参的函数,函数内调用了接口中的Eat函数
此时main函数变为
func main() {
Eat_Some(Person{Name: "李华"}) // 李华 可以吃肉和菜
Eat_Some(Dog{Name: "旺财"}) // 旺财 可以吃骨头
Eat_Some(Cat{Name: "叮当"}) // 叮当 可以吃鱼
}
我们的需求是:“需要他们分别做出Eat的动作,来记录他们吃的东西”,我们把他们的Eat——具有相同动作,却又有不同表现——抽象出来,只关注动作的实现,形成接口。这样通过Eat_Some函数,传入不同的类,执行同一动作,完成了不同的操作,从而实现了多态。
由图可以看出,抽象出来的接口位于动作实现层和对象实例化层的中间,为一个中间层,能够有效的解离定义和实现,减弱耦合。
最后附上源码:
package main
import (
"fmt"
)
type Eater interface {
Eat()
} // 定义一个接口
type Person struct {
Name string
Age int
}
func (P Person) Eat() {
fmt.Println(P.Name, "可以吃肉和草")
}
type Dog struct {
Name string
Age int
}
func (D Dog) Eat() {
fmt.Println(D.Name, "可以吃骨头")
}
type Cat struct {
Name string
Age int
}
func (C Cat) Eat() {
fmt.Println(C.Name, "可以吃鱼")
}
func Eat_Some(E Eater) {
E.Eat()
} // 定义一个以接口为入参的函数,函数内调用了接口中的方法
func main() {
Eat_Some(Person{Name: "李华"}) // 李华 可以吃肉和菜
Eat_Some(Dog{Name: "旺财"}) // 旺财 可以吃骨头
Eat_Some(Cat{Name: "叮当"}) // 叮当 可以吃鱼
}