每個初學者的理解不同,我加上自己的理解大體是這樣的:前提是處理大規模時,假設池塘中有10000頭小鴨子,有紅頭鴨,野鴨子,木頭鴨子等等,會叫會游泳,肯定是定義一個父類Duck,擁有這樣的基本行爲屬性;現在要求鴨子中400頭,可以飛,該怎麼做?
有句話叫:當你處理大規模時,一定要寫抽象層次編程。
問題:400只小鴨子可以飛,怎麼做?
1.最直接想到的就是 父類中定義fly()方法,全部的子鴨子去重寫,要飛的就子類自己實現飛這個行爲,不要飛的子類可以空實現。目的實現了,存在的問題:①全部子類都要改代碼 ②實現的fly方法 具體實現無論是不是空,都要在棧中保留方法的引用 而維護方法的指針需要成本尤其是大量時。 基於這兩點肯定不合適。
總結:通過繼承實現在父類中聲明的行爲,主要有以下缺點:
the followings are the disadvantages of using inheritance to provide Duck behavior
①Code is duplicated across subclasses (代碼在多個子類中重複)
② Runtime behavior changes are difficult (運行時行爲不易改變)
③Changes can unintentionally affect other ducks (改變會牽一髮而動全身,造成部分子類型不想要的改變)
2.定義一個FlyAble接口,然後定義400只需要飛的小鴨子實現這個接口,然後實現自己的方法,有沒有發現問題的所在,需要每一個子類都去實現這個接口,然後寫一樣的fly代碼;代碼如下:
public interface Flyable {
void fly();
}
public class RedHeadDuck extends Duck implements Flyable {
@Override
void display() {
System.out.println("紅頭的");
}
@Override
public void fly() {
System.out.println("要飛的");
}
}
圖示:
這就是“java的接口不具有實現代碼,實現接口無法達到代碼的複用”,也就意味着“當你需要修改某個行爲,你需要往下追蹤,並修改每一個定義了此行爲的類。” 這兩句話,可以細細體會,就是接口不具有代碼的實現,無法達到代碼的複用。
實現Flyable接口的方式到底有什麼壞處?
假設400個鴨子要能飛,那要400個鴨子實現這個接口,每個都重寫400次fly方法,然後定義飛的行爲,要是不同的鴨子有了高高飛,擦地飛,旋轉着飛,是不是,每個鴨子要寫不同的飛方法,比如
a =》高高飛
b =》擦地飛
c =》旋轉着飛
現在d e f g 也要旋轉飛 那肯定要每一個都寫旋轉飛的方法 這屬於“接口不具有實現代碼,所以實現接口無法達到代碼的複用”。
如果有專門的高高飛行爲類,擦地飛行爲類,旋轉着飛行爲類,都去實現flybehavior行爲接口,重寫fly表達行爲,那每次用的時候,就不用寫這麼多重複的代碼了。 而且當你想修改方法的時候只需要改一處就好了。
可以試想,將變化的內容定義形成接口可以實現變化內容和不變內容的剝離。 其接口的實現類可以實現變化內容的重用,即理解爲
這些實現類並非Duck.java的子類型,而是專門的一組實現類,稱之爲 行爲類。由行爲類而不是Duck.java的子類型實現接口。這樣才能保證變化的行爲獨立於不變的內容。
代碼如下:
public interface FlyBehavior {
void fly();
}
//具體的行爲類 用翅膀飛
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("==帶翅膀飛");
}
}
/**
* 父類
*/
public abstract class Duck {
FlyBehavior flyBehavior;
String color;
int age;
public void performFly() {
flyBehavior.fly();
}
void performSwim() {
System.out.println("==所有的鴨子都會游泳呢");
}
}
/**
* 紅頭鴨
*/
public class RedheadDuck extends Duck {
public RedheadDuck() {
flyBehavior = new FlyWithWings();
}
}
/**
* 測試類
*/
public class TestDuck {
public static void main(String[] args) {
System.out.println("=============紅頭鴨子=========");
showRedHeadDuck();
}
private static void showRedHeadDuck() {
Duck duck = new RedheadDuck();
duck.performFly();
}
}
這樣就實現了鴨子類和fly這一行爲的分離,兩着沒有了關係。以前的做法是:行爲是繼承自Duck超類的具體實現而來,或者實現某個接口並由子類自行實現而來。 這兩種方法都是依賴於實現。我們被實現綁的死死的,沒有辦法更改行爲(除非寫更多的代碼)。
這種方法和繼承的不同之處在於,小鴨子的行爲不是繼承而來,而是和適當的行爲對象結合。這是一個很重要的技巧,其實使用了策略模式中的第三個設計原則,多用組合 少用繼承。
當你處理大規模時 一定要寫抽象層次編程。
整個過程就是從is-a轉成has-a。