当前位置:首页 > 教学设计 > js实现继承_“实现继承”是有害的
 

js实现继承_“实现继承”是有害的

发布时间:2019-03-12 04:12:32 影响了:

  在面向对象开发中,“Extends”这个关键字是很有害的,开发者应当尽量避免使用它,应当采用接口继承取代实现继承。   在一次Java用户大会上,Java的发明者James Gosling在作主题演讲时,有听众问他:“如果您回过头重新设计Java,会做哪些改变?”他回答说: “我会去掉类。”等笑声平息之后,他解释说,真正的问题不是出在类本身,而是出在实现继承上。其实,接口继承更好,开发者应当尽可能避免使用实现继承。
  
  失去灵活性
  
  为什么应该避免实现继承呢?第一个问题就是,明确使用具体类的名称会把开发者束缚在特定的实现中,这为以后进行改变带来了不必要的困难。
  当代敏捷开发方法的核心是让设计和开发并行化,在全面而详细地描述程序之前,就可以开始编程了。这种技术完全违背了应当在开始编程之前完成设计的传统理念,但是很多成功的项目已经证明: 用这种方法,可以比传统的流水线开发方法更快速地开发出高质量的代码,而且更具成本效益。不过,并行开发的核心是灵活性这个概念。必须用以下方式来编写代码: 能够尽可能轻松地把新发现的需求加入到现有代码中。
  开发者要实现的不是“可能”需要的特性,而是“肯定”需要的特性。要是没有很强的灵活性,并行开发根本就不可能。根据接口编程是实现这种灵活结构的核心。要了解其中原委,不妨看看如果不使用接口,会发生什么。看看下列代码:
  f()
  { LinkedList list = new LinkedList();
  //...
  g( list ); }
  g( LinkedList list )
  { list.add( ... );
  g2( list ) }
  现在假设出现了以下新的需求: 需要快速查询,于是LinkedList 就不管用了。需要用HashSet来取代它。在现有代码中,这种改变不是单单出现在某个地方,因为不但要修改 f(),还要修改g()(负责接收LinkedList参数)以及g()传递列表的任何对象。现在只要把代码改成以下这样,就有可能只要用新的HashSet()取代LinkedList(),就可以把链接表改成哈希表,而不需要其他任何改变。
  f()
  { Collection list = new LinkedList();
  //...
  g( list ); }
  g( Collection list )
  { list.add( ... );
  g2( list ) }
  
  耦合性过强
  
  实现继承面临的一个更重要问题就是耦合――程序的一部分对另一部分产生不应该有的依赖。全局变量就是个典型例子,它表明了为什么强耦合会带来麻烦。譬如说,如果更改了全局变量的类型,那么使用这个变量(即与该变量耦合)的所有函数都会受到影响,所以所有这些代码都必须经过检查、修改及重新测试。另外,使用这个变量的所有函数也通过该变量彼此耦合起来。也就是说,如果变量的值在不合适的时候出现变化,一个函数可能会不正确地影响另一个函数的行为。这个问题在多线程程序中显得尤其可怕。
  软件设计师应当想方设法尽量减少耦合。虽然不可能完全消除耦合,因为从一个类的对象到另一个类对象的方法调用就是一种松散耦合,世上不存在没有耦合的程序。不过,只要竭力遵守面向对象原则(最重要的一条原则就是,一个对象的实现应当完全隐藏起来,不让使用它的对象看见),就可以大大减少耦合。譬如说,一个对象的实例变量(不是常量的成员字段)应当是private属性。同样道理,千万不要使用set/get函数――它们只是让字段变成public的过于复杂的方式。
  现在,我们不妨把耦合概念应用到继承上。在使用extends的实现继承系统中,派生类与基类有着很紧密的耦合关系,而这种紧密关系是不该有的。设计师们称这种行为是“脆弱基类问题”(fragile base-class problem)。基类之所以被认为很脆弱,是因为可以通过一种看似安全的方法来修改基类,但这种新的行为被派生类继承后,可能会导致派生类功能失常。无法只是通过孤立地检查基类的方法来判断基类的改变是不是安全的,而必须查看及测试所有的派生类。另外,必须检查同时使用了基类对象和派生类对象的所有代码,因为这些代码可能也会被新的行为所破坏。对某个关键基类的小小改变就会导致整个程序无法运行。
  我们不妨一起分析一下脆弱基类和基类耦合这两个问题。下面的类扩展了Java的 ArrayList类,以便其行为类似堆栈:
  class Stack extends ArrayList
  { private int stack_pointer = 0;
   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 = 0; i high_water_mark )
  high_water_mark = current_size;
  super.push(article); }
   public Object pop()
   { --current_size;
  return super.pop();}
   public int maximum_size_so_far()
   { return high_water_mark;}
  }
  这个新的类运行良好,至少一段时间是这样。遗憾的是,代码利用了这一事实: push_many()通过调用push()来运行。一开始,这个细节反过来不是一个坏的选择。它简化了代码,还得到了push()的派生类版本,即使通过Stack的引用来访问Monitorable_stack时也是如此,所以high_water_mark可以正确更新。(译自《JavaWorld》)

猜你想看
相关文章

Copyright © 2008 - 2022 版权所有 职场范文网

工业和信息化部 备案号:沪ICP备18009755号-3