当前位置:编程学习 > JSP >>

合成 VS 继承

答案:昨儿看到一片比较 继承与合成 的文章,不错,翻译出来大家共享。
内容浅显易懂,相信你也能从中有所收获

BTW:翻译的不好不要骂啊,嘿嘿。。。



合成 VS 继承
关联class的两种基本途径的对比
作者:Bill Venners
出处:http://www.artima.com/designtechniques/compoinh.html


摘要
这是我的Design Techniques的一部分,这里我分析了两者的构成(flexibility)和执行牵连(performance implications),并且我针对两者分别给出了指导方针。

正文
建立两个类之间的关联是软件设计的众多基本行为之一。继承和合成是两种基本的实现方法。尽管当你使用继承的时候JVM可以帮你做很多事情,但是你仍然可以使用合成来达到同样的目的。本篇将比较这两种途径且给出使用它们的指导方针。
    首先,介绍继承和合成的背景

关于继承
    Class Fruit {......}
    Class Apple extends Fruit {......}

    Apple和Fruit通过extends关联起来,苹果是水果的一种。Fruit是Apple的supperclass,Apple是Fruit的subclass

关于合成
    class Fruit {......}

class Apple {
    private Fruit fruit = new Fruit();
    //......
}

    这里Apple和Fruit通过合成关联起来,因为Apple拥有一个引用Fruit对象的实例变量。Apple被称为front-end class,Fruit被称为 back-end class。

动态绑定,多态和改变
当你使用继承来关联两个类的时候,你就可以利用动态绑定和多态的好处了。

动态绑定和多态最主要的好处之一是,他们可以帮助你更简单的修改代码,包括添加新的子类。然而这些不能包括所有你需要改变的地方


修改superclass接口
在继承关联中,superclasses通常被称为“脆弱的(fragile)”,因为对superclass的一点点的改动将波及到众多应用程序的代码。说的更明白些,superclass最脆弱的是它的接口。如果superclass是well-designed的――良好的接口设计,OO风格的实现,那么任何supperclass的实现的改变将不会有任何影响。如果修改superclass的接口,那么将波及到任何使用该superclass的地方和其subclass。
    继承有时被成为提供“弱封装(week encapsulation)”,因为你直接使用subclass的地方都会受superclass接口的改变的影响。从某个角度来讲,继承是让subclass重用superclass的代码。


选择合成?
继承性的关联很难来修改superclass的接口。合成则提供了easier-to-change的途径。
通过继承的代码重用
  class Fruit {
    //返回切割后的份数
    public int peel() {
      System.out.println(“Peeling is appealing”);
      return 1;
    }

    //将上面替换掉的新方法
    public Peel peel() {
      return new Peel(1);  //另外一个类
    }
  }

  class Apple extends Fruit {
  }

  class Example1 {
    public static void main(String[] args) {
      Apple apple = new Apple();
      int pieces = apple.peel();   //这里受到影响
    }
  }

Example1中通过Apple来调用继承自Fruit的peel()方法,但是当我们需要将peel()的返回值从int修改为Peel的时候(上面红色部分),问题出现了,由于类型的不匹配(int peel)造成Example1的代码不能通过编译,虽然Example1并没有和Fruit有任何直接的关联,但还是受到了很大影响。

通过合成的代码重用
合成通过在Apple中保持一个Fruit对象的引用,在Apple中声明一个新的peel方法,内部实现只是简单的调用Fruit的peel方法。

  class Fruit {
    //返回切割后的份数
    public int peel() {
      System.out.println(“Peeling is appealing”);
      return 1;
    }

    //需求改变后,将上面方法修改后的新接口
    public Peel peel() {
      return new Peel(1);  //另外一个类
    }
  }

  class Apple {
    private Fruit fruit = new Fruit();

    public int peel() {
      return fruit.peel();
    
      //使用Fruit的新接口,用于取代上面一行代码
      Peel peel = fruit.peel();
      return peel.getPeelCount();
    }
  }

  class Example2 {
    public static void main(String[] args) {
      Apple apple = new Apple();
      int pieces = apple.peel();  //这里不会有任何影响
    }
  }

在合成实现方式中,subclass变为front-end class,superclass变为back-end class。使用继承,subclass自动继承了superclass的non-private方法;使用合成,front-end class必须在自身的实现中明确的调用back-end class中的相应的方法。这种直接调用有时被称为“推进(forwarding)”或“委派(delegating)”这个方法的调用到back-end对象。

合成比继承提供了更强壮的在代码重用方面的封装,因为back-end class的修改不会波及任何依赖front-end class的代码。例如我们要将Fruit的peel方法返回值修改为Peel(上面红色部分),同时你会看到Apple的peel方法有相应的改变,这时对Fruit接口的修改将不会影响到Example2的代码,

比较合成与继承
几点对比:
    back-end class(合成)比superclass(继承)更容易修改接口。就像前面举例说明的那样,back-end class接口的改变必将导致front-end class实现的改变,但不会影响到front-end class的接口,所以依赖front-end class的代码将正常工作。作为对比,superclass接口的修改不仅波及subclass层,也会影响到所有直接使用superclass接口以及使用subclass接口的地方
    front-end class(合成)比subclass(继承)更容易修改接口。正像superclass是脆弱(fragile)的,而subclass是坚硬(rigid)的。你不能只改变subclass的接口而不去确认新接口是否和父类型(supertypes)兼容。例如,你不能在subclass中添加一个与superclass方法同样特征但返回值类型不同的新方法。合成则允许你修改front-end class的接口,而不用关心back-class。
    合成允许你延迟back-end objects的创建,直到他们被需要的时候才创建,在front-end object的生命期内可以动态的改变back-end objects。对于继承来说,一旦subclass被创建了,你就可以通过subclass object来获取superclass的某些资源了,在subclass生命期内一直保持着superclass的对象,也就是说,subclass object一旦被创建,superclass就已知且不可改变了。
    添加subclasses(继承)比添加front-end class(合成)更容易。因为继承伴随多态。如果你的一些代码仅依赖superclass,那么不用任何修改,你就能够使用一个新的subclass。对于合成来说就不可以,除非你使用带有接口的合成(composition with interfaces)。合成和接口的共同使用将提供一个非常强大的设计工具。
    同使用subclass中从继承superclass来的方法实现相比,合成中的直接的方法调用委派经常(often)有性能损耗。Often的意思是说,因为性能依赖众多因素,比如JVM优化并执行程序的能力。
    对于合成和继承来说,修改任何class的实现都是简单的。实现改变引起的连锁反映被保留在同一个class


在合成和继承中作出选择
怎么作出选择呢?这里有一个指导方针来让我们趋向合成与继承中的其中一个


继承是is-a的关系
主要是说,继承应该只被用在“subclass is-a superclass”的时候。在上面例子中,Apple is-a Fruit,所以我们倾向使用继承。

当你认为已经有一个is-a关系的时候,你需要问自己 一个非常重要的问题,那就是这个“is-a 关系”是否在应用程序或代码生命周期中保持不变的(constant)。举例:当Employee在某个时间段扮演的角色是Person的时候,你可能认为 Employee is-a Person。如果Person被解雇会怎样?如果Person即是Employee又是Supervisor会怎样?这种暂时的is-a关系通常使用合成,而不是继承。


不要仅仅为了得到代码重用就使用继承
如果你的确想重用代码且没有观察到is-a关系,

上一个:Java中鲜为人知的缺点(上)
下一个:有需要SQL SERVER(oracle、sybase等) JDBC 驱动的到这个网址下吧..(老外的网站.)

CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,