橋模式(Bridge)是為了解決將抽象部分與實現部分分離,好讓他們都在自己的維度上有多維度地變化。這句話是好理解的,只是我在學習完橋模式之後,存在一些疑問,還好現在想通了。現在我就橋模式的設計思想和我的疑問一並發出,希望於你有益。
現假設市面上有多種不同型號的電視機,和一些不同的遙控器生產產商。遙控器生產產商要為這些電視機進行生產遙控器,可是這些電視機型號太多,有一些電視機的型號太雜,甚至在後期會出現一些不同型號的電視機。
在這個一般方案中,遙控器產商要為每一種電視機生產一種遙控器。類圖如下
每個具體的遙控器都去繼承或是實現遙控器的抽象或是接口,並且在每個具體的遙控器內部都聚合了各自的電視機接口。這種方案可以解決問題,可以解決遙控器與電視機的對應關系。可是,卻有一個問題,就像上面場景中所說的,如果這裡的有一些新的電視機出現,那麼生產遙控器的產商還得去生產新的遙控器。這樣一來各自的遙控器數量不但不好把握,而且這樣的設計很繁雜。
上面一般的聚合方案無法解決遙控器的一般性,對於新的機型無法兼容和適應。對於這樣在抽象或是實現上會有多維度擴展的情況,我們就必須重新設計代碼結構了。下面是橋模式,以及橋模式是如何解決這個問題的。
將抽象部分(Abstraction)與實現部分(Implementor)分離,使它們可以獨立地變化。
橋模式的應用場景是某一個抽象或是實現可能存在多維度的變化。就像上面的遙控器實例,我們在遙控器這個抽象上,可能會有不同的生產產商;在電視機這個實現上可能會RCA、Sony等等。橋模式可以讓遙控器的抽象和電視機的實現在各自的方向上多維度變化,而不用關心其他的事情。
在上面我也分析了一般的聚合方案是不可靠的,它的確可以解決一些問題,可是卻是繁瑣的,也是不必要的。
這裡再說一個大家可能都比較熟悉的例子。比如我們現在編寫的Java程序,與運行平台之間的關系。我們可以有很多很多的Java程序,而運行平台也可以是Windows、Linux或是Mac。我們的Java程序需要在所有的平台上能夠執行,每一個平台也要保證能夠全部的Java程序。
這裡我們讓遙控器的抽象類聚合電視機的接口。因為所以的電視都實現電視機這個接口,那麼遙控器的抽象類也就聚合了所有的電視機實現。這樣不管你電視機的型號如何改變,遙控器都可以不做修改也能繼續工作。
注:當時我在這裡存在一個小疑問,為什麼我們的電視機接口不是聚合到遙控器的子類中,而是聚合到遙控器的抽象類中呢?也就是說,這裡我有沒有必要有一個遙控器的抽象類?
現在我假設這裡不存在這個RemoteControl的抽象類,那麼ConcreteRemote將持有TV這個接口,可是這樣在遙控器這個維度上都不好擴展了。橋模式的牛X之處就在於它可以讓多維度的抽象和實現都可以在多維度上進行任意擴展。
基於上面的類圖,可編寫Java代碼。
TV.java
public interface TV {
public void on();
public void off();
public void tuneChannel(int channel);
}
RCA.java
public class RCA implements TV {
@Override
public void on() {
System.out.println("RCA被打開了");
}
@Override
public void off() {
System.out.println("RCA被關閉了");
}
@Override
public void tuneChannel(int channel) {
System.out.println("切換到第" + channel + "頻道了");
}
}
Sony.java
public class Sony implements TV {
@Override
public void on() {
System.out.println("Sony被打開了");
}
@Override
public void off() {
System.out.println("Sony被打開了");
}
@Override
public void tuneChannel(int channel) {
System.out.println("切換到第" + channel + "頻道了");
}
}
RemoteControl.java
public abstract class RemoteControl {
protected TV tv = null;
public RemoteControl(TV _tv) {
this.tv = _tv;
}
public abstract void on();
public abstract void off();
public void setChannel(int channel) {
if (tv != null) {
tv.tuneChannel(channel);
}
}
}
ConcreteRemote.java
public class ConcreteRemote extends RemoteControl {
private int currentStation = 0;
private final int MAX_STATION = 25;
public ConcreteRemote(TV _tv) {
super(_tv);
}
@Override
public void on() {
tv.on();
}
@Override
public void off() {
tv.off();
}
public void setChannel(int channel) {
currentStation = channel;
super.setChannel(currentStation);
}
public void nextChannel() {
currentStation = (MAX_STATION + currentStation + 1) % MAX_STATION;
super.setChannel(currentStation);
}
public void previousChannel() {
currentStation = (MAX_STATION + currentStation - 1) % MAX_STATION;
super.setChannel(currentStation);
}
}