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

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

1.继承

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

class Insect {
private int size;
private String color; public Insect(int size, String color) {
this.size = size;
this.color = color;
} public int getSize() {
return size;
} public void setSize(int size) {
this.size = size;
} public String getColor() {
return color;
} public void setColor(String color) {
this.color = color;
} public void move() {
System.out.println("Move");
} public void attack() {
move(); //assuming an insect needs to move before attacking
System.out.println("Attack");
}
}

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

class Bee extends Insect {
public Bee(int size, String color) {
super(size, color);
} public void move() {
System.out.println("Fly");
} public void attack() {
move();
super.attack();
}
}
public class InheritanceVSComposition {
public static void main(String[] args) {
Insect i = new Bee(1, "red");
i.attack();
}
}

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

输出:

Fly
Fly
Attack

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

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

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

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

public void attack() {
move();
System.out.println("Attack");
}

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

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

2.组合

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

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

interface Attack {
public void move();
public void attack();
}

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

class AttackImpl implements Attack {
private String move;
private String attack; public AttackImpl(String move, String attack) {
this.move = move;
this.attack = attack;
} @Override
public void move() {
System.out.println(move);
} @Override
public void attack() {
move();
System.out.println(attack);
}
}

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

class Insect {
private int size;
private String color; public Insect(int size, String color) {
this.size = size;
this.color = color;
} public int getSize() {
return size;
} public void setSize(int size) {
this.size = size;
} public String getColor() {
return color;
} public void setColor(String color) {
this.color = color;
}
}

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

// This wrapper class wrap an Attack object
class Bee extends Insect implements Attack {
private Attack attack; public Bee(int size, String color, Attack attack) {
super(size, color);
this.attack = attack;
} public void move() {
attack.move();
} public void attack() {
attack.attack();
}
}

类的结构图:

public class InheritanceVSComposition2 {
public static void main(String[] args) {
Bee a = new Bee(1, "black", new AttackImpl("fly", "move"));
a.attack(); // if you need another implementation of move()
// there is no need to change Insect, we can quickly use new method to attack Bee b = new Bee(1, "black", new AttackImpl("fly", "sting"));
b.attack();
}
}
fly
move
fly
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. [linux]scp指令(转)

    Linux scp命令用于Linux之间复制文件和目录,具体如何使用这里好好介绍一下,从本地复制到远程.从远程复制到本地是两种使用方式.这里有具体举例: ================== Linu ...

  2. dom 规划(html和xml)

    html dom与xml dom关联: 什么是 DOM? DOM 是 W3C(万维网联盟)的标准. DOM 定义了訪问 HTML 和 XML 文档的标准: "W3C 文档对象模型 (DOM) ...

  3. 使用exchange普通表模式被切换到分区表

    随着数据库的不断增长的数据量.有些表需要转换的普通堆表分区表模式. 有几种不同的方式来执行此操作,如出口数据表,区表再导入数据到分区表:使用EXCHANGE PARTITION方式来转换为分区表以及使 ...

  4. pydev-python 链接mysql数据库(mac系统)

    1.首先,实现了命令行可以运行mysql          非常清楚了,直接引用过来,多谢哈.引用:http://www.lihui.info/mac-pydev-mysqldb/           ...

  5. Java NIO系列教程(三) Buffer(转)

    Java NIO中的Buffer用于和NIO通道进行交互.如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的. 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO ...

  6. JDK的dt.jar和Java BeanInfo接口

    在JAVA_HOME/lib以下有两个比較重要的jar文件.tools.jar和dt.jar. tools.jar在上篇文章中做了简单的介绍.这里来介绍下dt.jar. 在Oracle官方站点搜dt. ...

  7. BZOJ 2878([Noi2012]-失落的游乐园树DP+出站年轮加+后市展望DP+vector的erase)

    2878: [Noi2012]迷失乐园 Time Limit: 10 Sec  Memory Limit: 512 MBSec  Special Judge Submit: 319  Solved:  ...

  8. [Android]BaseExpandableListAdapter实现可折叠列表

    使用BaseExpandableListAdapter 以实现的可折叠的所谓列表,例如QQ朋友们在分组功能. 基于BaseExpandableListAdapter扩大ExpandableList说明 ...

  9. JavaScript之事件处理详解

    一.事件传播机制 客户端JavaScript程序(就是浏览器啦)采用了异步事件驱动编程模型.当文档.浏览器.元素或与之相关的对象发生某些有趣的事情时,Web浏览器就会产生事件(event).如果Jav ...

  10. wsdl生成的客户端

    首先, 你要先把你的WS服务启动起来,就是 比如ht tp:/ /localhost:8080/Example/services/HelloWorldService?wsdl然后在你的另一个项目中建一 ...