(文章翻译自Inheritance vs. Composition in Java)

这篇文章阐述了Java中继承和组合的概念。它首先给出了一个继承的例子然后指出怎么通过组合来提高继承的设计。最后总结出怎么在两者中作出选择。

1.继承

现在假设我们有一个Insect类。这个类包含了两个方法:1) move() and 2) attack().

  1. class Insect {
  2. private int size;
  3. private String color;
  4. public Insect(int size, String color) {
  5. this.size = size;
  6. this.color = color;
  7. }
  8. public int getSize() {
  9. return size;
  10. }
  11. public void setSize(int size) {
  12. this.size = size;
  13. }
  14. public String getColor() {
  15. return color;
  16. }
  17. public void setColor(String color) {
  18. this.color = color;
  19. }
  20. public void move() {
  21. System.out.println("Move");
  22. }
  23. public void attack() {
  24. move(); //assuming an insect needs to move before attacking
  25. System.out.println("Attack");
  26. }
  27. }

现在你想定义一个Insect类型的Bee类,但是对于方法attack() 和move()有不同的实现。这个可以向下面这样通过继承设计来先实现:

  1. class Bee extends Insect {
  2. public Bee(int size, String color) {
  3. super(size, color);
  4. }
  5. public void move() {
  6. System.out.println("Fly");
  7. }
  8. public void attack() {
  9. move();
  10. super.attack();
  11. }
  12. }
  1. public class InheritanceVSComposition {
  2. public static void main(String[] args) {
  3. Insect i = new Bee(1, "red");
  4. i.attack();
  5. }
  6. }

类的继承图就像下面这么简单:

输出:

  1. Fly
  2. Fly
  3. Attack

Fly被打印出了两次,这就表明move()方法被调用了两次。但是它只应该被调用一次。这个问题是因为super.attack()方法引起的。Insect的attack()方法调用了move()方法。当一个子类调用super.attack()方法的时候,它也会调用覆盖的move()方法。

为了修复这个问题,我们可以:

1.消除子类的attack()方法。这个将使子类依赖超类的attack()的实现。如果在超类中的attack()方法在后来被改变了,例如超类的attack()方法使用另外一个方法去移动,那么子类也需要去改变了。这是一个不好的设计。

2.像下面这样重写attack()方法:

  1. public void attack() {
  2. move();
  3. System.out.println("Attack");
  4. }

这将会保证结果的正确性,因为子类将不再依赖超类了。但是,代码重复了超类,这个不符合软件工程中复用的规则。

这个继承设计是不好的,因为子类依赖超类的实现。如果超类改变了那么结果就会发生改变。

2.组合

组合可以代替继承在这个场景中使用。让我们来看下组合的解决方式。

attack()方法作为接口中一个抽象的方法。

  1. interface Attack {
  2. public void move();
  3. public void attack();
  4. }

不同种类的attack可以用实现Attack接口而被重新定义。

  1. class AttackImpl implements Attack {
  2. private String move;
  3. private String attack;
  4. public AttackImpl(String move, String attack) {
  5. this.move = move;
  6. this.attack = attack;
  7. }
  8. @Override
  9. public void move() {
  10. System.out.println(move);
  11. }
  12. @Override
  13. public void attack() {
  14. move();
  15. System.out.println(attack);
  16. }
  17. }

因为attack方法被提取出来了,Insect就不在和attack有任何关系了。

  1. class Insect {
  2. private int size;
  3. private String color;
  4. public Insect(int size, String color) {
  5. this.size = size;
  6. this.color = color;
  7. }
  8. public int getSize() {
  9. return size;
  10. }
  11. public void setSize(int size) {
  12. this.size = size;
  13. }
  14. public String getColor() {
  15. return color;
  16. }
  17. public void setColor(String color) {
  18. this.color = color;
  19. }
  20. }

Beeb是Insect类型,它可以有attack方法>

  1. // This wrapper class wrap an Attack object
  2. class Bee extends Insect implements Attack {
  3. private Attack attack;
  4. public Bee(int size, String color, Attack attack) {
  5. super(size, color);
  6. this.attack = attack;
  7. }
  8. public void move() {
  9. attack.move();
  10. }
  11. public void attack() {
  12. attack.attack();
  13. }
  14. }

类的结构图:

  1. public class InheritanceVSComposition2 {
  2. public static void main(String[] args) {
  3. Bee a = new Bee(1, "black", new AttackImpl("fly", "move"));
  4. a.attack();
  5. // if you need another implementation of move()
  6. // there is no need to change Insect, we can quickly use new method to attack
  7. Bee b = new Bee(1, "black", new AttackImpl("fly", "sting"));
  8. b.attack();
  9. }
  10. }
  1. fly
  2. move
  3. fly
  4. sting

3.什么时候应该选择哪种方法呢?

下面两个场景可以指导在继承和组合总作出选择:

1.如果是IS-A关系,而且一个类想把它的所有接口暴露给另外一个类,那么应该选择继承。

2.如果是HAS-A关系,那么应该选择组合。

