知识大全 为什么extends是有害的(一)

Posted

篇首语:仓廪实则知礼节,衣食足则知荣辱。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 为什么extends是有害的(一)相关的知识,希望对你有一定的参考价值。

   概述  大多数好的设计者象躲避瘟疫一样来避免使用实现继承(extends 关系) % 的代码应该完全用interfaces写 不用具体的基类 事实上 四人帮的设计模式的书大量的关于怎样用interface继承代替实现继承 这个文章描述设计者为什么有这样的怪癖的想法     Extends是有害的 也许对于Charles Manson这个级别的不是 但是足够糟糕的它应该在任何的可能的时候被避开 四人帮的设计模式花了很大的部分讨论用interface继承代替实现继承     好的设计者在他的代码中 大部分用interface 而不是具体的基类 这个文章讨论 为什么设计者会有这样怪癖的习惯 并且也介绍一些基于interface的编程基础      Interface和Class  一次 我参加一个Java用户组的会议 在会议中 Jams Gosling(Java之父)做发起人讲话 在那令人难忘的Q&A部分 有人问他 如果你重新构造Java 你想改变什么? 我想抛弃classes 他回答 在笑声平息后 它解释说 真正的问题不是由于class本身 而是实现继承(extends 关系) 接口继承(implements关系)是更好的 你应该尽可能的避免实现继承      失去了灵活性  为什么你应该避免实现继承呢?第一个问题是明确的使用具体类名将你固定到特定的实现 给底层的改变增加了不必要的困难     在当前的敏捷编程方法中 核心是并行的设计和开发的概念 在你详细设计程序前 你开始编程 这个技术不同于传统方法的形式 传统的方式是设计应该在编码开始前完成 但是许多成功的项目已经证明你能够更快速的开发高质量代码 相对于传统的按部就班的方法 但是在并行开发的核心是主张灵活性 你不得不以某一种方式写你的代码以至于最新发现的需求能够尽可能没有痛苦的合并到已有的代码中     胜于实现你也许需要的特征 你只需实现你明确需要的特征 而且适度的对变化的包容 如果你没有这种灵活 并行的开发 那简直不可能     对于Inteface的编程是灵活结构的核心 为了说明为什么 让我们看一下当使用它们的时候 会发生什么 考虑下面的代码   [/代码]  f()    LinkedList list = new LinkedList();    //     g( list );      g( LinkedList list )      list add( );    g ( list )    [/代码]  现在 假设一个对于快速查询的需求被提出 以至于这个LinkedList不能够解决 你需要用HashSet来代替它 在已有代码中 变化不能够局部化 因为你不仅仅需要修改f()也需要修改g()(它带有LinkedList参数) 并且还有g()把列表传递给的任何代码   象下面这样重写代码:  [/代码]  f()    Collection list = new LinkedList();    //     g( list );      g( Collection list )      list add( );    g ( list )    [/代码]  这样修改Linked list成hash 可能只是简单的用new HashSet()代替new LinkedList() 就这样 没有其他的需要修改的地方   作为另一个例子 比较下面两段代码   [/代码]  f()    Collection c = new HashSet();    //     g( c );      g( Collection c )      for( Iterator i = erator(); i hasNext() )      do_something_with( i next() );    [/代码]  和   [/代码]  f ()    Collection c = new HashSet();    //     g ( erator() );      g ( Iterator i )    while( i hasNext() )      do_something_with( i next() );    [/代码]  g ()方法现在能够遍历Collection的派生 就像你能够从Map中得到的键值对 事实上 你能够写iterator 它产生数据 代替遍历一个Collection 你能够写iterator 它从测试的框架或者文件中得到信息 这会有巨大的灵活性   耦合  对于实现继承 一个更加关键的问题是耦合 令人烦躁的依赖 就是那种程序的一部分对于另一部分的依赖 全局变量提供经典的例子 证明为什么强耦合会引起麻烦 例如 如果你改变全局变量的类型 那么所有用到这个变量的函数也许都被影响 所以所有这些代码都要被检查 变更和重新测试 而且 所有用到这个变量的函数通过这个变量相互耦合 也就是 如果一个变量值在难以使用的时候被改变 一个函数也许就不正确的影响了另一个函数的行为 这个问题显著的隐藏于多线程的程序     作为一个设计者 你应该努力最小化耦合关系 你不能一并消除耦合 因为从一个类的对象到另一个类的对象的方法调用是一个松耦合的形式 你不可能有一个程序 它没有任何的耦合 然而 你能够通过遵守OO规则 最小化一定的耦合(最重要的是 一个对象的实现应该完全隐藏于使用他的对象) 例如 一个对象的实例变量(不是常量的成员域) 应该总是private 我意思是某段时期的 无例外的 不断的 (你能够偶尔有效地使用protected方法 但是protected实例变量是可憎的事)同样的原因你应该不用get/set函数 他们对于是一个域公用只是使人感到过于复杂的方式(尽管返回修饰的对象而不是基本类型值的访问函数是在某些情况下是由原因的 那种情况下 返回的对象类是一个在设计时的关键抽象)     这里 我不是书生气 在我自己的工作中 我发现一个直接的相互关系在我OO方法的严格之间 快速代码开发和容易的代码实现 无论什么时候我违反中心的OO原则 如实现隐藏 我结果重写那个代码(一般因为代码是不可调试的) 我没有时间重写代码 所以我遵循那些规则 我关心的完全实用—我对干净的原因没有兴趣      脆弱的基类问题  现在 让我们应用耦合的概念到继承 在一个用extends的继承实现系统中 派生类是非常紧密的和基类耦合 当且这种紧密的连接是不期望的 设计者已经应用了绰号 脆弱的基类问题 去描述这个行为 基础类被认为是脆弱的是 因为你在看起来安全的情况下修改基类 但是当从派生类继承时 新的行为也许引起派生类出现功能紊乱 你不能通过简单的在隔离下检查基类的方法来分辨基类的变化是安全的 而是你也必须看(和测试)所有派生类 而且 你必须检查所有的代码 它们也用在基类和派生类对象中 因为这个代码也许被新的行为所打破 一个对于基础类的简单变化可能导致整个程序不可操作     让我们一起检查脆弱的基类和基类耦合的问题 下面的类extends了Java的ArrayList类去使它像一个stack来运转   [/代码]  class Stack extends ArrayList    private int stack_pointer = ;      public void push( Object article )      add( stack_pointer++ article );          public Object pop()      return remove( stack_pointer );          public void push_many( Object[] articles )      for( int i = ; i < articles.length; ++i )        push( articles[i] );        [/代码]    甚至一个象这样简单的类也有问题。Tw.WiNgwIt.思考当一个用户平衡继承和用ArrayList的clear()方法去弹出堆栈时:  [/代码]  Stack a_stack = new Stack();  a_stack.push("1");  a_stack.push("2");  a_stack.clear();  [/代码]    这个代码成功编译,但是因为基类不知道关于stack指针堆栈的情况,这个stack对象当前在一个未定义的状态。下一个对于push()调用把新的项放入索引2的位置。(stack_pointer的当前值),所以stack有效地有三个元素-下边两个是垃圾。(Java的stack类正是有这个问题,不要用它).    对这个令人讨厌的继承的方法问题的解决办法是为Stack覆蓋所有的ArrayList方法,那能够修改数组的状态,所以覆蓋正确的操作Stack指针或者抛出一个例外。(removeRange()方法对于抛出一个例外一个好的候选方法)。    这个方法有两个缺点。第一,如果你覆蓋了所有的东西,这个基类应该真正的是一个interface,而不是一个class。如果你不用任何继承方法,在实现继承中就没有这一点。第二,更重要的是,你不能够让一个stack支持所有的ArrayList方法。例如,令人烦恼的removeRange()没有什么作用。唯一实现无用方法的合理的途径是使它抛出一个例外,因为它应该永远不被调用。这个方法有效的把编译错误成为运行错误。不好的方法是,如果方法只是不被定义,编译器会输出一个方法未找到的错误。如果方法存在,但是抛出一个例外,你只有在程序真正的运行时,你才能够发现调用错误。    对于这个基类问题的一个更好的解决办法是封装数据结构代替用继承。这是新的和改进的Stack的版本:  [/代码]  class Stack      private int stack_pointer = 0;    private ArrayList the_data = new ArrayList();        public void push( Object article )      the_data.add( stack_poniter++, article );      public Object pop()      return the_data.remove( --stack_pointer );      public void push_many( Object[] articles )      for( int i = 0; i < o.length; ++i )      push( articles[i] );     cha138/Article/program/Java/JSP/201311/19788

