编程需要注意的陷阱:java继承是有害的

上传人:子 文档编号:43281726 上传时间:2018-06-05 格式:DOC 页数:9 大小:17.87KB
返回 下载 相关 举报
编程需要注意的陷阱:java继承是有害的_第1页
第1页 / 共9页
编程需要注意的陷阱:java继承是有害的_第2页
第2页 / 共9页
编程需要注意的陷阱:java继承是有害的_第3页
第3页 / 共9页
编程需要注意的陷阱:java继承是有害的_第4页
第4页 / 共9页
编程需要注意的陷阱:java继承是有害的_第5页
第5页 / 共9页
点击查看更多>>
资源描述

《编程需要注意的陷阱:java继承是有害的》由会员分享,可在线阅读,更多相关《编程需要注意的陷阱:java继承是有害的(9页珍藏版)》请在金锄头文库上搜索。

1、编程需要注意的陷阱:编程需要注意的陷阱:JavaJava 继承是有害的继承是有害的大多数好的设计者象躲避瘟疫一样来避免使用实现继承(extends 关系)。实际上 80%的代码应该完全用 interfaces 写,而不是通过extends。 “JAVA 设计模式”一书详细阐述了怎样用接口继承代替实现继承。这篇文章描述设计者为什么会这么作。 Extends 是有害的;也许对于 Charles Manson 这个级别的不是,但是足够糟糕的它应该在任何可能的时候被避开。 “JAVA 设计模式”一书花了很大的部分讨论用 interface 继承代替实现继承。 好的设计者在他的代码中,大部分用 inte

2、rface,而不是具体的基类。本文讨论为什么设计者会这样选择,并且也介绍一些基于interface 的编程基础。 接口(Interface)和类(Class)? Jams Gosling(Java 之父)做发起人讲话。有人问他:“如果你重新构造 Java,你想改变什么?” 。 “我想抛弃 classes”他回答。在笑声平息后,它解释说,真正的问题不是由于 class 本身,而是实现继承(extends 关系)。接口继承(implements 关系)是更好的。你应该尽可能的避免实现继承。 失去了灵活性 为什么你应该避免实现继承呢?第一个问题是明确的使用具体类名将你固定到特定的实现,给底层的改变增

3、加了不必要的困难。 在当前的敏捷编程方法中,核心是并行的设计和开发的概念。在你详细设计程序前,你开始编程。这个技术不同于传统方法的形式-传统的方式是设计应该在编码开始前完成-但是许多成功的项目已经证明你能够更快速的开发高质量代码,相对于传统的按部就班的方法。但是在并行开发的核心是主张灵活性。你不得不以某一种方式写你的代码以至于最新发现的需求能够尽可能没有痛苦的合并到已有的代码中。 胜于实现你也许需要的特征,你只需实现你明确需要的特征,而且适度的对变化的包容。如果你没有这种灵活,并行的开发,那简直不可能。 对于 Inteface 的编程是灵活结构的核心。为了说明为什么,让我们看一下当使用它们的时

4、候,会发生什么。考虑下面的代码: f() LinkedList list = new LinkedList();/.g( list );g( LinkedList list )list.add( . );g2( list )现在,假设一个对于快速查询的需求被提出,以至于这个LinkedList 不能够解决。你需要用 HashSet 来代替它。在已有代码中,变化不能够局部化,因为你不仅仅需要修改 f()也需要修改 g()(它带有 LinkedList 参数) ,并且还有 g()把列表传递给的任何代码。象下面这样重写代码: f() Collection list = new LinkedList(

5、);/.g( list );g( Collection list )list.add( . );g2( list )这样修改 Linked list 成 hash,可能只是简单的用 new HashSet()代替 new LinkedList()。就这样。没有其他的需要修改的地方。 作为另一个例子,比较下面两段代码: f() Collection c = new HashSet();/.g( c );g( Collection c )for( Iterator i = c.iterator(); i.hasNext() )do_something_with( i.next() );和 f2()

