继承(指的是子类扩展超类,并不包含接口)是实现代码重用的有力手段,但它并不总是完成这项工作的最佳工具。不适当地使用继承会导致脆弱的软件。
与方法调用不同的是,继承打破了封装性。换句话说子类依赖于超类中特定功能的实现细节。超类的实现可能随着发行版本而变化,就有可能影响子类。因此,子类必须要跟着超类的更新而发展。除非超类是专门为扩展而设计的,并且具有很好的说明文档。
那么继承回来带什么不安定因素呢?书中第62-65页(潘爱民的中译版,下同)给出了一个扩展HashSet的例子,由于篇幅问题,这里就不摘抄了。
从本条的题目看,是有一种叫做复合的技术,更适合。事实上,书中的例子正是举出继承的脆弱性,又给出使用复合的处理方法。其实,所谓复合就是在原来的“子类”中,不再继承“超类”,而是声明一个私有的域,并使用“超类”类型。
我在以前的帖子中,也曾在讨论“自造控件”时提到过 继承 与 复合(http://bbs.airia.cn/FLEX/thread-2978-1-1.aspx)。那时候我给它们分别起了个非常具有比喻意味的别名——改装 和 组装。是以汽车为例的,比如我们要给一辆量产车上加上一个大号的尾翼(最近由于飞车党撞人事件,网络上都在声讨非法改装,与本例无关,我这里只是举个例子,最终要说明的还是编程技术问题),我们可以理解为以原来的车为基础,在基础之上为了扩展更能而加装了尾翼;另一种思维方式,我们也可以理解为我们是在“制造”一辆赛车,用了两个“零件”,一个是一辆量产车,另一个是赛车尾翼。
这两种“思路”反应在程序上,就是 继承 与 复合 的关系。按照本条的“精神”,如果可以用复合实现,那么就该优先使用复合,而不是继承。从自造Flex控件的工程中,我的一点体会来看,我感觉Flex的控件既不是转为继承而设计的(那些控件的超类除外),也并不明确禁止你去扩展。而在实际中确实发现,如果你不能完全读懂并驾驭原控件的源码,并能很好的改写所有你该改写的方法,那么使用继承确实是一件很危险的事情。比如我就遇到了无法扩展原控件的显示区域的问题。稳妥的办法就是使用复合,当然也会带来一些小麻烦,就如同本条中例子(书第65页)一样也会遇到这样的事情,就是如果你想将原控件(“超类”,例中的HashSet)中的公有成员依然暴露出来的话,就必须逐一为它们写getter方法。
【Effective Java 学习笔记】系列连载专题请见:
http://tonylian.iteye.com/categories/64208
分享到:
相关推荐
#### 第十三章:使用实体框架进行数据绑定 **知识点概述:** 本章讲解了如何使用实体框架进行数据绑定,以提高UI的响应速度和用户体验。 **详细知识点:** - **数据绑定基础:** 如何使用实体框架中的数据填充...
第14章 tie函数,DBM文件和数据库挂钩 14.1 连接变量与类 14.1.1 tie函数 14.1.2 预定义方法 14.1.3 连接标量 14.1.4 连接数组 14.1.5 连接散列 14.2 DBM文件 14.2.1 创建并赋给DBM文件数据 ...
3. **编程原则**:程序设计应以清晰性优先,效率其次。 4. **软件工程要素**:软件工程包括方法、工具和过程三个要素。 5. **程序流程图**(PFD):是程序设计中常用的一种表示控制流的工具,通常包含一系列的流程...
第14课 程序调试 14.1 程序的可调试性 14.1.1 增加注释 14.1.2 使用log 14.2 程序调试的基本方法 14.2.1 借助编译器的代码审查 14.2.2 跟踪程序执行流程 14.2.3 断点调试 14.2.4 隔离调试 14.2.5 错误重现...
第14章 三层架构项目开发 493 14.1 什么是三层架构 494 14.1.1 常用的三层架构设计 494 14.1.2 趣味理解:三层架构与养猪 496 14.2 为什么要用三层架构 497 14.3 三层架构项目开发示例 500 14.3.1 数据库设计 500 ...
第14章 三层架构项目开发 493 14.1 什么是三层架构 494 14.1.1 常用的三层架构设计 494 14.1.2 趣味理解:三层架构与养猪 496 14.2 为什么要用三层架构 497 14.3 三层架构项目开发示例 500 14.3.1 数据库设计 500 ...
13. 图论:图的表示(邻接矩阵、邻接表),深度优先搜索(DFS)、广度优先搜索(BFS)及其应用。 14. 树结构:二叉树的遍历(前序、中序、后序),二叉搜索树,平衡树(AVL、红黑树)等。 四、软件工程与设计原则 ...
2.6.3 复合的赋值运算符 2.6.4 赋值表达式 2.7 逗号运算符与逗号表达式 习题 第2篇 面向过程的程序设计 第3章 程序设计初步 3.1 面向过程的程序设计和算法 3.1.1 算法的概念 3.1.2 算法的表示 3.2 C++程序和语句...
2.6.3 复合的赋值运算符 2.6.4 赋值表达式 2.7 逗号运算符与逗号表达式 习题 第2篇 面向过程的程序设计 第3章 程序设计初步 3.1 面向过程的程序设计和算法 3.1.1 算法的概念 3.1.2 算法的表示 3.2 C++程序和语句...
18. 对二维数组进行行优先遍历,题目中的程序会打印第一列的元素,结果是1 5 9。 19. `mysqldump` 命令用于备份MySQL数据库的结构和数据,不是仅备份数据。 20. 关系数据库中,外键(Foreign Key)是指引用另一个...
静态初始化块在类加载时执行,优先于任何构造方法的调用。 53. **抽象方法只有方法头,而无方法体** 正确。抽象方法仅有方法签名,没有具体的实现。 54. **抽象方法一定出现在抽象类中** 正确。抽象方法必须...
- 如果循环体包含多条语句,则必须使用大括号 `{}` 将它们括起来,以形成一个复合语句。 ### 知识点八:do...while循环的控制表达式 1. **题目**:下面程序段()。 ``` x=3; do { y=x--; if(!y){cout”x”...
##### 5.6 Item 14:审慎使用异常规格(EXCEPTION SPECIFICATIONS) - **概念**: - 异常规格用来指定函数可能抛出的异常类型。 - **注意事项**: - 避免过度使用异常规格,以免增加代码复杂度。 - 确保异常规格的...