多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
### Replace Inheritance with Delegation(以委托取代继承) 某个subclass 只使用superclass 接口中的一部分,或是根本不需要继承而来的数据。 在subclass 中新建一个值域用以保存superclass ;调整subclass 函数,令它改而委托superclass ;然后去掉两者之间的继承关系。 ![](https://box.kancloud.cn/2016-08-15_57b1b5e7bac0c.gif) **动机(Motivation)** 继承(Inheritance )是一件很棒的事,但有时候它并不是你要的。常常你会遇到这样的情况:一开始你继承了一个class ,随后发现superclass 的许多操作并不真正 适用于subclass 。这种情况下你所拥有的接口并未真正反映出class 的功能。或者,你可能发现你从superclass 中继承了 一大堆subclass 并不需要的数据,抑或者你可能发现superclass 中的某些protected 函数对subclass 并没有什么意义。 你可以选择容忍,并接受传统说法:subclass 可以只使用superclass 功能的一部分。但这样做的结果是:代码传达的信息与你的意图南辑北辙——这是一种裩淆,你应该将它去除。 如果以委托(delegation)取代继承(Inheritance ),你可以更清楚地表明:你只需要受托类(delegated class)的一部分功能。接口中的哪一部分应该被使用,哪一部分应该被忽略,完全由你主导控制。这样做的成本则是需要额外写出请托函数(delegating methods),但这些函数都非常简单,极少可能出错。 **作法(Mechanics)** - 在subclass 中新建一个值域,使其引用(指向、指涉、refers)superclass 的一个实体,并将它初始化为this。 - 修改subclass 内的每一个(可能)函数,让它们不再使用superclass ,转而使 用上述那个「受托值域」(delegated field)。每次修改后,编译并测试。 - 你不能如此这般地修改subclass 中「通过super 调用superclass 函数」的函数,否则它们会陷入无限递归(infinite recurse)。这一类函数只有在继承关系被打破后才能修改。 - 去除两个classes 之间的继承关系,将上述「受托值域」(delegated field)的赋值动作修改为「赋予一个新对象」。 - 针对客户端所用的每一个superclass 函数,为它添加一个简单的请托函数(delegating method)。 - 编译,测试。 **范例:(Example)** 「滥用继承」的一个经典范例就是让Stack class 继承Vector class 。Java 1.1的utility library(java.util)恰好就是这样做的。(这些淘气的孩子啊!)不过,作为范例,我只给出一个比较简单的形式: ~~~ class MyStack extends Vector { public void push(Object element) { insertElementAt(element,0); } public Object pop() { Object result = firstElement(); removeElementAt(0); return result; } } ~~~ 只要看看Mystack 用户,我就会发现,用户只要它做四件事:push()、pop()、size() 和 isEmpty() 。后两个函数是从Vector 继承来的。 我要把这里的继承关系改为委托关系。首先,我要在中新建一个值域,用以保存「受托之Vector 对象」。一开始我把这个值域初始化为this,这样在重构进行过程中,我就可以同时使用继承和委托: ~~~ private Vector _vector = this; ~~~ 现在,我开始修改MyStack 的函数,让它们使用委托关系。首先从push() 开始: ~~~ public void push(Object element) { _vector.insertElementAt(element,0); } ~~~ 此时我可以编译并测试,一切都将运转如常。现在轮到pop() : ~~~ public Object pop() { Object result = _vector.firstElement(); _vector.removeElementAt(0); return result; } ~~~ 修改完所有subclass 函数后,我可以打破与superclass 之间的联系了 : ~~~ class MyStack extends Vector private Vector _vector = new Vector(); ~~~ 然后,对于Stack 客户端可能用到的每一个Vector 函数(译注:这些函数原本是 继承而来的),我都必须在中添加一个简单的请托函数(delegating method): ~~~ public int size() { return _vector.size(); } public boolean isEmpty() { return _vector.isEmpty(); } ~~~ 现在我可以编译并测试。如果我忘记加入某个请托函数,编译器会告诉我。