2.4 保护实现
C++面向对象高效编程(第2版)
传统的面向过程编程,缺乏对实现者的保护。如果实现在生存期内无法保证自身的完整性,这样的实现则毫无用处。假如有人篡改了汽车速度表的传感装置会怎样?速度表将无法正确显示汽车的速度。假如自动柜员机(ATM)允许客户可以直接操作他人的账户会怎样?假如允许消费者不通过电源插座,直接使用电力电缆会怎样?
以上这些情况都有一个共同点,即它们只可通过特定的方式(接口)使用,没有其他方法(电脑黑客除外!)可绕过接口直接通向实现。驾驶员不能篡改速度表,至少是很难1。ATM允许个人对自己的账户执行某些受限操作,但不允许客户接触他人的账户。同样,直接从家门前的电线杆上(或配电箱中)接电是非法的。
这些例子中,接口(速度表、ATM、电源插座)都由实现支持,而且该实现由对应的接口来保护(即接口提供一个清晰且定义明确的方法访问实现)。换言之,实现以特定方式工作,并跟踪自身的状态。另外,实现假设它的状态仅能通过接口更改,如果违反此前提条件(即不知何故,实现的状态被直接从外部更改,并未通过提供的接口更改),则无法保证实现进行正确地操作。从接口的角度看,实现应通过公共接口运行(或由公共接口访问),以确保实现的完整性。
回顾一下影碟播放机的例子,当我们按下PLAY时,LD播放机的实现将进行检查,以确保:
(a)已载入碟片;
(b)已关闭碟片托架(或托盘)。
如果满足了以上条件,LD播放机将激活激光束装置和马达驱动器等,并开始读碟。为了检测条件(a)和(b),LD播放机可能使用某些微动开关,而且假设任何人都无法直接操作这些开关,只有实现才能控制微动开关。[ 这与我们常见的洗衣机和烘干机的关门传感器类似。这些电器附带的说明书中会清楚地说明,只有关闭洗衣机的门,洗衣机才可运转。如果仔细查看会发现,在洗衣机门的下面或后面有一个小型传感器。洗衣机门关闭时,开关被压低,说明门已关闭。如果好奇心驱使你按下开关,可以确信洗衣机在门未关闭的情况下,将开始运转!此时你已经绕过洗衣机的接口直接操作,而这样的行为可能导致你受伤或洗衣机受损。] 如果违反了这个假设,将无法保证播放机按指定说明运行。如果我们手动操控这些开关,愚笨的播放机则相信碟片已载入,并且托架已关闭;然后我们按下PLAY按钮,播放机一定会启动激光束装置,这可能会对播放机和用户造成不可挽回的损失(万幸的是,播放机的构造不会让这样的情况发生)。
如果用户按已发布的接口(published interface)操作(即按照说明使用),则不会出现这种情况。
图2-3
数据抽象引出了相关的概念:数据封装(data encapsulation)。只要存在由实现支持的带接口的对象,就一定存在实现隐藏(implementation hiding)(也称为信息隐藏)。有些信息对实现而言相当重要,但是对使用接口的用户而言却毫无价值,这些信息将被隐藏在实现中。实现由接口封装,而且接口是访问该实现的唯一合法途径(见图2-3)。
这就相当于说:通过门是进入房间的唯一的途径。LD播放机把激光束装置、马达和电子元件封装在内。被封装的项很可能是另一个对象(或一组对象),而马达和激光束的确是其他类的对象。注意,被封装的数据对于对象的实现极其重要。进一步而言,实现必须维护被封装信息的完整(或正确的状态)。