Go 面向对象编程(译)

栏目: Go · 发布时间: 6年前

内容简介:今天接着给大家分享关于 Go 面向对象的好文。原文作者是 William Kennedy,《Go 语言实战》作者,博客有些与知识点无关的语句,翻译过来有点拗口,大家一眼带过。但与知识点相关的,都会尽量忠于原文。另外,文章做了简单的排版,方便阅读。翻译水平有限,有误的地方,请大家在下方留言指正。今天有人在论坛上问我一个问题,如何在不通过嵌入的情况下而获得继承的优点。重要的是,每个人都应该考虑 Go 语言,而不是他们留下的语言。我不能告诉你我从早期的 Go 实现中删除了多少代码,因为这是不必要的。语言设计师有多

『就要学习 Go 语言』系列 -- 第 26 篇分享好文

今天接着给大家分享关于 Go 面向对象的好文。原文作者是 William Kennedy,《Go 语言实战》作者,博客 www.ardanlabs.com/blog/ 的维护者。大部分中国的 Gopher 都是通过这个博客认识了这位 Go 大神。

有些与知识点无关的语句,翻译过来有点拗口,大家一眼带过。但与知识点相关的,都会尽量忠于原文。另外,文章做了简单的排版,方便阅读。翻译水平有限,有误的地方,请大家在下方留言指正。

基础

今天有人在论坛上问我一个问题,如何在不通过嵌入的情况下而获得继承的优点。重要的是,每个人都应该考虑 Go 语言,而不是他们留下的语言。我不能告诉你我从早期的 Go 实现中删除了多少代码,因为这是不必要的。语言设计师有多年的经验和知识,正在帮助创建一种快速、精简且编写代码非常有趣的语言。

我认为 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 的示例中,你都可能看到上面三个结构体。一个基础结构体 Animal 和基于 Animal 声明的结构体 Cat 和 Dog。结构体 Animal 拥有所有动物共同的属性。

除了成员 mean,其他所有成员都是公共的、可被外部访问的。结构体 Animal 的 mean 成员以小写字母开头。在 Go 中,变量、结构体、成员、函数等的第一个字母的大小写决定了访问权限。大写字母开头表示公共的,可供外部调用;小写字母开头表示私有的,外部不能调用。

由于 Go 里没有继承,所以组合是你唯一的选择。结构体 Cat 的一个成员是 Basics,类型是 Animal。而结构体 Dog 通过匿名的方式嵌入了结构体 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("")
}
复制代码

使用指针接收者实现各自的方法 MakeNoise()。这两个方法做同样的事情,如果 mean 为 true 话,每个动物会基于吠或喵的强度,用各自的母语说话。很无聊,但它展示了如何访问引用的对象。

我们可以使用 Dog 引用直接调用结构体 Animal 的成员,而 Cat 必须通过成员 Basics 访问到 Animal 的成员。

到目前为止,我们已经讨论了封装、组合、访问规范和成员函数,剩下的就是如何创建多态行为。

通过接口实现多态:

type AnimalSounder interface {
    MakeNoise()
}

func MakeSomeNoise(animalSounder AnimalSounder) {
    animalSounder.MakeNoise()
}
复制代码

上面的代码,我们添加一个接口 AnimalSounder 和一个公共函数 MakeSomeNoise(),该函数接受接口类型的值。实际上,该函数将引用实现此接口的类型的值。接口不是可以实例化的类型,是行为声明,其他类型可以去实现接口声明的行为。

在 Go 中, 通过方法实现接口的任何类型都表示接口类型 。在我们的例子中,结构体 Dog 和 Cat 都通过指针接收者实现了接口 AnimalSounder,所以它们都可以看成是 AnimalSounder 类型。

这意味着,Dog 和 Cat 的指针可以作为参数传递给函数 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")
}
复制代码

现在 Animal 类型有一个处理 noise 的方法,可以被其外部类型调用,例如 Dog、Cat 类型。还有一个好处,我们不需要将 mean 成员作为参数传递,因为它就属于 Animal 结构体。 下面是完整代码:

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()
        // PressHorn(hornSounder) 这种方式也可以
    }
}

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,并存储了 Car 类型变量和 Bike 变量的指针。这种赋值操作是可以的,因为 Car 和 Bike 都实现了接口。接着就是要一个简单的 loop 操作,循环调用 SoundHorn() 方法。

在你的应用程序中,任何你需要实现面向对象的东西在 Go 语言中都有。正如我之前所说的,Go 采用了 OOP 中最好的部分,省略了其他部分,为我们提供了编写多态代码的更好方法。

与主题相关联的几篇文章:

1. Methods, Interfaces and Embedded Types in Go

2. How Packages Work in Go

3. Singleton Design Pattern in Go

希望这几个简单的例子能对你的 Go 编程有帮助!

推荐阅读:

1. 教女朋友写方法(续)

2. Go 面向对象式编程


以上所述就是小编给大家介绍的《Go 面向对象编程(译)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Algorithmic Beauty of Plants

The Algorithmic Beauty of Plants

Przemyslaw Prusinkiewicz、Aristid Lindenmayer / Springer / 1996-4-18 / USD 99.00

Now available in an affordable softcover edition, this classic in Springer's acclaimed Virtual Laboratory series is the first comprehensive account of the computer simulation of plant development. 150......一起来看看 《The Algorithmic Beauty of Plants》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具