2020重新出發(fā),JAVA設(shè)計(jì)模式 之八 橋接模式

橋接模式(Bridge模式)詳解

在現(xiàn)實(shí)生活中,某些類具有兩個(gè)或多個(gè)維度的變化,如圖形既可按形狀分,又可按顏色分。如何設(shè)計(jì)類似于 Photoshop 這樣的軟件,能畫不同形狀和不同顏色的圖形呢?如果用繼承方式,m 種形狀和 n 種顏色的圖形就有 m×n 種,不但對(duì)應(yīng)的子類很多,而且擴(kuò)展困難。

當(dāng)然,這樣的例子還有很多,如不同顏色和字體的文字、不同品牌和功率的汽車、不同性別和職業(yè)的男女、支持不同平臺(tái)和不同文件格式的媒體播放器等。如果用橋接模式就能很好地解決這些問題。

橋接模式的定義與特點(diǎn)

橋接(Bridge)模式的定義如下:將抽象與實(shí)現(xiàn)分離,使它們可以獨(dú)立變化。它是用組合關(guān)系代替繼承關(guān)系來(lái)實(shí)現(xiàn),從而降低了抽象和實(shí)現(xiàn)這兩個(gè)可變維度的耦合度。

橋接(Bridge)模式的優(yōu)點(diǎn)是:

  • 由于抽象與實(shí)現(xiàn)分離,所以擴(kuò)展能力強(qiáng);

  • 其實(shí)現(xiàn)細(xì)節(jié)對(duì)客戶透明。

缺點(diǎn)是:由于聚合關(guān)系建立在抽象層,要求開發(fā)者針對(duì)抽象化進(jìn)行設(shè)計(jì)與編程,這增加了系統(tǒng)的理解與設(shè)計(jì)難度。

橋接模式的結(jié)構(gòu)與實(shí)現(xiàn)

可以將抽象化部分與實(shí)現(xiàn)化部分分開,取消二者的繼承關(guān)系,改用組合關(guān)系。

1. 模式的結(jié)構(gòu)

橋接(Bridge)模式包含以下主要角色。

  1. 抽象化(Abstraction)角色:定義抽象類,并包含一個(gè)對(duì)實(shí)現(xiàn)化對(duì)象的引用。

  2. 擴(kuò)展抽象化(Refined Abstraction)角色:是抽象化角色的子類,實(shí)現(xiàn)父類中的業(yè)務(wù)方法,并通過組合關(guān)系調(diào)用實(shí)現(xiàn)化角色中的業(yè)務(wù)方法。

  3. 實(shí)現(xiàn)化(Implementor)角色:定義實(shí)現(xiàn)化角色的接口,供擴(kuò)展抽象化角色調(diào)用。

  4. 具體實(shí)現(xiàn)化(Concrete Implementor)角色:給出實(shí)現(xiàn)化角色接口的具體實(shí)現(xiàn)。

其結(jié)構(gòu)圖如圖 1 所示。

橋接模式的結(jié)構(gòu)圖

圖1 橋接模式的結(jié)構(gòu)圖

2. 模式的實(shí)現(xiàn)

橋接模式的代碼如下:

<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="java" cid="n31" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> package bridge;
public class BridgeTest
{
public static void main(String[] args)
{
Implementor imple=new ConcreteImplementorA();
Abstraction abs=new RefinedAbstraction(imple);
abs.Operation();
}
}
//實(shí)現(xiàn)化角色
interface Implementor
{
public void OperationImpl();
}
//具體實(shí)現(xiàn)化角色
class ConcreteImplementorA implements Implementor
{
public void OperationImpl()
{
System.out.println("具體實(shí)現(xiàn)化(Concrete Implementor)角色被訪問" );
}
}
//抽象化角色
abstract class Abstraction
{
protected Implementor imple;
protected Abstraction(Implementor imple)
{
this.imple=imple;
}
public abstract void Operation();
}
//擴(kuò)展抽象化角色
class RefinedAbstraction extends Abstraction
{
protected RefinedAbstraction(Implementor imple)
{
super(imple);
}
public void Operation()
{
System.out.println("擴(kuò)展抽象化(Refined Abstraction)角色被訪問" );
imple.OperationImpl();
}
}</pre>

程序的運(yùn)行結(jié)果如下:

<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="" cid="n33" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 擴(kuò)展抽象化(Refined Abstraction)角色被訪問
具體實(shí)現(xiàn)化(Concrete Implementor)角色被訪問</pre>

橋接模式的應(yīng)用實(shí)例

用橋接(Bridge)模式模擬女士皮包的選購(gòu) 例子

分析:女士皮包有很多種,可以按用途分、按皮質(zhì)分、按品牌分、按顏色分、按大小分等,存在多個(gè)維度的變化,所以采用橋接模式來(lái)實(shí)現(xiàn)女士皮包的選購(gòu)比較合適。

本實(shí)例按用途分可選錢包(Wallet)和挎包(HandBag),按顏色分可選黃色(Yellow)和紅色(Red)。可以按兩個(gè)維度定義為顏色類和包類。(點(diǎn)此下載本實(shí)例所要顯示的包的圖片)。

