Introduce Local Extension 引入本地拓展
你需要为服务类提供一些额外函数,但你无法修改这个类。建立一个新类,使它包含这些额外函数。让这个扩展品成为源类的子类或包装类。
动机
很遗憾,类的作者无法预知未來,他们常常没能为你预先准备一些有用的函数。如果你可以修改源码,最好的办法就是直接加入自己需要的函数。但你经常无法修改源码。如果只需要一两个函数,你可以使用Introduce Foreign Method。但如果你需要的额外函数超过两个,外加函数就很难控制它们了。所以,你需要将这些函数组织在一起,放到一个恰当地方去。要达到这一目的,两种标准对象技术——子类化(subclassing)和包装(wrapping)——是显而易见的办法。这种情况下,我把子类或包装类统称为本地扩展(local extension)。
所谓本地扩展是一个独立的类,但也是被扩展类的子类型:它提供源类的一切特性,同时额外添加新特性。在任何使用源类的地方,你都可以使用本地扩展取而代之。
使用本地扩展使你得以坚持“函数和数据应该被统一封装”的原则。如果你一直把本该放在扩展类中的代码零散地放置于其他类中,最终只会让其他这些类变得过分复杂,并使得其中函数难以被复用。
在子类和包装类之间做选择时,我通常首选子类,因为这样的工作量比较少。 制作子类的最大障碍在于,它必须在对象创建期实施。如果我可以接管对象创建过程,那当然没问题:但如果你想在对象创建之后再使用本地扩展,就有问题了。此外,子类化方案还必须产生一个子类对象,这种情况下,如果有其他对象引用了旧对象,我们就同时有两个对象保存了原数据!如果原数据是不可修改的,那也没问题,我可以放心进行复制:但如果原数据允许被修改,问题就来了,因为一个修改动作无法同时改变两份副本。这时候我就必须改用包装类。使用包装类时,对本地扩展的修改会波及原对象,反之亦然。
做法
- 建立一个扩展类,将它作为原始类的子类或包装类。
- 在扩展类中加入转型构造函数。
- 所谓“转型构造函数”是指“接受原对象作为参数”的构造函数。如果采用子类化方案,那么转型构造函数应该调用适当的超类构造函数;如果采用包装类方案,那么转型构造函数应该将它得到的传入参数以实例变量的 形式保存起来,用作接受委托的原对象。
- 在扩展类中加入新特性。
- 根据需要,将原对象替换为扩展对象。
- 将针对原始类定义的所有外加函数搬移到扩展类中。