Go语言中的面向对象编程
本文为技术翻译,原文出自:《Object Oriented Programming in Go》 。
今天有人在论坛上提出了一个问题,即如何在不嵌入的情况下获得继承的好处。对于 每个团队中的人来说,考虑Go的应用而不是他们留下的代码这是非常重要的。 我无法告诉你我在早期Go项目实现中删除的那些代码,因为它没有必要。 Go语言设计者们拥有多年的经验和知识。 Hindsight正在帮助创建一种快速,精简且非常有趣的语言。
我认为Go是一种轻量级的面向对象编程语言。 虽然它确实有封装和类型成员函数,但它缺乏继承,因此缺乏传统的多态性。 对我来说,除非你想实现多态,否则继承是无用的。 通过在Go中实现接口的方式,而不需要继承。 Go很好的采用了OOP设计模式,省略了其余的部分并为我们提供了编写多态代码的更好方法。
以下是Go中OOP的快速视图。 从这三个结构开始:
type Animal struct {
Name string
mean bool
}
type Cat struct {
Basics Animal
MeowStrength int
}
type Dog struct {
Animal
BarkStrength int
}
以上是你可能在任何OOP模式的Go代码中看到的三个结构。 我们定义了一个基础结构体和两个特定于基础结构体的结构体。Animal结构体包含所有Animals共享的属性,另外两个结构体则特定于Cat和Dog。
除了mean之外,所有成员属性(字段)都是公共的。 Animal结构中的mean字段以小写字母开头。 在Go中,变量,结构,字段,函数等的第一个字母的大小写确定了其可访问的规范。 使用大写字母,它是公开的(public),使用小写字母,它是私人的(private)。
http://www.goinggo.net/2014/03/exportedunexported-identifiers-in-go.html
由于Go中没有继承,因此组合是你唯一的选择。 Cat结构有一个名为Basics的字段,其类型为Animal。 Dog结构使用未命名的struct(嵌入)作为Animal类型。 由你来决定哪个更适合。
要为Cat和Dog创建成员函数(方法),语法如下:
func (dog *Dog) MakeNoise() {
barkStrength := dog.BarkStrength
if dog.mean == true {
barkStrength = barkStrength * 5
}
for bark := 0; bark < barkStrength; bark++ {
fmt.Printf("BARK ")
}
fmt.Println("")
}
func (cat *Cat) MakeNoise() {
meowStrength := cat.MeowStrength
if cat.Basics.mean == true {
meowStrength = meowStrength * 5
}
for meow := 0; meow < meowStrength; meow++ {
fmt.Printf("MEOW ")
}
fmt.Println("")
}
在方法名称之前,我们指定了一个接收器,它是指向每种类型的指针。 现在Cat和Dog都可以调用MakeNoise方法。
它们的MakeNoise方法都做同样的事情。 每个Animal都会根据它们的Cat或Dog的MakeNoise方法打印一些字符,如果mean为true的话。它告诉我们如何访问引用的对象(值)。
使用Dog参考时,我们直接访问Animal字段。 使用Cat参考,我们使用名为Basics的命名字段。
现在我们已经介绍了封装,组合,访问规范和成员函数, 剩下的就是如何创建多态行为。
我们使用接口来创建多态行为:
type AnimalSounder interface {
MakeNoise()
}
func MakeSomeNoise(animalSounder AnimalSounder) {
animalSounder.MakeNoise()
}
上面我们添加了一个接口和一个公共函数,它接受一个接口类型的值。 实际上,该函数将引用实现此接口的类型的值。 接口不是可以实例化的类型。 接口是由其他类型实现的行为声明。
当接口只包含一个方法时,Go中约定通常使用“er”后缀命名接口。
在Go中,通过方法实现接口的任何类型都表示接口类型。 在我们的例子中,Cat和Dog都使用指针接收器实现了AnimalSounder接口,因此被认为是AnimalSounder类型。
这意味着Cat和Dog的指针都可以作为参数传递给MakeSomeNoise函数。 MakeSomeNoise函数通过AnimalSounder接口实现多态行为。
如果你想减少Cat和Dog的MakeNoise方法中的代码重复,可以为Animal类型创建一个方法来处理它:
func (animal *Animal) PerformNoise(strength int, sound string) {
if animal.mean == true {
strength = strength * 5
}
for voice := 0; voice < strength; voice++ {
fmt.Printf("%s ", sound)
}
fmt.Println("")
}
func (dog *Dog) MakeNoise() {
dog.PerformNoise(dog.BarkStrength, "BARK")
}
func (cat *Cat) MakeNoise() {
cat.Basics.PerformNoise(cat.MeowStrength, "MEOW")
}
package main
import (
"fmt"
)
type Animal struct {
Name string
mean bool
}
type AnimalSounder interface {
MakeNoise()
}
type Dog struct {
Animal
BarkStrength int
}
type Cat struct {
Basics Animal
MeowStrength int
}
func main() {
myDog := &Dog{
Animal{
"Rover", // Name
false, // mean
},
2, // BarkStrength
}
myCat := &Cat{
Basics: Animal{
Name: "Julius",
mean: true,
},
MeowStrength: 3,
}
MakeSomeNoise(myDog)
MakeSomeNoise(myCat)
}
func (animal *Animal) PerformNoise(strength int, sound string) {
if animal.mean == true {
strength = strength * 5
}
for voice := 0; voice < strength; voice++ {
fmt.Printf("%s ", sound)
}
fmt.Println("")
}
func (dog *Dog) MakeNoise() {
dog.PerformNoise(dog.BarkStrength, "BARK")
}
func (cat *Cat) MakeNoise() {
cat.Basics.PerformNoise(cat.MeowStrength, "MEOW")
}
func MakeSomeNoise(animalSounder AnimalSounder) {
animalSounder.MakeNoise()
}
输入如下:
BARK BARK MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW
有人在留言上发布了一个关于在结构中嵌入接口的示例:
package main
import (
"fmt"
)
type HornSounder interface {
SoundHorn()
}
type Vehicle struct {
List [2]HornSounder
}
type Car struct {
Sound string
}
type Bike struct {
Sound string
}
func main() {
vehicle := new(Vehicle)
vehicle.List[0] = &Car{"BEEP"}
vehicle.List[1] = &Bike{"RING"}
for _, hornSounder := range vehicle.List {
hornSounder.SoundHorn()
}
}
func (car *Car) SoundHorn() {
fmt.Println(car.Sound)
}
func (bike *Bike) SoundHorn() {
fmt.Println(bike.Sound)
}
func PressHorn(hornSounder HornSounder) {
hornSounder.SoundHorn()
}
在这个示例中,Vehicle结构体维护了一个实现HornSounder接口的列表。 在main函数中创建了一个Vehicle实例,并为其成员变量List初始化了Car和Bike实例。 这种赋值是允许的,因为Car和Bike存在Sound。 然后使用一个简单的循环,使用hornSounder接口来SoundHorn。
如需学习更多相关知识,可以参考下面链接:
http://www.goinggo.net/2014/05/methods-interfaces-and-embedded-types.html
http://www.goinggo.net/2013/07/how-packages-work-in-go-language.html
http://www.goinggo.net/2013/07/singleton-design-pattern-in-go.html
