程序原本(五十九):程序设计的核心思想——对象系统:表达、使用与模式(再论继承性)

阅读数:28 2019 年 10 月 1 日 16:48

程序原本(五十九):程序设计的核心思想——对象系统:表达、使用与模式(再论继承性)

接下来我们再回过头来,深入地讨论一下对象系统中的继承性问题。

首先,继承是一种抽象方法,即对事物间延伸关系的一种认识。同时它也是一种手段,使得对象的一个性质能够“遗传”给子类对象,从而使子类对象可以具有该性质,而无需特别地、显式地说明。

这涉及对象、类与子类的定义与关系。《结构程序设计》将“类”定义为“产生对象(实例)”的过程,这的确带有历史痕迹。除开这一点,这一定义意味着两个事实:

  1. 类,必须了解对象是什么样的,即类必须知道对象所有性质的定义;
  2. 对象,必然是由一个构造过程产生的,这个过程必须知道类,或等同于类本身。

基于这两点,如何来实现继承呢?总结如今在面向对象开发上的实践,所谓继承,有三种实现方式:原型继承、类继承、元类继承。

原型继承是对上述第二项的实践。这种继承方式认为,子类对象对父类对象的相似性可以藉由“抄写(复制)”而得到。这一抄写的过程,即是构造过程。因此,如果一个构造过程F产生b对象,则F只需要知道b的父类对象A即可,并不需要通过一个独立的(例如类 B)来获得这一认识。下面的代码反映了这一实现原型继承的基本模式:

复制代码
// JavaScript Syntax
F.prototype = A;
b = F();

类继承则显式地要求一个“”来记录对象间的继承关系。类可以是

  • A:构造过程(逻辑),或
  • B:记录上述关系的数据,或
  • C:仅仅是在源代码期间可知的一种文本定义(声明性文本,由翻译程序处理)。

最后一种情况 C 是基本形式。即作为某种面向对象语言的一个特性 / 机制时,仅有翻译程序需要知道代码中对象之间的继承关系,这一关系可以用声明性的文本记录在代码中,而完全不需要成为可执行机器代码(即翻译程序处理的结果)的一部分——因为,事实上我们说过,机器代码根本不知道何谓“对象”,更无所谓的“继承”关系8

8 例如不支持 RTTI(RunTime Type Information, 运行期类型信息)、编译性的语言。

当这种关系需要为程序员所知——例如试图在代码中知道和处理继承关系,那么它就必须被表达为一种数据,即,亦即是上述的情况 B;更进一步,该种数据也必然因其具有一种系列关系而具有数据类型,即类类型。可见,类类型本身只描述、记录对象的继承关系;而,根据我们此前的定义,它可以是构造过程本身,也可以仅仅是构造过程执行时了解继承关系所需的一个参考9

9 例如支持 RTTI 的语言。由于在运行期了解类的关系,因此类必须在 RTTI 中登记为一个类型,即类类型。

那么,从这里也可以了解到,事实上如果构造过程本身记录了上述继承性关系,又可以向外公布这种继承性关系(以便程序员得知),那么也可以是一个过程,即上述的情况 A。这种情况下,类类型也可以是该过程的一个引用10

10 例如在函数式语言中实现类继承方式的面向对象系统。这时,类信息应该记录在一个构造过程(函数)中,并通过该过程向外公布。

下面的形式代码(基于 JavaScript 和 Pascal)反映了 A、B 两种实现类继承的基本模式11

11 C 模型是 Delphi 的基本模型,并且由 C 至 B,是 Delphi 在抽象概念上从 Object 到 Component 进化的基本动力——亦即是说,“组件与组件库”是 Delphi 基于 RTTI 的成功实现。在作者的开源项目 Qomo 中,使用了 A 模型来实现“基于原型继承的类继承”,因为 JavaScript 的原型继承本身就实现了“构造过程”这一特性。

复制代码
// B:类是数据,而非执行体
type // <-- 翻译程序可以通过 type 定义来注册 rtti
TMyObject = { ... };
TMyObjectClassType = class of TMyObject;
Obj = TMyObject.Create();
// A:类是代码,即构造过程
Obj = MyObject();
rtti = MyObject.typeinfo; // 或 MyObject('typeinfo');

现在,我们再一次考察支持类继承的系统,便会意识到一个问题:这样的对象系统中既有对象,也有类。既然对象是通过类来得到的,那么“类”又是从哪里来的呢?也就是说,对于最终执行的程序来说,如何了解一个类自何诞生?

而这也就是元类继承所要解释的问题了。既然

  • 我们可以将所有数据视为对象,又,
  • 上述的也是一种数据,那么,
  • 上述的自然也就是一种对象,因此,
  • 也就可以有类的构造过程

这种构造过程,就被称为元类,它是在从对象系统——这一数据定义——中分别出之后,所必需的一种解释。而本章中有关的一切解释也可以同样作用于元类,例如元类也可以有上述的三种记录对象间的继承关系的方式。

(在上一小节所述之外的)其他六种 GoF 模式讨论了对上述“构造过程”的实现12 13,但并没有强调继承关系通过何种方式来维护——事实上 GoF 隐含地基于并依赖“类继承”模型。这些实现方式的关系如图 30 所示。

12 GoF 对元类的叙述是没有的,但这不妨碍用 GoF 模式在语言去中实现元类继承。

13 Adapter for Class、Interpreter 和 Template Method 的“范围”为“类”,表明它们可以是类上的行为,而并非是指它们“产生类”。

图 30 GoF 隐含地基于并依赖“类继承”模型

程序原本(五十九):程序设计的核心思想——对象系统:表达、使用与模式(再论继承性)

评论

发布