陈哈哈的博客

Go语言重点笔记-结构体Struct和类Class的继承与组合

2018-05-22

阅读本文需要具备

  • 简历级精通Java面向对象知识,熟悉继承、封装、多态等概念;
  • 了解Golang组合与继承;

本文介绍和说明Java与Golang两种语言在实现面向对象编程中,是如何实现“继承”和“组合”的概念以及它们之间的差异。

一、引子 - “继承”

在任何语言中,无论是继承概念还是组合概念,都是为了解决代码重用的问题。

在继承概念中,以面向对象语言为例,它包含了两种情况:

  1. 在概念上存在着“猫 IS A (一种)动物”的继承层级关系;
  2. 在表现和结构上存在注册用户 Has A 名字Http插件 HAS A 名称这种重复的但看似无关联的关系;

1.1 正统教义的继承 - 派生

派生的概念和用意,应该是所有面向对象语言中都属于正统教义的概念,也是最恰当的继承用法。
这个没什么好讲的,都是教科书那一套Animals派生出HumansCatsAndDogs这种套路。

1.2 代码和结构层面 - 重用

Java 代码演示

这里以Java为例子,展示两个类不同用意的Class,不具备上下级继承关系,但具有重复的代码和结构的场景:

两个毫不相干的类,它们只是恰巧具备了相同的“设置和读取名字”功能。上述例子可能过于简单,有人可能觉得只是重用一个Name,没必要过度设计出一个AbstractNamed。我们可以把它们的真实项目的业务逻辑想像得更复杂一点:

  1. 两个类型都有GetName() StringSetName(String)的方法;
  2. 它们对Name变量的值都有相同的,复杂的校验逻辑;

那么:

这种“抽取公用代码”写成一个抽像类的用法,你认为合理吗?

Golang 代码演示

相同功能的代码,在Go语言中的“继承”:

Go语言中并没有面向对象语言所指的真正意义的继承,Go语言的继承,只是看起来像继承而已,本质上它是组合

问题来了

  1. 这种通过继承一个“抽取公共代码和结构”的BaseClass来实现代码重用的方式,你觉得合理吗?

二、Go语言系统中的假继承真组合

上述的Java代码只是个引子,Go代码来是本文的主角。我们来看Go例子。

2.1 “看起来像继承” - 假继承真组合

如上代码演示所说,Go语言提供的继承技术,采用了组合的文法,但本质上它是个组合技术,也可以说是“匿名组合”。

  1. 表现上,在使用Person类HttpPlugin类时,代码调用上是像Java那样直接调用其”继承“的方法:personObj.GetName()
  2. 实现上,AbstractNamed是匿名地嵌入到Persion类中,使用语法糖来模拟上述的直接调用,personObj.AbstractNamed.GetName()才是它的真实形态。

也就是说,Person类的真实形态是:

可见,调用方法函数时,匿名的AbstractNamed是被Go编译器隐藏起来。