总而言之,继承和组要都有他们的用途,需要了解他们相对的优势。

[译]Java中的继承 VS 组合的更多相关文章

  1. 关于Java中的继承和组合的一个错误使用的例子

    [TOC] 关于Java中的继承和组合的一个错误使用的例子 相信绝大多数人都比较熟悉Java中的「继承」和「组合」这两个东西,本篇文章就主要就这两个话题谈论一下.如果我某些地方写的不对,或者比较幼稚, ...

  2. <Java中的继承和组合之间的联系和区别>

    //Java中的继承和组合之间的联系和区别 //本例是继承 class Animal { private void beat() { System.out.println("心胀跳动...& ...

  3. Java中的继承与组合(转载)

    本文主要说明Java中继承与组合的概念,以及它们之间的联系与区别.首先文章会给出一小段代码示例,用于展示到底什么是继承.然后演示如何通过“组合”来改进这种继承的设计机制.最后总结这两者的应用场景,即到 ...

  4. Java中的继承与组合

    本文主要说明Java中继承与组合的概念,以及它们之间的联系与区别.首先文章会给出一小段代码示例,用于展示到底什么是继承.然后演示如何通过“组合”来改进这种继承的设计机制.最后总结这两者的应用场景,即到 ...

  5. 菜鸟译文(一)——Java中的继承和组合

    阅读英文的能力对于程序员来说,是很重要的.这几年也一直在学习英文,今天心血来潮,就在网上找了一篇简短的博文翻译一下.水平一般,能力有限,还请各位看官多多指点. 译文: 本文将会举例说明Java中继承和 ...

  6. java中的继承与oc中的继承的区别

    为什么要使用继承? 继承的好处: (1)抽取出了重复的代码,使代码更加灵活 (2)建立了类和类之间的联系 继承的缺点: 耦合性太强 OC中的继承 1.OC中不允许子类和父类拥有相同名称的成员变量名:( ...

  7. Java中的继承

    我们在以前的学习中,我们会了C#中的继承,今天我们来了解了解Java中的继承,其实都大同小异啦! 1.语法 修饰符 SubClass extends SuperClass(){ //类定义部分 } e ...

  8. extends:类似于java中的继承特征,extends="struts-default"

    extends:类似于java中的继承特征,extends="struts-default"就是继承struts-default.xml,它里面定义了许多跳转类型.拦截器等一些常用 ...

  9. 关于java中的继承

    我们都知道Java中的继承是复用代码.扩展子类的一种方式,继承使得Java中重复的代码能够被提取出来供子类共用,对于Java程序的性能以及修改和扩展有很大的意义,所以这是一个非常重要的知识点. 那么对 ...

随机推荐

  1. Android 获取屏幕大小和密度

    Android 获取屏幕大小和密度 DisplayMetrics metric = new DisplayMetrics(); getWindowManager().getDefaultDisplay ...

  2. Java里泛型有什么作用

    1 泛型赋予了类型參数式多态的能力 2 泛型的第一个优点是编译时的严格类型检查,提高了程序的安全性和健壮性,这是集合框架最重要的特点. 3 泛型消除了绝大多数的类型转换.假设没有泛型,当你使用集合框架 ...

  3. Facebook 网页应用图文设置教程

    最近在弄一个项目,需要使用Facebook进行登陆并且获取用户Facebook相关的数据.网上查找有关Facebook应用设置教程,中文资料中,要么介绍的是N版之前的API,要么是App端的教程.Fa ...

  4. Intent常用使用汇总

    方法一:调用默认的短信程序Intent intent = new Intent(Intent.ACTION_VIEW);intent.setType("vnd.android-dir/mms ...

  5. java jdk缓存-128~127的Long与Integer

    先推断下以下代码的输出结果 Qa:----------------------------------------------           Long a = Long.valueOf(127) ...

  6. Pro Aspnet MVC 4读书笔记(5) - Essential Tools for MVC

    Listing 6-1. The Product Model Class using System; using System.Collections.Generic; using System.Li ...

  7. VARCHAR2 他们占几个字节? NLS_LENGTH_SEMANTICS,nls_language

    ORACLE初始化参数:NLS_LENGTH_SEMANTICS 初始化參数NLS_LENGTH_SEMANTICS用于指定CHAR列或VARCHAR2列的长度定义方式,默认值为BYTE. 当设置该參 ...

  8. Javascript DOM编程艺术

    Chapter 0 为什么读这本书?作为js入门书,补基础,由于本书代码demo较简单,并没有贴代码,只记录一些自己要注意的知识点以及代码脚本 Chapter 1: javascript简史 DOM全 ...

  9. http://blog.jobbole.com/50603/#comment-153933

    http://blog.jobbole.com/50603/#comment-153933

  10. thinkphp学习笔记2—入口文件

    原文:thinkphp学习笔记2-入口文件 在thinkphp中有两个入口文件,一个是项目的入口文件,是index.php在主目录里面,还有一个是thinkphp框架的的入口文件,放在框架目录下面如: ...