Go 面向对象?封装、继承、多态?
系统学习并整理了下 Go 的面向对象的思想,分享下。
参考:
- https://mp.weixin.qq.com/s/r5gIVtyBWtD7UQncK_JPNQ
- 《Go 语言学习笔记》–作者:雨痕
Go 是一门面向对象的语言么?
是又不是。
- Go 支持面向对象编程,但却不是通过提供经典的类、对象以及类型层次来实现的。
- Go 同时支持面向过程的编程,可以直接调用进行函数调用,而不需要经过类的调用。
封装
封装就是把数据以及操作数据的方法“打包”到一个抽象数据类型中,这个类型封装隐藏了实现的细节,所有数据仅能通过导出的方法来访问和操作。
Go 的 Struct 可以起到抽象数据类型的效果,可以拥有属性和方法。而且 Go 提供 field 大小写来隐藏属性,外部不能访问。
注意:Go 的 Struct 在声明时可以通过大小写来标识包外部能否调用。
继承
组合大于继承
子类自动拥有了父类的属性和方法。
具有明显的层级关系,被继承的称为父类,继承的称为子类。子类对象实例可以赋值给父类变量。
Go 的匿名嵌套可以起到继承的效果,但 Go 的匿名嵌套却没有层级关系,两个 Struct 完全是不同的 Struct 类型,根本没有任何关系,当然也不能赋值。以经典 OO 理论话术去理解就是两个 Struct 的关系不是 is-a,而是 has-a 的关系。
其实这种继承更应该被称为组合。Go 更愿意将模块分成互相独立的小单元,分别处理不同方面的需求,最后以匿名嵌入的方式组合到一起,共同实现对外接口。也就是组合大于继承的思想。
组合没有父子依赖,不会破坏封装。且整体和局部松耦合,可任意增加来实现扩展。各单元持有单一职责,互不关联,自由灵活组合,实现和维护更加简单。
匿名嵌套
匿名嵌套在编译时会根据嵌套类型生成包装方法,包装方法实际是调用嵌套类型的原始方法。
![/images/Go 面向Go_对象?封装、继承、多态如何实现?/包装方法.png /images/Go 面向Go_对象?封装、继承、多态如何实现?/包装方法.png](/images/Go%20%e9%9d%a2%e5%90%91Go_%e5%af%b9%e8%b1%a1%ef%bc%9f%e5%b0%81%e8%a3%85%e3%80%81%e7%bb%a7%e6%89%bf%e3%80%81%e5%a4%9a%e6%80%81%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0%ef%bc%9f/%e5%8c%85%e8%a3%85%e6%96%b9%e6%b3%95.png)
TIP:图片来源 B 站幼麟实验室,主讲 Go 语言的各种底层实现,特别推荐!
类似与继承的“重写”
匿名嵌套有同名遮蔽问题,编译器编译时会自动选择深度最浅的作为 Struct 方法集中的方法,类似与继承的重写。
|
|
多态
Go 通过接口来实现多态。
Go的接口类型本质就是一组方法集合(行为集合),一个类型如果实现了某个接口类型中的所有方法,那么就可以作为动态类型赋值给接口类型(注意:定义一个接口变量,该变量本质是个 Struct 类型的变量哦)。
Go 的接口是特别重要的东西,通过学习 Go 接口的底层实现可以学到很多东西,例如动态语言的实现,方法动态派发实现等。这块下次再聊把。
拓:匿名嵌套的多种玩法
- struct 匿名嵌套 struct(上面已经展示过了)
- interface 匿名嵌套 interface
- struct 匿名嵌套 interface
interface 匿名嵌套 interface
接口可嵌入其他匿名接口,相当于将其声明的方法集导入。
当然,注意只有实现了两个接口的全部的方法,才算实现大接口哈。
Go 标准库中经典用法如下:
|
|
struct 匿名嵌套 interface
编译器自动为 struct 的方法集加上 interface 的所有方法。
(后面是我猜的)如我们通过 struct.M() 调用 interface 的 M 方法,编译器实际为 struct 生成包装方法 struct.interface.M()
。
注意:struct 中的 interface 要记得赋值哈,不然调用时会显示 interface nil panic。
|
|
我们同时验证以下匿名嵌套的同名覆盖问题:
|
|
Go 标准库中经典用法如下:
context 包中:
|
|