JavaRush /Java Blog /Random-TW /Java 中的繼承與組合
dio
等級 16
Москва

Java 中的繼承與組合

在 Random-TW 群組發布
本文闡述了 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