問題引入
現(xiàn)在對不同手機類型的不同品牌實現(xiàn)操作編程(比如:開機、關(guān)機、上網(wǎng),打電話等),如圖
【對應(yīng)類圖】
【分析】
- 擴展性問題(類爆炸),如果我們再增加手機的樣式(旋轉(zhuǎn)式),就需要增加各個品牌手機的類,同樣如果我們增加一個手機品牌,也要在各個手機樣式類下增加。
- 違反了單一職責(zé)原則,當(dāng)我們增加手機樣式時,要同時增加所有品牌的手機,這樣增加了代碼維護成本
- 解決方案:使用橋接模式
介紹
基礎(chǔ)介紹
- 橋接模式(Bridge模式)是指: 將實現(xiàn)與抽象放在兩個不同的類層次中,使兩個層次可以獨立改變。
- Bridge 的意思是“橋梁”。就像在現(xiàn)實世界中,橋梁的功能是將河流的兩側(cè)連接起來一樣Bridge 模式的作用也是將兩樣?xùn)|西連接起來,它們分別是類的功能層次結(jié)構(gòu)和類的實現(xiàn)層次結(jié)構(gòu)。
- 是一種結(jié)構(gòu)型設(shè)計模式
- Bridge模式基于
類的最小設(shè)計原則
(實現(xiàn)功能的同時,讓類盡可能少),通過使用封裝、聚合及繼承等行為讓不同的類承擔(dān)不同的職責(zé)。它的主要特點是把抽象(Abstraction)與行為實現(xiàn)(lmplementation)分離開來,從而可以保持各部分的獨立性以及應(yīng)對他們的功能擴展
類的功能層次結(jié)構(gòu)
假設(shè)現(xiàn)在有一個類Something。當(dāng)我們想在Something中增加新功能時(想增加一個具體方法時),會編寫一個Something類的子類(派生類),如SomethingGood類。這樣就構(gòu)成了一個小小的類層次結(jié)構(gòu)。
父類本身具備一些基本功能,子類在繼承父類的功能之外,還可以添加新的功能。這種層次結(jié)構(gòu)被稱為“類的功能層次結(jié)構(gòu)”。如果需要在SomethingGood的基礎(chǔ)上繼續(xù)增加新的功能,再寫SomethingGood的子類即可。
注意:類的層次結(jié)構(gòu)關(guān)系不應(yīng)當(dāng)過深
類的實現(xiàn)層次結(jié)構(gòu)
抽象類聲明了一些抽象方法,定義了接口(API),然后子類負責(zé)去實現(xiàn)這些抽象方法。父類的任務(wù)是通過聲明抽象方法的方式定義接口(API),而子類的任務(wù)是實現(xiàn)抽象方法。正是由于父類和子類的這種任務(wù)分擔(dān),我們才可以編寫出具有高可替換性的類。
當(dāng)子類Concreteclass實現(xiàn)了父類Abstractclass類的抽象方法時,它們之間就構(gòu)成了一個小小的層次結(jié)構(gòu)。
這種類的層次結(jié)構(gòu)(類的實現(xiàn)層次結(jié)構(gòu))并非用于增加功能,并不方便我們增加新的方法。它的真正作用是幫助我們實現(xiàn)下面這樣的任務(wù)分擔(dān)
- 類通過聲明抽象方法來定義接口(API)
- 子類通過實現(xiàn)具體方法來實現(xiàn)接口(API)
一個抽象類可以有多種子實現(xiàn)類
層次結(jié)構(gòu)分離
當(dāng)類的層次結(jié)構(gòu)只有一層時,功能層次結(jié)構(gòu)與實現(xiàn)層次結(jié)構(gòu)是混雜在一個層次結(jié)構(gòu)中的。這樣很容易使類的層次結(jié)構(gòu)變得復(fù)雜,也難以透徹地理解類的層次結(jié)構(gòu)。因為自己難以確定究竟應(yīng)該在類的哪一個層次結(jié)構(gòu)中去增加子類。
因此,我們需要將“類的功能層次結(jié)構(gòu)”與“類的實現(xiàn)層次結(jié)構(gòu)”分離為兩個獨立的類層次結(jié)構(gòu)。當(dāng)然,如果只是簡單地將它們分開,兩者之間必然會缺少聯(lián)系。所以我們還需要在它們之間搭建一座橋梁,這就是橋接模式。
類圖
抽象類和接口是聚合關(guān)系,也是調(diào)用和被調(diào)用關(guān)系
應(yīng)用場景
對于那些不希望使用繼承或因為多層次繼承導(dǎo)致系統(tǒng)類的個數(shù)急劇增加的系統(tǒng),橋接模式尤為適用,如以下場景:
- JDBC驅(qū)動程序
- 銀行轉(zhuǎn)賬系統(tǒng)
- 轉(zhuǎn)賬分類: 網(wǎng)上轉(zhuǎn)賬,柜臺轉(zhuǎn)賬,AMT轉(zhuǎn)賬
- 轉(zhuǎn)賬用戶類型: 普通用戶,銀卡用戶,金卡用戶
- 消息管理
- 消息類型: 即時消息,延時消息
- 消息分類: 手機短信,郵件消息,QQ消息…
案例
案例一
類圖
代碼實現(xiàn)
【接口】
package com.atguigu.bridge;
/**
* 接口
*/
public interface Brand {
void open();
void close();
void call();
}
【接口實現(xiàn)類:小米手機】
package com.atguigu.bridge;
public class XiaoMi implements Brand {
@Override
public void open() {
System.out.println(" 小米手機開機 ");
}
@Override
public void close() {
System.out.println(" 小米手機關(guān)機 ");
}
@Override
public void call() {
System.out.println(" 小米手機打電話 ");
}
}
【接口實現(xiàn)類:Vivo手機】
package com.atguigu.bridge;
public class Vivo implements Brand {
@Override
public void open() {
System.out.println(" Vivo手機開機 ");
}
@Override
public void close() {
System.out.println(" Vivo手機關(guān)機 ");
}
@Override
public void call() {
System.out.println(" Vivo手機打電話 ");
}
}
【抽象類】
package com.atguigu.bridge;
public abstract class Phone {
/**
* 聚合品牌
*/
private Brand brand;
/**
* 構(gòu)造器
* @param brand
*/
public Phone(Brand brand) {
super();
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
this.brand.close();
}
protected void call() {
this.brand.call();
}
}
【抽象類子類:折疊手機】
package com.atguigu.bridge;
/**
* 折疊式手機類,繼承 抽象類 Phone
*/
public class FoldPhone extends Phone {
//構(gòu)造器
public FoldPhone(Brand brand) {
super(brand);
}
public void open() {
System.out.println(" 折疊樣式手機 ");
// 實際上調(diào)用的是具體品牌(如Xiaomi)的開機方法,抽象類Phone充當(dāng)橋接作用
super.open();
}
public void close() {
System.out.println(" 折疊樣式手機 ");
super.close();
}
public void call() {
System.out.println(" 折疊樣式手機 ");
super.call();
}
}
【抽象類子類:直立手機】
package com.atguigu.bridge;
public class UpRightPhone extends Phone {
//構(gòu)造器
public UpRightPhone(Brand brand) {
super(brand);
}
public void open() {
System.out.println(" 直立樣式手機 ");
super.open();
}
public void close() {
System.out.println(" 直立樣式手機 ");
super.close();
}
public void call() {
System.out.println(" 直立樣式手機 ");
super.call();
}
}
【客戶端】
package com.atguigu.bridge;
public class Client {
public static void main(String[] args) {
//獲取折疊式手機 (樣式 + 品牌 才是具體的手機)
Phone phone1 = new FoldPhone(new XiaoMi());
phone1.open();
phone1.call();
phone1.close();
System.out.println("==============");
Phone phone2 = new FoldPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();
System.out.println("==============");
UpRightPhone phone3 = new UpRightPhone(new XiaoMi());
phone3.open();
phone3.call();
phone3.close();
System.out.println("==============");
UpRightPhone phone4 = new UpRightPhone(new Vivo());
phone4.open();
phone4.call();
phone4.close();
}
}
【運行】
折疊樣式手機
小米手機開機
折疊樣式手機
小米手機打電話
折疊樣式手機
小米手機關(guān)機
==============
折疊樣式手機
Vivo手機開機
折疊樣式手機
Vivo手機打電話
折疊樣式手機
Vivo手機關(guān)機
==============
直立樣式手機
小米手機開機
直立樣式手機
小米手機打電話
直立樣式手機
小米手機關(guān)機
==============
直立樣式手機
Vivo手機開機
直立樣式手機
Vivo手機打電話
直立樣式手機
Vivo手機關(guān)機
Process finished with exit code 0
分析
- 無論增加一個樣式或者增加一個新的手機品牌,都只需要增加一個類
案例二
類圖
Display使用DispalyImpl的方法來完成功能,StringDisplayImpl負責(zé)DispalyImpl方法的具體實現(xiàn),CountDisplay用來完成更多的功能
代碼實現(xiàn)
【類的功能層次結(jié)構(gòu):Display】
package com.atguigu.bridge.Sample;
/**
* 類的功能層次最上層
*/
public class Display {
/**
* 實現(xiàn)了Display類的具體功能的實例 (橋梁)
*/
private DisplayImpl impl;
public Display(DisplayImpl impl) {
this.impl = impl;
}
/**
* 顯示前的處理
*/
public void open() {
impl.rawOpen();
}
/**
* 顯示處理
*/
public void print() {
impl.rawPrint();
}
/**
* 顯示后的處理
*/
public void close() {
impl.rawClose();
}
/**
* 調(diào)用上面的三個方法來進行顯示
*/
public final void display() {
open();
print();
close();
}
}
? private DisplayImpl impl;
使用了“委托”關(guān)系,而不是使用“繼承”關(guān)系。繼承是強關(guān)聯(lián)關(guān)系,委托是弱關(guān)聯(lián)關(guān)系。使用委托更方便擴展。
【類的功能層次結(jié)構(gòu):CountDisplay】
package com.atguigu.bridge.Sample;
/**
* 在Display類的基礎(chǔ)上增加新功能
*/
public class CountDisplay extends Display {
public CountDisplay(DisplayImpl impl) {
super(impl);
}
/**
* 循環(huán)顯示times次
*
* @param times
*/
public void multiDisplay(int times) {
open();
for (int i = 0; i < times; i++) {
print();
}
close();
}
}
【類的實現(xiàn)層次結(jié)構(gòu):上層】
package com.atguigu.bridge.Sample;
/**
* 類的實現(xiàn)層次最上層
*/
public abstract class DisplayImpl {
public abstract void rawOpen();
public abstract void rawPrint();
public abstract void rawClose();
}
【類的實現(xiàn)層次結(jié)構(gòu):下層】
package com.atguigu.bridge.Sample;
/**
* 類的實現(xiàn)層次結(jié)構(gòu),具體實現(xiàn)類
*/
public class StringDisplayImpl extends DisplayImpl {
/**
* 要顯示的字符串
*/
private String string;
/**
* 以字節(jié)單位計算出的字符串的寬度
*/
private int width;
/**
* 構(gòu)造方法
*
* @param string 構(gòu)造函數(shù)接收要顯示的字符串string
*/
public StringDisplayImpl(String string) {
// 將它保存在字段中
this.string = string;
// 把字符串的寬度也保存在字段中,以供使用
this.width = string.getBytes().length;
}
public void rawOpen() {
printLine();
}
public void rawPrint() {
// 前后加上"|"并顯示
System.out.println("|" + string + "|");
}
public void rawClose() {
printLine();
}
private void printLine() {
// 顯示用來表示方框的角的"+"
System.out.print("+");
// 顯示width個"-"
for (int i = 0; i < width; i++) {
// 將其用作方框的邊框
System.out.print("-");
}
// 顯示用來表示方框的角的"+"
System.out.println("+");
}
}
【主類】
package com.atguigu.bridge.Sample;
public class Main {
public static void main(String[] args) {
Display d1 = new Display(new StringDisplayImpl("Hello, China."));
Display d2 = new CountDisplay(new StringDisplayImpl("Hello, World."));
CountDisplay d3 = new CountDisplay(new StringDisplayImpl("Hello, Universe."));
d1.display();
System.out.println();
d2.display();
System.out.println();
d3.display();
d3.multiDisplay(5);
}
}
【運行】
+-------------+
|Hello, China.|
+-------------+
+-------------+
|Hello, World.|
+-------------+
+----------------+
|Hello, Universe.|
+----------------+
+----------------+
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
+----------------+
Process finished with exit code 0
拓展一
在上述示例程序中增加一個類,實現(xiàn)“顯示字符串若干(隨機)次”的功能。
【新增類】
package com.atguigu.bridge.A1;
import java.util.Random;
public class RandomCountDisplay extends CountDisplay {
private Random random = new Random();
public RandomCountDisplay(DisplayImpl impl) {
super(impl);
}
public void randomDisplay(int times) {
multiDisplay(random.nextInt(times));
}
}
【主類】
package com.atguigu.bridge.A1;
public class Main {
public static void main(String[] args) {
RandomCountDisplay d = new RandomCountDisplay(new StringDisplayImpl("Hello, China."));
d.randomDisplay(10);
}
}
擴展二
在上述示例程序中增加一個類,實現(xiàn)“顯示文本文件的內(nèi)容”的功能。
【新增類】
package com.atguigu.bridge.A2;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileDisplayImpl extends DisplayImpl {
private String filename;
private BufferedReader reader;
/**
* 循環(huán)顯示的極限(緩存大小限制)
*/
private final int MAX_READAHEAD_LIMIT = 4096;
public FileDisplayImpl(String filename) {
this.filename = filename;
}
public void rawOpen() {
try {
reader = new BufferedReader(new FileReader(filename));
reader.mark(MAX_READAHEAD_LIMIT);
} catch (IOException e) {
e.printStackTrace();
}
// 裝飾框
System.out.println("=-=-=-=-=-= " + filename + " =-=-=-=-=-=");
}
public void rawPrint() {
try {
String line;
reader.reset(); // 回到mark的位置
while ((line = reader.readLine()) != null) {
System.out.println("> " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void rawClose() {
// 裝飾框
System.out.println("=-=-=-=-=-= ");
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
【主類】
package com.atguigu.bridge.A2;
public class Main {
public static void main(String[] args) {
CountDisplay d = new CountDisplay(new FileDisplayImpl("star.txt"));
d.multiDisplay(3);
}
}
登場角色
- Abstraction(抽象化):位于
類的功能層次結(jié)構(gòu)
的最上層。它使用Implementor角色的方法定義了基本的功能。該角色中保存了Implementor 角色的實例 - RefinedAbstraction(改善后的抽象化):在Abstraction角色的基礎(chǔ)上增加了新功能
- Implementor(實現(xiàn)者):位于“類的實現(xiàn)層次結(jié)構(gòu)”的最上層。它定義了用于實現(xiàn)Abstraction 角色的接口(API)的方法
- Concretelmplementor( 具體實現(xiàn)者):負責(zé)實現(xiàn)在Implementor 角色中定義的接口(API)
文章來源:http://www.zghlxwxcb.cn/news/detail-608637.html
橋接模式在JDBC源碼中的應(yīng)用
文章來源地址http://www.zghlxwxcb.cn/news/detail-608637.html
總結(jié)
- 實現(xiàn)了抽象和實現(xiàn)部分的分離,從而極大的
提高了系統(tǒng)的靈活性
,讓抽象部分和實現(xiàn)部分獨立開來,這有助于系統(tǒng)進行分層設(shè)計,從而產(chǎn)生更好的結(jié)構(gòu)化系統(tǒng) - 分離之后代碼的擴展性更強,當(dāng)想要增加功能時,只需要在“類的功能層次結(jié)構(gòu)”一側(cè)增加類即可,不必對“類的實現(xiàn)層次結(jié)構(gòu)”做任何修改。而且,增加后的功能可以被“所有的實現(xiàn)”使用(例如,我們可以將“類的功能層次結(jié)構(gòu)”應(yīng)用于軟件所運行的操作系統(tǒng)上。如果我們將某個程序中依賴于操作系統(tǒng)的部分劃分為 Windows版、Macintosh 版、Unix 版,那么我們就可以用 Bridge模式中的“類的實現(xiàn)層次結(jié)構(gòu)”來表現(xiàn)這些依賴于操作系統(tǒng)的部分。也就是說,我們需要編寫一個定義這些操作系統(tǒng)的共同接口(API)的Implementor角色,然后編寫Windows版、Macintosh版Unix版的3個Concretelmplementor角色。這樣一來,無論在“類的功能層次結(jié)構(gòu)”中增加多少個功能,它們都可以工作于這3個操作系統(tǒng)上。)
- 對于系統(tǒng)的高層部分,只需要知道抽象部分和實現(xiàn)部分的接口就可以了,其它的部分由具體業(yè)務(wù)來完成
-
橋接模式替代多層繼承方案
,可以減少子類的個數(shù),降低系統(tǒng)的管理和維護成本 - 橋接模式的引入增加了系統(tǒng)的理解和設(shè)計難度(較難分析出哪些是抽象層,哪些是實現(xiàn)層),由于聚合關(guān)聯(lián)關(guān)系建立在抽象層要求開發(fā)者針對抽象進行設(shè)計和編程
- 橋接模式要求正確識別出系統(tǒng)中兩個獨立變化的維度,因此其使用范圍有一定的局限性,即需要有這樣的應(yīng)用場景
文章說明
- 本文章為本人學(xué)習(xí)尚硅谷的學(xué)習(xí)筆記,文章中大部分內(nèi)容來源于尚硅谷視頻(點擊學(xué)習(xí)尚硅谷相關(guān)課程),也有部分內(nèi)容來自于自己的思考,發(fā)布文章是想幫助其他學(xué)習(xí)的人更方便地整理自己的筆記或者直接通過文章學(xué)習(xí)相關(guān)知識,如有侵權(quán)請聯(lián)系刪除,最后對尚硅谷的優(yōu)質(zhì)課程表示感謝。
- 本人還同步閱讀《圖解設(shè)計模式》書籍(圖解設(shè)計模式/(日)結(jié)城浩著;楊文軒譯–北京:人民郵電出版社,2017.1),進而綜合兩者的內(nèi)容,讓知識點更加全面
到了這里,關(guān)于【設(shè)計模式——學(xué)習(xí)筆記】23種設(shè)計模式——橋接模式Bridge(原理講解+應(yīng)用場景介紹+案例介紹+Java代碼實現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!