顏色類(Color)是一個(gè)維度,定義為實(shí)現(xiàn)化角色,它有兩個(gè)具體實(shí)現(xiàn)化角色:黃色和紅色,通過 getColor() 方法可以選擇顏色;包類(Bag)是另一個(gè)維度,定義為抽象化角色,它有兩個(gè)擴(kuò)展抽象化角色:挎包和錢包,它包含了顏色類對(duì)象,通過 getName() 方法可以選擇相關(guān)顏色的挎包和錢包。

客戶類通過 ReadXML 類從 XML 配置文件中獲取包信息(點(diǎn)此下載 XML 配置文件),并把選到的產(chǎn)品通過窗體顯示出現(xiàn),圖 2 所示是其結(jié)構(gòu)圖。

女士皮包選購(gòu)的結(jié)構(gòu)圖

圖2 女士皮包選購(gòu)的結(jié)構(gòu)圖

程序代碼如下:

<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="java" cid="n44" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> package bridge;
import java.awt.;
import javax.swing.
;
public class BagManage
{
public static void main(String[] args)
{
Color color;
Bag bag;
color=(Color)ReadXML.getObject("color");
bag=(Bag)ReadXML.getObject("bag");
bag.setColor(color);
String name=bag.getName();
show(name);
}
public static void show(String name)
{
JFrame jf=new JFrame("橋接模式測(cè)試");
Container contentPane=jf.getContentPane();
JPanel p=new JPanel();
JLabel l=new JLabel(new ImageIcon("src/bridge/"+name+".jpg"));
p.setLayout(new GridLayout(1,1));
p.setBorder(BorderFactory.createTitledBorder("女士皮包"));
p.add(l);
contentPane.add(p, BorderLayout.CENTER);
jf.pack();
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
//實(shí)現(xiàn)化角色:顏色
interface Color
{
String getColor();
}
//具體實(shí)現(xiàn)化角色:黃色
class Yellow implements Color
{
public String getColor()
{
return "yellow";
}
}
//具體實(shí)現(xiàn)化角色:紅色
class Red implements Color
{
public String getColor()
{
return "red";
}
}
//抽象化角色:包
abstract class Bag
{
protected Color color;
public void setColor(Color color)
{
this.color=color;
}
public abstract String getName();
}
//擴(kuò)展抽象化角色:挎包
class HandBag extends Bag
{
public String getName()
{
return color.getColor()+"HandBag";
}
}
//擴(kuò)展抽象化角色:錢包
class Wallet extends Bag
{
public String getName()
{
return color.getColor()+"Wallet";
}
}</pre>

<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="java" cid="n45" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> package bridge;
import javax.xml.parsers.;
import org.w3c.dom.
;
import java.io.*;
class ReadXML
{
public static Object getObject(String args)
{
try
{
DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=dFactory.newDocumentBuilder();
Document doc;
doc=builder.parse(new File("src/bridge/config.xml"));
NodeList nl=doc.getElementsByTagName("className");
Node classNode=null;
if(args.equals("color"))
{
classNode=nl.item(0).getFirstChild();
}
else if(args.equals("bag"))
{
classNode=nl.item(1).getFirstChild();
}
String cName="bridge."+classNode.getNodeValue();
Class<?> c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
}</pre>

程序的運(yùn)行結(jié)果如圖 3 所示。

女士皮包選購(gòu)的運(yùn)行結(jié)果1

圖3 女士皮包選購(gòu)的運(yùn)行結(jié)果1

如果將 XML 配置文件按如下修改:

<pre spellcheck="false" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" lang="xml" cid="n52" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> <?xml version="1.0" encoding="UTF-8"?>
<config>
<className>Red</className>
<className>Wallet</className>
</config></pre>

則程序的運(yùn)行結(jié)果如圖 4 所示。

女士皮包選購(gòu)的運(yùn)行結(jié)果2

圖4 女士皮包選購(gòu)的運(yùn)行結(jié)果2

橋接模式的應(yīng)用場(chǎng)景

橋接模式通常適用于以下場(chǎng)景。

  1. 當(dāng)一個(gè)類存在兩個(gè)獨(dú)立變化的維度,且這兩個(gè)維度都需要進(jìn)行擴(kuò)展時(shí)。

  2. 當(dāng)一個(gè)系統(tǒng)不希望使用繼承或因?yàn)槎鄬哟卫^承導(dǎo)致系統(tǒng)類的個(gè)數(shù)急劇增加時(shí)。

  3. 當(dāng)一個(gè)系統(tǒng)需要在構(gòu)件的抽象化角色和具體化角色之間增加更多的靈活性時(shí)。

橋接模式模式的擴(kuò)展

在軟件開發(fā)中,有時(shí)橋接(Bridge)模式可與適配器模式聯(lián)合使用。當(dāng)橋接(Bridge)模式的實(shí)現(xiàn)化角色的接口與現(xiàn)有類的接口不一致時(shí),可以在二者中間定義一個(gè)適配器將二者連接起來(lái),其具體結(jié)構(gòu)圖如圖 5 所示。

橋接模式與適配器模式聯(lián)用的結(jié)構(gòu)圖

圖5 橋接模式與適配器模式聯(lián)用的結(jié)構(gòu)圖

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容