舉個慄子 在M和N兩種品牌的手機裡面各做通訊錄和游戲兩種軟體。前提:M和N手機之間不通用。 反例思路1 以手機品牌為主體,各自實現軟體。 反例思路2 以手機軟體為主體,各自適配品牌。 弊端說明 對象的繼承關係是在編譯時就定義好了,所以無法在運行時改變從父類繼承的實現。 子類的實現與它的父類有非常緊密 ...
舉個慄子
在M和N兩種品牌的手機裡面各做通訊錄和游戲兩種軟體。前提:M和N手機之間不通用。
反例思路1
以手機品牌為主體,各自實現軟體。
反例思路2
以手機軟體為主體,各自適配品牌。
弊端說明
- 對象的繼承關係是在編譯時就定義好了,所以無法在運行時改變從父類繼承的實現。
- 子類的實現與它的父類有非常緊密的依賴關係,以至於父類實現中的任何變化必然會導致子類發生變化。
- 當你需要復用子類時,如果繼承下來的實現不適合解決新的問題,則父類必須重寫或被其他更適合的類替換。
- 這種依賴關係限制了靈活性並最終限制了復用性。
引申:合成/聚合復用原則(CARP)
定義
即:儘量使用合成/聚合,儘量不要使用類繼承。
- 聚合表示一種弱的‘擁有’關係,體現的是A對象可以包含B對象,但B對象不是A對象的一部分;
- 合成則是一種強的‘擁有關係’,體現了嚴格的部分和整體的關係,部分和整體的生命周期一樣。
好處
優先使用對象的合成/聚合將有助於你保持每個類被封裝,並被集中在單個任務上。這樣的類和類繼承層次會保持較小的規模,並且不太可能增長為不可控制的龐然大物。
應用上述原則對例子進行改造
Talk is cheap, show me the code
(屁話少說,放碼過來)
/**
* 手機軟體
* Created by callmeDevil on 2019/12/14.
*/
public abstract class HandsetSoft {
public abstract void run();
}
/**
* 手機游戲
* Created by callmeDevil on 2019/12/14.
*/
public class HandsetGame extends HandsetSoft{
@Override
public void run() {
System.out.println("運行手機游戲");
}
}
/**
* 手機通訊錄
* Created by callmeDevil on 2019/12/14.
*/
public class HandsetAddressList extends HandsetSoft{
@Override
public void run() {
System.out.println("運行手機通訊錄");
}
}
/**
* 手機品牌
* Created by callmeDevil on 2019/12/14.
*/
public abstract class HandsetBrand {
protected HandsetSoft soft;
// 設置手機軟體
public void setHandsetSort(HandsetSoft sort){ // 品牌需要關註軟體,所以可在機器中安裝軟體,以備運行
this.soft = sort;
}
public abstract void run();
}
/**
* 手機品牌M
* Created by callmeDevil on 2019/12/14.
*/
public class HandsetBrandM extends HandsetBrand{
@Override
public void run() {
soft.run();
}
}
/**
* 手機品牌N
* Created by callmeDevil on 2019/12/14.
*/
public class HandsetBrandN extends HandsetBrand{
@Override
public void run() {
soft.run();
}
}
public class Test {
public static void main(String[] args) {
HandsetBrand ab;
System.out.println("---手機品牌N:");
ab = new HandsetBrandN();
ab.setHandsetSort(new HandsetGame());
ab.run();
ab.setHandsetSort(new HandsetAddressList());
ab.run();
System.out.println("---手機品牌M:");
ab = new HandsetBrandM();
ab.setHandsetSort(new HandsetGame());
ab.run();
ab.setHandsetSort(new HandsetAddressList());
ab.run();
}
}
運行結果
---手機品牌N:
運行手機游戲
運行手機通訊錄
---手機品牌M:
運行手機游戲
運行手機通訊錄
需求變更
增加一個MP3音樂播放功能,只需要添加一個類,其他無需改造
/**
* 手機MP3
* Created by callmeDevil on 2019/12/14.
*/
public class HandsetMP3 extends HandsetSoft{
@Override
public void run() {
System.out.println("運行手機MP3");
}
}
如果是增加一個品牌,也只需要添加一個類
/**
* 手機品牌S
* Created by callmeDevil on 2019/12/14.
*/
public class HandsetBrandS extends HandsetBrand{
@Override
public void run() {
soft.run();
}
}
橋接模式
定義
將抽象部分與它的實現部分分離,使它們都可以獨立的變化。
需要註意的是,實現分離,並不是說讓抽象類與其派生類分離,因為這沒有任何意義。實現指的是抽象類和它的派生類用來實現自己的對象。
UML圖
總結
- 實現系統可能有多角度的分類,每一種分類都有可能變化,那麼就把這種多角度分離出來讓他們獨立,減少他們之間的耦合。
- 只要真正深入的理解了設計原則,很多設計模式其實就是原則的應用而已,或許在不知不覺中就在使用設計模式了。