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

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

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

基础

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

我认为 Go 是一门轻量级的面向对象编程语言。是的,它具有封装和类型成员函数,但它缺乏继承,因此缺乏传统的多态性。对我来说,除非想实现多态性,否则继承是无用的。通过在 Go 中实现接口的方式,不需要继承。Go采用 OOP 中最好的部分,而忽略了其他部分,为我们提供了一种编写多态代码的更好方法。

下面是 Go 中的 OOP 快速概览。先从这三个结构体开始。

type Animal struct {Name stringmean bool
}type Cat struct {Basics AnimalMeowStrength int
}type Dog struct {AnimalBarkStrength 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.BarkStrengthif dog.mean == true {barkStrength = barkStrength * 5}for bark := 0; bark < barkStrength; bark++ {fmt.Printf("BARK ")}fmt.Println("")
}func (cat *Cat) MakeNoise() {meowStrength := cat.MeowStrengthif 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 mainimport ("fmt"
)type Animal struct {Name stringmean bool
}type AnimalSounder interface {MakeNoise()
}type Dog struct {AnimalBarkStrength int
}type Cat struct {Basics AnimalMeowStrength int
}func main() {myDog := &Dog{Animal{"Rover", // Namefalse,   // 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 mainimport ("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 面向对象式编程

转载于:https://juejin.im/post/5d065cad51882523be6a92f2

Go 面向对象编程(译)相关推荐

  1. [译] 被遗忘的面向对象编程史(软件编写)(第十六部分)

    原文地址:The Forgotten History of OOP 原文作者:Eric Elliott 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:ge ...

  2. 《Python面向对象编程指南》——1.2 基类中的__init__()方法

    本节书摘来自异步社区<Python面向对象编程指南>一书中的第1章,第1.2节,作者[美]Steven F. Lott, 张心韬 兰亮 译,更多章节内容可以访问云栖社区"异步社区 ...

  3. 《易学Python》——第6章 类与面向对象编程 6.1 类是什么

    本节书摘来自异步社区<易学Python>一书中的第6章,第6.1节,作者[澳]Anthony Briggs,王威,袁国忠 译,更多章节内容可以访问云栖社区"异步社区"公 ...

  4. 《Python面向对象编程指南》——2.7 __del__()方法

    本节书摘来自异步社区<Python面向对象编程指南>一书中的第2章,第2.7节,作者[美]Steven F. Lott, 张心韬 兰亮 译,更多章节内容可以访问云栖社区"异步社区 ...

  5. python用类名直接调用方法_一文读全 Python 的面向对象编程方法

    背景介绍: Python 支持三种形式的编程,分别是:"命令式"."函数式"和"面向对象式". 很多优秀的开源 Python 项目里都用到了 ...

  6. 孙卫琴:我为什么要写《Java面向对象编程》

    孙卫琴:我为什么要写<Java面向对象编程> 特约作者:孙卫琴 策划 & 设计 & 制作:李大微 当<精通Struts>和<精通Hibernate> ...

  7. JavaSE:第5章 面向对象编程(中)

    文章目录 JavaSE:第5章 面向对象编程(中) 5.1 OOP特征二:继承性 5.2 方法的重写(override) 5.3 四种访问权限修饰符 5.4 关键字:super 5.5 子类对象实例化 ...

  8. 【面向对象编程】(4) 类的继承,重构父类中的方法

    各位同学好,今天和大家分享一下面向对象编程中,类的三大特征之继承.主要介绍:子类继承父类的基本方法:重写父类的类方法:重构父类的初始化方法:super() 方法.本节主要是单继承,多继承在下一节中介绍 ...

  9. 【面向对象编程】(3) 类之间的交互,依赖关系,关联关系

    各位同学好,今天和大家分享一下面向对象编程中,类之间的交互,类之间的依赖关系和关联关系.有不明白的可见前一章节:https://blog.csdn.net/dgvv4/article/details/ ...

  10. 【面向对象编程】(1) 类实例化的基本方法

    各位同学好,本章节和大家分享一下面向对象编程的一些方法,通过一些案例带大家由浅入深掌握面向对象的编程. 1. 最基本的类实例化 创建类的方法是 class 变量名: ,实例化方法是 类名() ,分配属 ...

最新文章

  1. 中科大计算机学院的保研率,中科大2021届保研率44.7%,少年班83.4%,物理计算机人数最多...
  2. 迅为I.MX6Q开发板配不同分辨率不同尺寸液晶屏幕
  3. 一般试卷的纸张大小是多少_pdf试卷怎么打印在A3纸上
  4. cmd c语言 图形,CMD-C彩图隐写方案
  5. MATLAB连接SQLServer和MySql数据库
  6. 面试题之GC垃圾回收算法
  7. 接口配置锦囊妙计之三----端口自协商
  8. 文件夹无法删除详细解决方案(Windows版)
  9. 这款智能手机比 iPhone 早13年,你猜多少钱?
  10. 不一样的设计!20个国外优秀的电子商务网站
  11. drools mysql_drools 介绍
  12. java之继承 封装 多态
  13. Docker直接删除elasticsearch报错:Failed to obtain node locks
  14. moveit缺少libfcl.so.0.6文件
  15. 问题 - GitLab repositories 文件夹权限异常
  16. 软件测试需要学什么课程?好学吗?
  17. Leetcode77 组合
  18. Python3 正则表达式
  19. 3秒测试:组建一个网络,需要几个硬件设备搞定?
  20. 模拟测静电场实验报告

热门文章

  1. LeetCode 209. 长度最小的子数组
  2. 数据库的主键和外键总结
  3. 虚拟化网络与云网关打通的解决方案
  4. 用牛顿插值多项式求函数近似值的算法,能用C语言编程实现
  5. php获取cpu编码,PHP下通过exec获得计算机的唯一标识[CPU,网卡 MAC地址]
  6. 白盒测试有哪些方法_软件测试中有哪些方法可以测试服务器稳定性
  7. 如何实现接口的幂等性?
  8. 使用Spring Secuirty Oauth2实现SSO单点登录
  9. android做题imageview缩放,巧用ViewPager实现驾考宝典做题翻页效果
  10. Linux的网卡由eth0变成了eth1,如何修复