6、 Collection c = new HashSet();/.g2( c.iterator() );g2( Iterator i ) while( i.hasNext() )do_something_with( i.next() );g2()方法现在能够遍历 Collection 的派生,就像你能够从 Map 中得到的键值对。事实上,你能够写 iterator,它产生数据,代替遍历一个 Collection。你能够写 iterator,它从测试的框架或者文件中得到信息。这会有巨大的灵活性。 耦合 对于实现继承,一个更加关键的问题是耦合-令人烦躁的依赖,就是那种程序的一部分对于另一部分的依赖。

7、全局变量提供经典的例子,证明为什么强耦合会引起麻烦。例如,如果你改变全局变量的类型,那么所有用到这个变量的函数也许都被影响,所以所有这些代码都要被检查,变更和重新测试。而且,所有用到这个变量的函数通过这个变量相互耦合。也就是,如果一个变量值在难以使用的时候被改变,一个函数也许就不正确的影响了另一个函数的行为。这个问题显著的隐藏于多线程的程序。 作为一个设计者,你应该努力最小化耦合关系。你不能一并消除耦合,因为从一个类的对象到另一个类的对象的方法调用是一个松耦合的形式。你不可能有一个程序,它没有任何的耦合。然而,你能够通过遵守 OO 规则,最小化一定的耦合(最重要的是,一个对象的实现应该完全隐藏

8、于使用他的对象) 。例如,一个对象的实例变量(不是常量的成员域) ,应该总是 private。我意思是某段时期的,无例外的,不断的。 (你能够偶尔有效地使用 protected 方法,但是protected 实例变量是可憎的事)同样的原因你应该不用 get/set函数-他们对于是一个域公用只是使人感到过于复杂的方式(尽管返回修饰的对象而不是基本类型值的访问函数是在某些情况下是由原因的,那种情况下,返回的对象类是一个在设计时的关键抽象) 。 这里,我不是书生气。在我自己的工作中,我发现一个直接的相互关系在我 OO 方法的严格之间,快速代码开发和容易的代码实现。无论什么时候我违反中心的 OO 原则

9、,如实现隐藏,我结果重写那个代码(一般因为代码是不可调试的) 。我没有时间重写代码,所以我遵循那些规则。我关心的完全实用?我对干净的原因没有兴趣。 脆弱的基类问题 现在,让我们应用耦合的概念到继承。在一个用 extends 的继承实现系统中,派生类是非常紧密的和基类耦合,当且这种紧密的连接是不期望的。设计者已经应用了绰号“脆弱的基类问题”去描述这个行为。基础类被认为是脆弱的是,因为你在看起来安全的情况下修改基类,但是当从派生类继承时,新的行为也许引起派生类出现功能紊乱。你不能通过简单的在隔离下检查基类的方法来分辨基类的变化是安全的;而是你也必须看(和测试)所有派生类。而且,你必须检查所有的代码

10、,它们也用在基类和派生类对象中,因为这个代码也许被新的行为所打破。一个对于基础类的简单变化可能导致整个程序不可操作。 让我们一起检查脆弱的基类和基类耦合的问题。下面的类 extends了 Java 的 ArrayList 类去使它像一个 stack 来运转: class Stack extends ArrayList private int stack_pointer = 0;public void push( Object article ) add( stack_pointer+, article );public Object pop() return remove( -stack_po

11、inter );public void push_many( Object articles ) for( int i = 0; i high_water_mark )high_water_mark = current_size;super.push( article );publish Object pop()-current_size;return super.pop();public int maximum_size_so_far()return high_water_mark;这个新类运行的很好,至少是一段时间。不幸的是,这个代码发掘了一个事实,push_many()通过调用 push()来运行。首先,这个细节看起来不是一个坏的选择。它简化了代码,并且你能够得到push()的派生类版本,甚至当 Monitorable_stack 通过 Stack 的参考来访问的时候,以至于 high_water_mark 能够正确的更新

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 生活休闲 > 科普知识

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号