在軟件系統中,某些類型因爲自身的邏輯,它具備兩個或多個維度的變化,那麼如何應對這種「多維度的變化」?如何利用面嚮對象的技術來使得該類型可以輕鬆的沿着多個方向進行變化,而又不引入額外的複雜度?這就要使用橋接模式。而具體使用的方式,則是將抽象部分與他們的實現部分分離,使得它們均可以獨立的變化。
手機,不一樣品牌的手機有着相似的功能,假設如今都具備通信錄和遊戲功能。或許咱們會這樣設計實現這樣的關係:ide
這種結構下若是增長一個音樂功能,每一個品牌類中都會增長一個新的音樂子類。若是新增一個品牌類,則須要新增一個新的品牌類,而且品牌類下還要對應增長各個功能的子類。
又或者是這個:
這種結構下新增品牌和功能一樣須要很大的改動。
上述狀況出現的主要緣由是:對象的繼承關係在編譯時就已經肯定,沒法在運行時改變從父類繼承的實現。子類的實現與他的父類有很是緊密的關係,以致於父類實現中的任何變化必然會致使子類的變化。當你須要複用子類時,父類的實現若是不適合解決新的問題,則父類必須重寫或被其餘更適合的類代替。這種依賴關係限制了靈活性並最終限制了複用性。
面向對象的設計中,還有一個重要的原則:合成/聚合複用原則---優先使用對象合成/聚合,而不是繼承。
聚合(Aggregation)表示一種弱的擁有關係,體現的是A對象能夠包含B對象,可是B不是A對象的一部分。合成(Composition)是一種強的擁有關係,體現了嚴格的部分和總體的關係,部分和總體的生命週期同樣。
好比鳥有兩隻翅膀,翅膀和鳥就是部分與總體的關係,而且他們的生命週期是相同的。這就是合成關係。
鳥羣中有多隻鳥,每隻鳥都屬於一個鳥羣,一個鳥羣中能夠擁有多個鳥,因此鳥羣和鳥的關係就是聚合關係。
合成/聚合複用原則的好處是優先使用對象的合成/聚合有助於保持各個類被封裝,並集中在單個任務上,這樣類和類繼承的層次會保持較小的規模,而且不太可能增加爲不可控制的龐然大物。
因此咱們如今的結構圖應該是這個樣子:測試
使用代碼描述以下:
首先是軟件的抽象接口:this
package com.hy.bridge; abstract class SoftWare { public abstract void run(); }
接下來是軟件的具體實現:設計
package com.hy.bridge; public class Camera extends SoftWare { @Override public void run() { System.out.println("照相機運行中..."); } }
package com.hy.bridge; public class Game extends SoftWare { @Override public void run() { System.out.println("魂鬥羅戰鬥中..."); } }
而後是手機品牌的抽象類及具體實現類:code
package com.hy.bridge; abstract class Brand { public abstract void run(); }
package com.hy.bridge; public class MotoRola extends Brand { @Override public void run() { System.out.println("生產了一個摩托羅拉手機"); } }
package com.hy.bridge; public class Nokia extends Brand { @Override public void run() { System.out.println("生產了一個諾基亞手機"); } }
接下來的是將兩個維度聚合起來的抽象手機類及其實現類:對象
package com.hy.bridge; public abstract class IPhone { protected Brand brand; protected SoftWare softWare; public Brand getBrand() { return brand; } public void setBrand(Brand brand) { this.brand = brand; } public SoftWare getSoftWare() { return softWare; } public void setSoftWare(SoftWare softWare) { this.softWare = softWare; } // 具體的實現交給實現部分處理 public void run() { softWare.run(); } // 具體的實現交給實現部分處理 public void showBrand() { brand.run(); } //抽象的方法,留給繼承實現 public abstract void setON(); }
package com.hy.bridge; public class Phone extends IPhone{ @Override public void setON() { System.out.println("手機開機..."); } }
最後給出測試類:繼承
package com.hy.bridge; public class Test { public static void main(String[] args) { IPhone phone = new Phone(); phone.setBrand(new Nokia()); phone.showBrand(); phone.setON(); phone.setSoftWare(new Game()); phone.run(); phone.setSoftWare(new Camera()); phone.run(); System.out.println("--------------"); phone.setBrand(new MotoRola()); phone.showBrand(); phone.setON(); phone.setSoftWare(new Game()); phone.run(); phone.setSoftWare(new Camera()); phone.run(); } }
如今若是新增其餘的功能與品牌只須要添加對應的實現而沒必要對其餘的類作修改。接口
最終他的類圖是這個樣子:
定義中 抽象與其實現分離,並非指抽象類和派生類分離,由於這並無意義,實現是指抽象類和實現部分用來實現本身的對象。
抽象部分和實現部分,一般意義下,應該指的是繼承體系中,接口相同而實現也相同的部分則爲抽象部分,而接口相同可是實現不一樣的部分則爲實現部分。
橋接模式的核心意圖就是讓抽象類和實現類各自獨立實現,各自進行變化避免影響其餘的實現。
或者說:實現的系統可能有多個角度分類,每一種分類都有可能變化,那麼就把多種角度分離出來讓它們獨立變化,減小他們之間的耦合。生命週期