閱讀英文的能力對於程式設計師來說,是很重要的。這幾年也一直在學習英文,今天心血來潮,就在網上找了一篇簡短的博文翻譯一下。水平一般,能力有限,還請各位看官多多指點。
譯文:
本文將會舉例說明Java中繼承和組合的概念。首先舉一個繼承的例子,然後展示一下如何用組合來改善繼承的設計。最後概括一下如何在它們之間做出選擇。
1. 繼承
假設我們有一個Insect類。這個類包含兩個方法:一個是move(),一個是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(); } }
類層次結構關係圖就是如此簡單:
輸出:
Fly
Fly
Attack
"Fly"被列印了兩次,表示move()被呼叫了兩次。但是它應該被呼叫了一次才對。
問題出在super.attack()方法上。Insect的attack()方法呼叫move()方法。當子類呼叫super.attack()時,總是會呼叫重寫的move()方法。
我們可以用下面的方法解決這個問題:
- 去掉子類的attack()方法。這將使子類取決於父類attack()方法的實現,如果父類中的attack()方法發生改變(這是你無法控制的),例如:父類的attack()方法使用其他的方式來實現,子類也需要跟著改變,這不是好的設計。
- 重寫attack()方法,如下:
public void attack() { move(); System.out.println("Attack"); }
這樣能保證正確的結果,因為子類不再依賴於父類 。然而, 程式碼變成了一個父類的複製品。(想象一下,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); } }
將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. 何時用繼承,何時用組合?
下面兩條內容,可以告訴我們如何在繼承與組合之間做出選擇:
- 如果存在一個“是”的關係,並且一個類要對另一個類公開所有的介面,那麼繼承是更好的選擇
- 如果存在一個“有”的關係,那麼首選組合。
總之,繼承和組合都有其用途,和理解他們的優缺點是很有必要的。
最後說一點自己的感受吧,小弟自打初中開始學英語,成績就沒好過,最好成績也就剛及格吧。記得當年高考的時候lz的英語成績是55分(足以載入史冊的成績),我的英文水平有多差,大家可想而知了吧。後來承蒙恩師的諄諄教誨,一直沒有放棄英語的學習,現在依然每天都在學(雖然沒有掌握其精髓,但是學總比不學強)。以前遇到外國人根本張不開嘴,不知道說什麼,現在好多了,之前經常跟老外一起踢球,沒事瞎白話幾句,感覺也挺好玩的。以前看到英文的文章,直接關掉,現在也能靜下心來看下去了。
總之,學英語心態很重要,只要你不怕它,它就沒什麼好怕的。毛主席曾說過:“All the reactionaries are the Papertiger(一切反動派都是紙老虎)”。英語沒什麼好怕的,遇到老外你就跟他瞎扯唄,最不濟你倆打個平手——誰也聽不懂誰說什麼。還有更壞的結果嗎?不管怎樣我們都不會輸,那你還怕啥?看英文文章、書籍看不懂,那就更不用怕了,大不了弄個詞典唄,我大有道在手,還怕治不了你個小英文了。別猶豫了,上吧,少年!
原文連結: Inheritance vs. Composition in Java