您的位置:首页 > Web前端

Effective Java 16:复合优先于继承 Favor composition over inheritance

2016-10-17 23:01 274 查看
注:

本书使用继承(inheritance)一词,含义是实现继承(implement inheritance,当一个类扩展另一个类的时候)

本条目中讨论的问题并不适用于接口继承(interface inheritance,当一个类实现一个接口的时候,或者当一个接口扩展另一个接口的时候)

———————————————————————————————————————————————

对于专门为了继承而设计、并且具有很好的文档说明的类来说,使用继承是非常安全的

但对普通的具体类(concrete class)进行跨越包界限的继承,则是非常危险的

与方法调用不同的是,继承打破了封装性

换句话说,子类依赖于其超类中特定功能的实现细节

超类的实现有可能会随着发行版本的不同而有变化

子类可能会遭到破坏,即使它的代码完全没有改变

一种可能,子类对超类的依赖基于超类内部的自用方法之上

这种自用性(self-use)是实现细节,不是承诺,不能保证在Java平台的所有实现中都保持不变

这样得到的子类是非常脆弱的,有可能导致安全上的漏洞

另一种可能,超类在后续的发行版本中获得了一个新的方法

并且不幸的是,你给子类提供了一个签名相同但返回类型不同的方法,那么这样的子类将无法通过编译

———————————————————————————————————————————————

解决方案:不用扩展现有的类,而是在新的类中增加一个私有域,它引用现有类的一个实例

这种设计被称做“复合(composition)”,因为现有的类变成了新类的一个组件

新类中的每个实例方法都可以调用被包含的现有类实例中对应的方法,并返回它的结果

这被称为转发(forwarding),新类中的方法被称为转发方法(forwarding method)

这种实现分为两部分:类本身和可重用转发类(forwarding class)

这也正是Decorator模式,转发类某种程度上也可以被称为包装类(wrapper class)

有时候,复合和转发的结合也被错误地称为“委托(delegation)


从技术的角度而言,除非包装对象把自身传递给被包装的对象

———————————————————————————————————————————————

需要注意一点,包装类不适合应用在回调框架(callback framework)

在回调框架中,对象把自身的引用传递给其他的对象,用户后续的调用(“回调”)

因为被包装起来的对象并不知道它外面的包装对象,所以它传递一个指向自身的引用(this)

回调时避开了外面的包装对象,这被称为SELF问题

———————————————————————————————————————————————

只有当子类真正是超类的子类型(subtype)时,才适合继承

换句话说,对于两个类A和B,只有当两者之间确实存在“is-a”关系的时候,类B才应该扩展类A

如果每个B确实也是A么的答案是否定的,通常情况下,B应该包含A的一个私有实例

并且暴露一个较小的、较简单的API:A本质上不是B的一部分,只是它的实现细节而已

———————————————————————————————————————————————

此外,对于你正试图扩展的类,它的API中有没有缺陷呢?

如果有,你是否愿意把那些缺陷传播到类的API中?

继承机制会把超类API中的所有缺陷传播到子类中,而复合则允许设计新的API来隐藏这些缺陷
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: