橋式 (The Bridge Pattern)
概述
這一章我們將會通過 Bridge 模式來繼續我們設計模式的學習。 Bridge 模式比我們以前所講到的 Facade 、 Adapter 、 Strategy 這些模式都更有用,但也更復雜。
這一章
-
我將給出一個實際的例子,并進行細致的講解,來幫助你學習 Bridge 模式。
-
我還會把 Bridge 模式的功能,實現方法之類的很關鍵性的東西通過一個表列出來。
-
我還會加入一點我自己對 Bridge 模式的看法。
Bridge 模式簡介
按照 GoF 的說法,我們使用 Bridge 模式的目的是“把抽象從它的具體實現中分離出來,以使二者可以獨立變化”。
我還記得我第一次看到這句話時是多么的驚訝,(原以為會是多么高級,原來就是把實現和抽象分離而已,貌似作者當時很失望)。我很清楚這句話里每一個單詞的具體含義,然而,我想我還是沒有理解到這句話。
我知道:
-
“分離”就是說使事物的行為和其他事物無關,或者至少說要把他們之間的關系弄明白。
-
“抽象”是指不同事物在某種概念上的一致性,或者說是相關性。
我以為,要建立抽象的方法就是“實現”,當我看到 GoF 說要把抽象從實現中分離出來的時候,我就不能不困惑了。
看起來,我的困惑都是因為我誤解了實現的含義。這里的“實現”是指那些抽象類以及其派生類用來實現它們自身的對象。老實說,如果一開始我就能明白到這一點,那肯定要省事很多,然而這個句子的確很難讓人一下子就明白其真正含義。
如果你現在和我當時一樣對 Bridge 模式仍然充滿了困惑,沒關系,相信通過后面的講解,你會對 Bridge 模式有一個非常清楚的認識;如果你現在就已經很清楚 Bridge 模式的目的了的話,那么后面你將會很輕松。
Bridge 模式可以算是眾多晦澀的模式中的一個,因為它功能強大,應用范圍又很廣,還因為它的處理方式和通常用繼承/派生方式處理問題有點背道而馳的感覺。然而,它仍然是一個很好的例子,一個遵循設計模式兩大要求的例子。它們是:“找到什么是變化的并封裝它” 和 “盡量考慮使用聚合而不是繼承/派生”,隨后,我們將會看到 Bridge 模式是如何符合這兩點的。
通過例子學習 Bridge 模式
我將通過從頭開始講解一個例子的方式來幫助你理解 Bridge 模式的思想和它能做什么。我將從最初的系統要求開始講解這個例子,然后逐步引入 Bridge 模式并把它應用到這個例子當中。
也許這個例子看起來過于基礎。但還是請看看本例所討論的一些概念,再想想你所遇到過的和下面相似的情形:
-
被“抽象”的概念在變化。
-
抽象的實現方法也在變化。
你將會發現這和我們先前討論過的 CAD/CAM 問題有些相似。我將會逐漸增加這個例子里的需求,就像我們實際遇到的那樣,而不是一次就把所有的需求都提出來。你總不可能在問題一開始就預見所有可能的變化吧。
這樣看來,我們的底線就是:在系統的需求沒有定型之前,盡可能多盡可能早的預見可能出現的變化。
假設,我接到一個任務,可以用兩個不同的程序來畫矩形。而且要求,在初始化一個矩形的時候,我要知道我是使用第一個繪圖程序 (DP!) 還是第二個繪圖程序 (DP2) 。
如圖 10-1 所示,通過對角線上的兩個點來定義一個矩形。這兩個繪圖程序的不同之處也已經總結在表 10-1 里。
圖 10-1 定位一個矩形
表 10-1 兩個繪圖程序的不同之處
-
DP1
DP2
畫線
draw_a_line( x1 , y1 , x2 , y2 )
drawline( x1, x2, y1, y2)
畫圓
draw_a_circle (x , y , r )
drawcircle( x, y, r)
根據分析,我不希望繪制矩形的代碼知道具體是使用的 DP1 還是 DP2 。但是,前面又有要求說在矩形初始化的時候就要明確使用的到底是 DP1 還是 DP2 ,這樣的話,我可以通過用兩種矩形來解決:一種用 DP1 ,一種用 DP2 。它們都有一個 draw() 方法,但是各自的實現方式不同。如圖 10-2 所示。
圖 10-2 Design for rectangles and drawing programs (DP1 and DP2).
由于這兩種矩形的唯一不同之處就在于他們使用的繪圖程序,所以,通過抽象類 Rectangle ,讓這兩種矩形分別實現 drawLine() 方法就可以了。 V1Rectangle 通過包含一個 DP1 的對象,并調用該對象的 draw_a_line() 方法來實現, V2Rectangle 則通過包含一個 DP2 的對象,并調用該對象的 drawLine() 方法來實現。這樣,在初始化 Rectangle 的時候,我就不必再理會這些不同了。
例 10-1 Java 代碼片段
???? private ? double ?_x1,?_y1,?_x2,?_y2;
???? public ?Rectangle?( double ?x1,? double ?y1,? double ?x2,? double ?y2){
????????_x1? = ?x1;
????????_y1? = ?y1;
????????_x2? = ?x2;
????????_y2? = ?y2;
????}
???? public ? void ?draw?(){
????????drawLine(_x1,?_y1,?_x2,?_y1);
????????drawLine(_x2,?_y1,?_x2,?_y2);
????????drawLine(_x2,?_y2,?_x1,?_y2);
????????drawLine(_x1,?_y2,?_x1,?_y1);
????}
???? abstract ? protected ? void ?drawLine?( double ?x1,? double ?y1,? double ?x2,? double ?y2);
}
現在我們已經完成了上面的代碼以及 V1Rectangle 和 V2Rectangle 的代碼。但是,我又被要求程序能夠支持另一種除了矩形以外的形狀,圓。而且,同樣要求,保存這些“圓”的容器不需要知道它保存的到底是圓 (Circle) 還是矩形 (Rectangle) 。
看起來,我可以在我現有的實現基礎上擴展一下就可以了。我加入一個新的 Shape 類,讓 Rectangle 和 Circle 都去繼承它。這樣,客戶端對象可以只保存 Shape 類的對象,而不用起考慮到底具體是什么類型的 Shape(Rectangle 或是 Circle) 。
在一個面向對象分析的新手的眼里,用繼承來實現這些要求,看起來是很自然的事情。例如,我可以像圖 10-2 所演示的那樣做。通過繼承再繼承,每個形狀都分別派生出使用 DP1 和 DP2 的類,最后,得到圖 10-3 這樣的結果。
Figure 10-3 A straightforward approach: implementing two shapes and two drawing programs.
我用實現 Rectangle 的方法來實現 Circle 。然而,這次,實現 draw() 這個方法的時候是用 drawCircle() 而不再是 Rectangle 里的 drawLine() 。
例 10-2 Java 代碼片段
???? abstract ? public ? void ?draw?();
}
// ?the?only?change?to?Rectangle?is
abstract ? class ?Rectangle? extends ?Shape?{
????
}
// V1Rectangle?and?V2Rectangle?don't?change
public ? abstract ? class ?Circle? extends ?Shape?{
???? protected ? double ?_x,?_y,?_r;
???? public ?Circle?( double ?x,? double ?y,? double ?r){
????????_x? = ?x;
????????_y? = ?y;
????????_r? = ?r;
????}
???? public ? void ?draw?(){
????????drawCircle?();
????}
???? abstract ? protected ? void ?drawCircle();
}
public ? class ?V1Circle? extends ?Circle?{
???? public ?V1Circle?( double ?x,? double ?y,? double ?r){
???????? super (?x,?y,?r);
????}
???? protected ? void ?drawCircle?(){
????????DP1.draw_a_circle?(_x,?_y,?_r);
????}
}
public ? class ?V2Circle? extends ?Circle?{
???? public ?V2Cricle?( double ?x,? double ?y,? double ?r){
???????? super (?x,?y,?r);
????}
???? protected ? void ?drawCircle?(){
????????DP2.drawCircle(?_x,?_y,?_r);
????}
}
我們來仔細看看這個例子。看看 draw() 和 V1Rectangle 都做了些什么。
-
Rectangle 的 draw() 方法和先前的一樣 ( 都根據需要調用 drawLine() 四次 ) 。
-
drawLine() 通過調用 DP1 的 draw_a_line() 來實現。
其執行的步驟如圖 10-4 所示。
Figure 10-4 Sequence Diagram when have a V1Rectangle.
雖然,在類圖上看起來,程序里有很多的對象,但是實際上,我只需要處理三個對象。如圖 10-5 所示。
Figure 10-5 The Objects present.
-
Client 對象使用到矩形。
-
V1Rectangle 對象。
-
繪圖程序 DP1 。
當 client 對象傳遞一個消息到 V1Rectangle 的對象 ( 也就是 myRectangle) ,執行其中的 draw() 方法的時候,它通過圖中的步驟 2 到步驟 9 來完成調用 draw() 方法的全過程。
不幸的是,這種方法又引起了新的問題。回過頭來看看圖 10-3 ,仔細看看第三行的類,想想下面的問題:
-
這一行的類代表了四種不同類型的 Shape 。
-
如果我現在又有了一個新的繪圖程序,也就是說,在實現上又有了一個新的變化。這樣,我是不是該有 6 個類了? (2 個 Shape 再乘上 3 個繪圖程序。 )
-
再來,如果在前面基礎上,我現在又要實現一個新的形狀,比方說,再來個橢圓,完了,現在,我該有 3 * 3 = 9 個類了。
類的數量就和滾雪球一樣,越來越多。這種情況的出現是因為抽象 ( 各種各樣的 Shape) 和實現 ( 多個繪圖程序 ) 他們很緊密地耦合在一起!每種確切的形狀都要知道它所使用的繪圖程序的具體類型。看樣子,要避免這種類在數量上的爆炸式的增長,我得需要一個能夠把抽象和實現分離看來的方法。讓他們可以獨立的發生變化。這樣,類的數量就可以呈現出線性的增長,而不是近指數形式的增長了。如圖 10-6 所示。
Figure 10-6 The Bridge pattern separate variations in abstraction and implementation.
在具體講解處理方法和 Bridge 模式之前,我還想講點其他的問題。
讓我們再次回過頭去看看圖 10-3 ,自問一下:這個設計的缺點在哪里?
-
這個設計看起來是不是有點累贅?
-
設計里的對象間是結合是否緊密呢?
-
對象是不是很緊密的耦合在一起呢?
這樣的代碼,你愿意去維護嗎?
-
濫用繼承
作為一個新手,我曾經趨向于用“特殊化”的方式來解決問題,也就是利用繼承。我曾經很喜歡使用繼承這種方式,因為它是個新穎有功能強大的方法。我總是盡可能地使用繼承,這樣的情況在新手里經常出現,但是,這種做法是很天真的:有了“繼承”這把“榔頭”,見到啥玩意都象顆釘子,一榔頭就下去了。然而,令人感到遺憾的是,在學習面向對象設計的過程中,人們總是受著“特殊化”的熏陶,總是被教導著通過特殊化來處理一切變化,無休止的從基類派生出新類。由于過度地注重“是什么”,而使得很多設計都是建立在一些很固化的繼承鏈上的,這些設計在一開始的時候可以很好的運行,但是,隨著時間的推移,這些設計將會變得非常難以維護 ( 正如在第九章,”策略模式”里面所講到的那樣 ) 。
即使是在我經驗已經比較豐富的時候,我還是會把這種基于繼承的方法過度的應用到設計當中,只管類”是什么”,而并沒有意識到我這么做,將會使整個設計在結構上變得多么的復雜!
而,是設計模式的思維方法使我擺脫了這些。我學會用我的對象具有什么要嗯的職能的方式去思考問題,而不再是單從對象的結構上去思考問題。
真正有經驗的面向對象的設計師,他們都會很有選擇性地使用繼承。而學習設計模式,將會使你更快的學到這些。它將帶給你一個從把每個變體特殊化到學會使用聚合的轉變。
最初,我看到這些問題的出現,我曾以為這一切都是因為我的繼承方式使用得不對而引起的。于是,我試著改進了先前的繼承鏈,把它變得像圖 10-7 那樣。
Figure 10-7 An alternative implementation.
我還是有四個類,但是,通過首先就按繪圖程序的不同而區分類的方式,減少了很多 DP1 和 DP2 之間的冗余內容。
然而,我還是不能排除兩種 Rectangle 和兩種 Circle 之間的冗余,每種 Rectangle 和每種的 Circle 都有相同的 draw() 方法。
不管怎么說,先前的那種類數量上的爆炸式的增長,在這里又出現了。
新的設計方案的執行過程如圖 10-8 所示。
Figure 10-8 Sequence diagram for new approach.
雖然這個方案比最初的那個要好那么一點點,但是還是存在類數量將會過大的問題,而且對象間的耦合也存在問題。
也就是說,我還是不想維護這樣的代碼,一定還會有更好的方案的!
-
替代原有設計方案
雖然我使用的替代方案并比我的原始方案優秀多少,但是,我還是要指出來的就是,在原有方案上尋找替代方案是很重要的。太多的程序員都是抱著最開始的東西不放。當然了,我不是說要去深入地考慮所有可替代的方案 ( 真是這樣的話,就永遠停留在分析上了 ) 。然而,在遇到困難的時候,回過頭去看看是否可以在原始方案里就能夠解決這些困難。這,是很重要的。事實上,我們只是退后一步而已,明知道原來的設計方案存在這樣那樣的問題,退回來看看有沒有什么辦法可以補救。正是這樣,使我領悟到設計模式的魅力。
關于使用設計模式的一些觀察
當人們開始關注設計模式的時候,通常他們只關心的是設計模式所提供的解決方法。這看起來不無道理,因為設計模式不是一直都被說成是能夠提供一些好的辦法來解決我們手頭上的難題么?
然而,這樣的看法是站在我們遇到一個難題的基礎上提出來的。實際上,在運用設計模式到設計中之間,你應該首先明白你的設計將會做什么。正確的做法應該是考慮一下你可以在設計中的哪些地方用到設計模式,這將告訴你要做什么,而不是什么時候我要用設計模式了,也不是為什么我要去用設計模式。
我發現,關心一下一個設計模式將會處理的問題的方式很有用。這樣,我就會知道什么時候,為什么要使用到這個模式。
當你的一個抽象有很多種實現方法的時候,你就會發現 Bridge 模式的妙用。它可以讓你的抽象和實現各自獨立的發生變化。
我們一直在討論的這個繪圖的問題就很符合 Bridge 模式的應用場合。因此,我知道,我應該在這個設計里用到 Bridge 模式了,雖然,到目前位置,我還不知道我該怎么做。讓抽象和實現可以實現獨立的變化意味著我可以任意的添加新的抽象而不用去修改原有的具體實現。
而當前的方案還不允許這樣的獨立變化。
有一點很重要,就是即使在不知道具體怎么實現 Bridge 模式的情況下,你還是可以肯定 Bridge 模式在這個設計中是很有用的。以后你就會發現,這種情況在設計模式領域里很常見,那就是,你可以確定你應該把某個模式應用到你的設計當中,盡管,你還并不知道該怎么去實現這個模式。
Bridge 模式的引出
相信你已經很清楚我們所要處理的問題了,那么,現在我們就用 Bridge 模式來解決我們的問題。這個過程將幫助你更加深入地理解這個復雜而強大的 Bridge 模式。
我們還是要遵循那兩個“原則”:
-
找到什么是變化的并封裝它。
-
盡量使用聚合而不是繼承。
以前,開發人員總是依靠大量的繼承來處理程序中的變化,而第二個“原則”告訴我要盡可能的使用聚合。使用聚合的目的就是讓程序可以在單獨的類里面包含它自己的變化,這樣,今后有新變化出現的時候就可以通過添加新的類來包含這個變化,而不會對原先的類造成影響。要達到這個目的,我們就需要把所有的變化都包含到它們共同的抽象類里面,然后,我們再去考慮抽象類之間的聯系,而不再考慮抽象類的具體實現。
-
再說封裝
很多面向對象程序設計的開發人員都知道“封裝”就是數據隱藏。其實,這是一個很狹隘的定義!實際上,“封裝”除了數據隱藏以外,還有很多其他的用法。如果你再回過頭去看看圖 7-2 ,你會在里面發現“封裝”可以操縱很多個層次。當然,那里,封裝的目的就是為每個 Shape 做數據隱藏。然而,要注意的是, Client 對象并不知道它所處理的 Shape 的具體類型 ( 到底是 Rectangle 還是 Circle) 。因此,那個具體被 Client 所處理的類就通過 Shape 而實現了隱藏 ( 也就是封裝 ) ,這也就是 GoF 在說到“找到什么是變化的并封裝它”的時候所指的“封裝”,讓抽象類 (Shape) 出面去和 Client 對象交互,具體的 Rectangle 、 Circle 這些類都“躲”在了 Shape 類的后面, Client 只知道自己在和 Shape 交互,其他一無所知。
現在我具體討論一下本章里一直在講的這個繪圖的例子。
首先,找出什么是變化的。在這個例子里,也就是不同的形狀和不同的繪圖程序。而這些不同的形狀、不同的繪圖程序在概念上,分屬于“形狀”和“繪圖程序”,這樣,我就可以用 10-9 所示的類來代表這些不同的形狀和程序 ( 注意圖里面的類名是斜體的,這表示他們都是抽象類 ) 。
Figure 10-9 What is varying?
這樣,我打算用 Shape 來封裝這個例子里的所有的不同的形狀,而代表這些不同的形狀的類將必須自己能夠完成他本身的繪制工作。 Drawing 類的對象將僅僅負責完成“線”和“圓”等具體線條的繪制工作。這些具體的繪制,都將通過在類里定義的一些方法來完成。
現在我們已經很抽象地表示出“形狀”和“繪圖程序”了,下一步,就是怎么表示具體的形狀和具體的繪圖程序的問題了。我有的“形狀”是矩形和圓,那么,我就從 Shape 里派生出 Rectangle 和 Circle 來分別代表矩形和圓。“繪圖程序”我必須使用已有的 DP1 和 DP2 ,但是我又不能直接使用它們,所以,我可以從 Drawing 派生出 V1Drawing 和 V2Drawing ,讓他們分別去使用 DP1 和 DP2 。這樣,就得到如圖 10-10 所示的類圖。
Figure 10-10 Represent the variations.
到目前為止,這一切看起來還是有點抽象。我只說了 V1Drawing 和 V2Drawing 要分別對應地使用到 DP1 和 DP2 ,但是我還沒有說怎么用。我只是把整個例子中存在的一些變化找了出來而已。
有了這兩組類,我現在要關心的就是它們之間怎么彼此關聯起來的問題了。我不想再出現一組用同樣繼承的方式出現的類,這樣的結果我已經很清楚了 ( 再回過頭去看看圖 10-3 和 10-7 ,加深點印象 ) 。相反地,我在想,我是不是可以通過用一組類使用另一組的方式來完成 ( 這不就是用聚合了嗎? ) 。現在的問題是,兩組類,到底誰用誰?
很明顯的兩種可能性:要么在 Shape 里用到 Drawing ,要么反過來, Drawing 里用到 Shape 。
我們先來看看后面這種 Drawing 里使用 Shape 的情況,如果繪圖程序直接使用到具體的形狀,那么這些程序就必須知道該怎么去完成這些具體形狀的繪制。但是,這些形狀僅僅只知道如何完成“線”和“圓”這些簡單線條的繪制,這,超出了它們的能力范圍。
那么,再看看前面的那種情況。如果在 Shape 里用 Drawing 會怎么樣? Shape 將沒有必要知道它所使用的 Drawing 的確切類型,因為,我們已經用 Drawing 封裝了 V1Drawing 和 V2Drawing 。 這樣看起來不錯,就像圖 10-11 所示的那樣。
Figure 10-11 Tie the classes together.
在這個設計里, Shape 通過 Drawing 來完成自己的繪制。這里,我略去了 V1Drawing 使用 DP1 和 V2Drawing 使用 DP2 的細節,在圖 10-12 里,我把這些都表示了出來還加入了一些 protected 的方法。
Figure 10-12 Expanding the design.
圖 10-13 所示的是把 Shape( 抽象 ) 和 Drawing( 實現 ) 分離開來。
Figure 10-13 Class diagram illustrating separation of abstraction and implementation.
-
一個規則,一個地方
我們必須遵循的一個非常重要的實現策略就是,一個規則僅僅應用到一個地方。也就是說,如果你有一個處理事務的規則,這個規則就僅用一次就好。很顯然這會導致設計里出現很多的小方法。但是,這么做你可以避免一些預想不到的問題的出現,可以避免因為規則相似,而復制原有代碼再修改滿足新規則的情況,而這么做的代價是很小的。
雖然 Rectangle 或者 draw() 方法都可以直接操縱 Shape 的任意的 Drawing 對象,我可以通過使用下面的“一個規則,一個地方”的策略來改進這點。我讓 Shape 的 drawLine() 方法來調用 Drawing 對象的 drawLine() 。
當然,凡事也不是那么的絕對。如果,我認為某個地方應該一直遵守某種規則的話,就像這個例子一樣。我的 Shape 類有個 drawLine() ,因為它代表著我要用 Drawing 來繪制一條直線;我同樣可以用這樣的方式創建一個 drawCircle() ,因為它代表著用 Drawing 繪制一個圓。
站在各個類里面的具體方法 (method) 的角度來看,這和基于繼承的設計方案 ( 如圖 10-3) 沒什么不同。如果非要說有什么不同,那就是現在這些方法存在的位置不同,他們不再是在同一個類里面,而是被分散到多個類里面。
在這一章一開始我就說了,我對 Bridge 模式的困惑是源于我對“實現”的誤解。我以為“實現”指的是怎么實現一個具體的抽象。
是 bridge 模式讓我意識到,應該把“實現”看作是一個對象以外的東西,是一個為對象所用的東西。用這樣的方式著手設計,在不同的繼承鏈里包含各自的變化。圖 10-13 左側的繼承鏈包含了抽象的變化,右側的繼承鏈包含的則是我如何實現這些抽象的變化。這和我們說要“盡量使用聚合而不是繼承”的原則相一致。
雖然在整個設計里有不少的類,但是,你只要牢記一點,你一次要處理的就只有三個對象,這樣你的思路就會很清晰。
Figure 10-14 There are only three objects at a time.
Example 10-3 Java Code Fragments
???? public ? static ? void ?main(String?args[]){
????????Shape?myShapes[];
????????Factory?myFactory? = ? new ?Factory();
????????
???????? // get?rectangles?form?some?other?source
????????myShapes? = ?myFactory.getShapes();
???????? for ?(Shape?shape?:?myShapes){
????????????shape.draw();
????????}
????}
}
abstract ? public ? class ?Shape?{
???? protected ?Drawing?myDrawing;
???? abstract ? public ? void ?draw();
????
????Shape?(Drawing?drawing){
????????myDrawing? = ?drawing;
????}
???? protected ? void ?drawLine?( double ?x1,? double ?y1,? double ?x2,? double ?y2){
????????myDrawing.drawLine(x1,?y1,?x2,?y2);
????}
???? protected ? void ?drawCircle?( double ?x,? double ?y,? double ?r){
????????myDrawing.drawCircle(x,?y,?r);
????}
}
public ? class ?Rectangle? extends ?Shape?{
???? private ? double ?_x1,?_y1,?_x2,?_y2;
???? public ?Rectangle?(Drawing?dp,? double ?x1,? double ?y1,? double ?x2,? double ?y2){
???????? super (?dp?);
????????_x1? = ?x1;
????????_y1? = ?y1;
????????_x2? = ?x2;
????????_y2? = ?y2;
????}
???? public ? void ?draw(){
????????drawLine(?_x1,?_y1,?_x2,?_y1);
????????drawLine(?_x2,?_y1,?_x2,?_y2);
????????drawLine(?_x2,?_y2,?_x1,?_y2);
????????drawLine(?_x1,?_y2,?_x1,?_y1);
????}
???? protected ? void ?drawLine( double ?x1,? double ?y1,? double ?x2,? double ?y2){
????????myDrawing.drawLine(?x1,?y1,?x2,?y2);
????}
}
public ? class ?Circle? extends ?Shape?{
???? private ? double ?_x,?_y,?_r;
???? public ?Circle?(Drawing?dp,? double ?x,? double ?y,? double ?r){
???????? super (dp);
????????_x? = ?x;
????????_y? = ?y;
????????_r? = ?r;
????}
???? public ? void ?draw()?{
????????myDrawing.drawCircle?(_x,?_y,?_r);
????}
}
public ? abstract ? class ?Drawing?{
???? abstract ? public ? void ?drawLine?( double ?x1,? double ?y1,? double ?x2,? double ?y2);
???? abstract ? public ? void ?drawCircle?( double ?x,? double ?y,? double ?r);
}
public ? class ?V1Drawing? extends ?Drawing?{
???? public ? void ?drawLine?(? double ?x1,? double ?y1,? double ?x2,? double ?y2){
????????DP1.draw_a_line?(x1,?y1,?x2,?y2);
????}
???? public ? void ?drawCircle?( double ?x,? double ?y,? double ?r){
????????DP1.draw_a_circle?(x,?y,?r);
????}
}
public ? class ?V2Drawing? extends ?Drawing?{
???? public ? void ?drawLine?(? double ?x1,? double ?y1,? double ?x2,? double ?y2){
????????DP2.drawLine?(x1,?y1,?x2,?y2);
????}
???? public ? void ?drawCircle?( double ?x,? double ?y,? double ?r){
????????DP2.drawCircle?(x,?y,?r);
????}
}
回顧 Bridge 模式
Bridge 模式主要功能 |
|
目標 |
把具體實現從使用該實現的對象中分離出來。 |
環境 |
( 概念 ) 抽象類的派生類需要使用多個實現方法而又要避免類的數量的過多。 |
解決方式 |
給所有實現定義一個公共接口并讓 ( 概念 ) 抽象的派生類使用它。 |
參與方式 |
Abstraction 給要實現的對象定義一個接口。 Implementor 給具體的實現定義接口, Abstraction 的派生類使用 Implementor 的派生類而不管到底是用的哪一個 ConcreteImplementor 。 |
結果 |
把具體實現從使用該實現的對象中分離出來的方法增加了程序的靈活性,客戶端對象不會知道實現的確切類型。 |
執行方式 |
|
Figure 10-15 Generic structure of the Bridge pattern. |