继承VS组合

来源:互联网 发布:中国项目数据分析师网 编辑:程序博客网 时间:2024/04/29 21:24

本文展示了Java中继承和组合的概念。首先给出一个继承的例子,然后给出如何使用组合来改进继承的设计。最后总结了如何在继承和组合之间做选择。

1.继承

假设我们有一个Insect类。这个类包含两个方法:1move()和2attack()。

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 attackingSystem.out.println("Attack");

}

}

现在你打算定义一个Bee类,它是一种Insect,但是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()方法引起的。Insectattack()方法调用了move()方法。当子类调用super.attack()时,它同样调用被重写的move()方法。

要来修复这个问题,我们可以:

  1. 取消子类的attack()方法。这将使得子类依赖超类的attack()的实现。如果超类中的attack()方法在之后被改变了(这超出了你的控制),比如,超类的attack()方法使用了另一种方法来移动,子类也将需要被改动。这是一种坏的封装。
  2. 像下面一样重写attack()方法:

public void attack() {

move();

System.out.println("Attack");

}

这会保证正确的结果,因为子类不再依赖超类。然而,这个代码复制了超类的代码(想想如果attack()做很复杂的事而不仅仅只打印一个字符串)。这不符合软件工程的重用原则。


2.组合

代替继承,组合在这种情况下可以被使用。让我们首先看一下组合的解决方案。

 

Attach 函数被抽象为一个接口。

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;

} 

@Overridepublic void move() {

System.out.println(move);

} 

@Overridepublic 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;

 }

}

Bee是一种Insect,它可以攻击:

// 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关系,组合是可选的。

 

总的来说,继承和组合都有他们的使用,理解它们的相对优点是值得的。


参考:

1. Bloch, Joshua. Effective java. Pearson Education India, 2008.
2. http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
3. http://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should-you-choose-.html



原文:

http://www.programcreek.com/2014/05/inheritance-vs-composition-in-java/






0 0
原创粉丝点击