相关参考

知识大全 关于扩展(extended)的rowid的一些内容

     rowid的介绍  先对rowid有个感官认识    SQL>selectROWIDfromBruce_testwhererownum<;    ROWID    AAABnlA

知识大全 Extended CHM PHP 语法手册之 DIY

ExtendedCHMPHP语法手册之DIY  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Ex

复印机使用过程中会排放一种对人体有害的气体。这种气体是:__

复印机使用过程中会排放一种对人体有害的气体。这种气体是:_____A、一氧化碳B、二氧化硫C、臭氧D、硫化氢答案:C解析:空气中的臭氧含量极低,人体吸入后不会产生影响:但是复印机在使用过程中产生的静电

低辐射是否有害

大量辐射对人体有害,低剂量辐射是好是坏尚无定论。一提起辐射,人们也许最先想到的是核辐射,广岛、长崎原子弹爆炸曾造成数十万人死亡;切尔诺贝利核泄露事故也酿成震惊世界的悲剧。在日常生活中,人们最担心的则是

有害生物防治的轻简技术关键是什么?

有害生物防治的轻简技术关键是:程序简单、操作简便、效果明显。轻简技术的要求是:以实用性为原则,以简便、节约性为指标,以有效性为目的。技术关键的所在就是:一种技术的推广应用能够解决多种生物为害的问题;一

有害生物防治的轻简技术关键是什么?

有害生物防治的轻简技术关键是:程序简单、操作简便、效果明显。轻简技术的要求是:以实用性为原则,以简便、节约性为指标,以有效性为目的。技术关键的所在就是:一种技术的推广应用能够解决多种生物为害的问题;一

知识大全 请问长时间的睡眠对身体有害吗

请问长时间的睡眠对身体有害吗?我每天晚上的睡眠时间是11个小时,可是白天…是的,我可以肯定的告诉你:过长的睡眠有害无益!越睡越懒,智力下降一晚睡10个小时,为什么白天还是无精打采呢?在某公司上班的小陈

为什么食用生鱼粥、生鱼片对人体有害?

吃生鱼粥、生鱼片对广东、福建一带的人来说,可谓一种美味。然而从医学角度来看,这种吃法对人体是有害的。  吃生鱼粥或生鱼片容易得肝吸虫病,危害肝脏,导致肝纤维化或肝癌。因为在南方的河流中,生存有大量肝吸

知识大全 我经常会在熟悉的环境下,会有害怕和陌生的心理,一个人会很不自在,会想快点逃离

我经常会在熟悉的环境下,会有害怕和陌生的心理,一个人会很不自在,会想快点逃离?主动出击,我以前也是和你一样,但是我这人有一点就是话多,别人和我并不熟,没办法我就先听听别人之间交谈有什么话题自己也挺感兴

白癜风激光有害吗

白癜风激光有害吗,随着现在医学水平的不断进步,治疗疾病的方法也越来越多,但是一种小感冒就可能有好多种治疗方法,所以说对于白癜风这样的疾病,就会有更得治疗方法了,我们熟知到治疗有药物治疗,以及手术治疗,