美文网首页
Effective Java学习笔记-继承篇

Effective Java学习笔记-继承篇

作者: 一只小松 | 来源:发表于2019-10-19 15:30 被阅读0次

    Section 4

    Article 14 复合优于继承

    1. 不适当的使用(超类并不是为了扩展而设计)继承会导致脆弱的软件。但在一个包内使用继承是非常安全的。另外对于专门为了继承而设计、并且有非常好的文档说明的类,使用继承也是非常安全的。然而对于普通的具体类进行跨越包边界的继承,是非常危险的。
    2. 继承打破了封装性。除非超类是专门为了扩展而设计的,并且有很好的文档说明。否则一个子类必须随着超类的更新而发展。关于自用性self-use的定义:是实现细节,不是承诺,不能保证后续都保持不变、不随版本的不同而不发生变化。
    3. 造成子类脆弱性原因之一:他们的超类在后续版本中可以获得新的方法。子类化增加的方法和超类获得的新方法功能冲突时,很可能调用到超类这个未被子类改写的新方法,导致子类的特异化处理失效。例如:把Hashtable和Vector加入到Collections Framework中的时候,这就成为了一个安全问题:非法元素会被添加到子类的实例中。另 外还有一种情况,超类在后续版本中获得了一个新的方法,不幸的是,子类中也存在一个拥有同样的原型特征方法,只是返回类型不同,那么这样的子类将无法通过编译(实际上是改写了超类中的方法)。
    4. 幸运的是,我们可以通过复合来避免以上出现的问题。我们不再扩展一个已有的类,而是在新类中增加一个私有域,引用一个已有的类的一个实例。依照已有类的API重新进行包装实现(可以将API规范进行抽象)。这种设计获得了格外的灵活性和健壮性。其实这种包装类采用的Decorator模式,为已有类增加新的特性,同时保留了原有类的能力。
    5. 包装类不适合用在回调框架中。在回调框架中,对象把自己的引用传递给其他对象,以便将来回调回来。因为被包装起来的对象并不知道他外面的包装对象,所以他传递一个指向自己的引用,回调时绕开了外面的包装对象。所以回调框架适合使用接口规范进行回调。微信支付的框架显然不太符合以上的原则。
    6. 只有当子类真正是超类的“子类型”的时候,继承才是合适的。所以设计时,我们应该问自己类B扩展类A,每一个B确实也是A吗?在Java平台库中,有许多明显违反这条原则的设计。Stack(栈)并不是一个Vector(向量),Properties(属性列表)并不是Hashtable(散列表)。此时使用复合才是最恰当的。
    7. 决定使用继承而不是复合之前,深思熟虑,我们正在试图扩展的类,其API设计是否有缺陷?如果有,是否愿意把这些缺陷传播到自己类的API中呢?继承机制会把超类的API所有的缺陷传播到子类中,而复合技术则允许你设计一个新的API,从而隐藏这些缺陷。

    Article 15 要么专门为继承而设计,并给出文档说明,要么禁止继承

    1. 一个好的API文档应该描述一个方法做了什么工作。而不是如何做到。
    2. 为了允许继承,构造函数一定不能调用可被改写的方法
    3. 为了继承而进行设计,一个类必须通过某种形式提供适当的钩子,以便能够进入到它的内部工作流程中,这样的形式可以是精心选择的受保护方法。
    4. 为了继承而设计一个类的时候,应该尽可能少量地提供受保护的方法和域,因为每一项这样的方法或者域都代表了一项关于实现细节的承诺

    Article 16 接口优于抽象类

    1. 接口和抽象类被用于定义一个允许多个实现的类型
    2. 已有类可以很容易被更新,以实现新的接口。如果希望让两个类扩展同一个抽象类,则这个抽象类的需位于类型层次的上部,使得成为所有子类的共同祖先。不幸的是,这样做会间接的伤害到类层次。强迫所有子类都扩展这个新的抽象类,而不管他对于后代类是否合适。
    3. 接口是定义混合类型的理想选择
    4. 接口使得我们可以构造出非层次结构的类型框架

    相关文章

      网友评论

          本文标题:Effective Java学习笔记-继承篇

          本文链接:https://www.haomeiwen.com/subject/dkqnmctx.html