JavaRush /Java 博客 /Random-ZH /Java 中的继承与组合
dio
第 16 级
Москва

Java 中的继承与组合

已在 Random-ZH 群组中发布
本文阐述了 Java 中继承和组合的概念。第一个示例演示了继承,然后展示了如何使用组合来改进继承设计。我们将在最后总结如何在它们之间进行选择。 Java 中的继承与组合 - 1

1. 继承

假设我们有一个类Insect(英语昆虫) 该类包含两个方法:1. move()(来自英语 move)和 2. attack()(来自英语 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");
	}
}
现在您想要定义一个类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();
	}
}
类层次结构图很简单: Java 中的继承与组合 - 2执行结果:
Fly
Fly
Attack
“Fly”被输入两次,因此该方法move()被调用两次。但它应该只被调用一次。该问题是由 引起的super.attack()。该方法attack ()调用move()类方法Insect。当子类调用时super.attack (),它也会调用重写的方法move()。为了解决这个问题,我们可以:
  1. 消除attack()子类方法。这将使子类依赖于attack()超类的方法实现。如果attack()attack()超类开始使用不同的移动方法,则子类也需要更改。这是不好的封装。
  2. 重写该方法attack()如下:

    public void attack() {
    	move();
    	System.out.println("Attack");
    }
  3. 这保证了正确的结果,因为子类不再依赖于超类。然而,该代码是超类的重复。(该方法attack()做的事情比仅仅输出字符串更复杂)。这不是好的软件设计,不应该有重复的代码。

这种继承设计很糟糕,因为子类依赖于其超类的实现细节。如果超类发生变化,子类将无法正常工作。

2. 组成

您可以使用组合而不是继承。让我们看一下使用它的解决方案。函数attack()被抽象为接口。
interface Attack {
	public void move();
	public void 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);
	}
}
由于攻击函数是外部的,因此该类Insect不再包含它。
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;
	}
}
Class Bee(来自英语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();
	}
}
类图: Java 中的继承与组合 - 3
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”形式的类之间的关系,并且一个类想要向另一个类提供其所有接口,那么继承是更好的选择。
  2. 如果关系是“HAS”,则首选组合。
因此,继承和组合都有自己的应用,值得理解它们的优点。链接:
  1. 布洛赫、约书亚. 有效的java。印度培生教育,2008 年
  2. https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
  3. https://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should...
链接到原始文章 翻译
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION