您好朋友,感謝您關(guān)注xiaosilent,我在blogjava的博客已經(jīng)停止更新,請訪問http://kuan.fm了解我的最新情況,謝謝!
          隨筆-82  評論-133  文章-0  trackbacks-0

          橋式 (The Bridge Pattern)


          概述

          這一章我們將會通過 Bridge 模式來繼續(xù)我們設(shè)計模式的學(xué)習(xí)。 Bridge 模式比我們以前所講到的 Facade 、 Adapter Strategy 這些模式都更有用,但也更復(fù)雜。

          這一章

          1. 我將給出一個實際的例子,并進(jìn)行細(xì)致的講解,來幫助你學(xué)習(xí) Bridge 模式。

          2. 我還會把 Bridge 模式的功能,實現(xiàn)方法之類的很關(guān)鍵性的東西通過一個表列出來。

          3. 我還會加入一點我自己對 Bridge 模式的看法。


          Bridge 模式簡介

          按照 GoF 的說法,我們使用 Bridge 模式的目的是“把抽象從它的具體實現(xiàn)中分離出來,以使二者可以獨立變化”。

          我還記得我第一次看到這句話時是多么的驚訝,(原以為會是多么高級,原來就是把實現(xiàn)和抽象分離而已,貌似作者當(dāng)時很失望)。我很清楚這句話里每一個單詞的具體含義,然而,我想我還是沒有理解到這句話。

          我知道:

          1. 分離”就是說使事物的行為和其他事物無關(guān),或者至少說要把他們之間的關(guān)系弄明白。

          2. 抽象”是指不同事物在某種概念上的一致性,或者說是相關(guān)性。

          我以為,要建立抽象的方法就是“實現(xiàn)”,當(dāng)我看到 GoF 說要把抽象從實現(xiàn)中分離出來的時候,我就不能不困惑了。

          看起來,我的困惑都是因為我誤解了實現(xiàn)的含義。這里的“實現(xiàn)”是指那些抽象類以及其派生類用來實現(xiàn)它們自身的對象。老實說,如果一開始我就能明白到這一點,那肯定要省事很多,然而這個句子的確很難讓人一下子就明白其真正含義。

          如果你現(xiàn)在和我當(dāng)時一樣對 Bridge 模式仍然充滿了困惑,沒關(guān)系,相信通過后面的講解,你會對 Bridge 模式有一個非常清楚的認(rèn)識;如果你現(xiàn)在就已經(jīng)很清楚 Bridge 模式的目的了的話,那么后面你將會很輕松。

          Bridge 模式可以算是眾多晦澀的模式中的一個,因為它功能強(qiáng)大,應(yīng)用范圍又很廣,還因為它的處理方式和通常用繼承/派生方式處理問題有點背道而馳的感覺。然而,它仍然是一個很好的例子,一個遵循設(shè)計模式兩大要求的例子。它們是:“找到什么是變化的并封裝它” 和 “盡量考慮使用聚合而不是繼承/派生”,隨后,我們將會看到 Bridge 模式是如何符合這兩點的。


          通過例子學(xué)習(xí) Bridge 模式

          我將通過從頭開始講解一個例子的方式來幫助你理解 Bridge 模式的思想和它能做什么。我將從最初的系統(tǒng)要求開始講解這個例子,然后逐步引入 Bridge 模式并把它應(yīng)用到這個例子當(dāng)中。

          也許這個例子看起來過于基礎(chǔ)。但還是請看看本例所討論的一些概念,再想想你所遇到過的和下面相似的情形:

          1. 被“抽象”的概念在變化。

          2. 抽象的實現(xiàn)方法也在變化。

          你將會發(fā)現(xiàn)這和我們先前討論過的 CAD/CAM 問題有些相似。我將會逐漸增加這個例子里的需求,就像我們實際遇到的那樣,而不是一次就把所有的需求都提出來。你總不可能在問題一開始就預(yù)見所有可能的變化吧。

          這樣看來,我們的底線就是:在系統(tǒng)的需求沒有定型之前,盡可能多盡可能早的預(yù)見可能出現(xiàn)的變化。

          假設(shè),我接到一個任務(wù),可以用兩個不同的程序來畫矩形。而且要求,在初始化一個矩形的時候,我要知道我是使用第一個繪圖程序 (DP!) 還是第二個繪圖程序 (DP2) 。

          如圖 10-1 所示,通過對角線上的兩個點來定義一個矩形。這兩個繪圖程序的不同之處也已經(jīng)總結(jié)在表 10-1 里。

          o_10-1.png

          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)

          根據(jù)分析,我不希望繪制矩形的代碼知道具體是使用的 DP1 還是 DP2 。但是,前面又有要求說在矩形初始化的時候就要明確使用的到底是 DP1 還是 DP2 ,這樣的話,我可以通過用兩種矩形來解決:一種用 DP1 ,一種用 DP2 。它們都有一個 draw() 方法,但是各自的實現(xiàn)方式不同。如圖 10-2 所示。

          o_10-2.png

          10-2 Design for rectangles and drawing programs (DP1 and DP2).

          由于這兩種矩形的唯一不同之處就在于他們使用的繪圖程序,所以,通過抽象類 Rectangle ,讓這兩種矩形分別實現(xiàn) drawLine() 方法就可以了。 V1Rectangle 通過包含一個 DP1 的對象,并調(diào)用該對象的 draw_a_line() 方法來實現(xiàn), V2Rectangle 則通過包含一個 DP2 的對象,并調(diào)用該對象的 drawLine() 方法來實現(xiàn)。這樣,在初始化 Rectangle 的時候,我就不必再理會這些不同了。

          10-1 Java 代碼片段

          abstract ? public ? class ?Rectangle?{
          ????
          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);
          }

          現(xiàn)在我們已經(jīng)完成了上面的代碼以及 V1Rectangle V2Rectangle 的代碼。但是,我又被要求程序能夠支持另一種除了矩形以外的形狀,圓。而且,同樣要求,保存這些“圓”的容器不需要知道它保存的到底是圓 (Circle) 還是矩形 (Rectangle) 。

          看起來,我可以在我現(xiàn)有的實現(xiàn)基礎(chǔ)上擴(kuò)展一下就可以了。我加入一個新的 Shape 類,讓 Rectangle Circle 都去繼承它。這樣,客戶端對象可以只保存 Shape 類的對象,而不用起考慮到底具體是什么類型的 Shape(Rectangle 或是 Circle)

          在一個面向?qū)ο蠓治龅男率值难劾铮美^承來實現(xiàn)這些要求,看起來是很自然的事情。例如,我可以像圖 10-2 所演示的那樣做。通過繼承再繼承,每個形狀都分別派生出使用 DP1 DP2 的類,最后,得到圖 10-3 這樣的結(jié)果。

          o_10-3.png

          Figure 10-3 A straightforward approach: implementing two shapes and two drawing programs.

          我用實現(xiàn) Rectangle 的方法來實現(xiàn) Circle 。然而,這次,實現(xiàn) draw() 這個方法的時候是用 drawCircle() 而不再是 Rectangle 里的 drawLine() 。

          10-2 Java 代碼片段

          public ? abstract ? class ?Shape?{
          ????
          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);
          ????}
          }

          我們來仔細(xì)看看這個例子??纯?/font> draw() V1Rectangle 都做了些什么。

          1. Rectangle draw() 方法和先前的一樣 ( 都根據(jù)需要調(diào)用 drawLine() 四次 ) 。

          2. drawLine() 通過調(diào)用 DP1 draw_a_line() 來實現(xiàn)。

          其執(zhí)行的步驟如圖 10-4 所示。

          o_10-4.png

          Figure 10-4 Sequence Diagram when have a V1Rectangle.

          雖然,在類圖上看起來,程序里有很多的對象,但是實際上,我只需要處理三個對象。如圖 10-5 所示。

          o_10-5.png

          Figure 10-5 The Objects present.

          1. Client 對象使用到矩形。

          2. V1Rectangle 對象。

          3. 繪圖程序 DP1 。

          當(dāng) client 對象傳遞一個消息到 V1Rectangle 的對象 ( 也就是 myRectangle) ,執(zhí)行其中的 draw() 方法的時候,它通過圖中的步驟 2 到步驟 9 來完成調(diào)用 draw() 方法的全過程。

          不幸的是,這種方法又引起了新的問題?;剡^頭來看看圖 10-3 ,仔細(xì)看看第三行的類,想想下面的問題:

          1. 這一行的類代表了四種不同類型的 Shape

          2. 如果我現(xiàn)在又有了一個新的繪圖程序,也就是說,在實現(xiàn)上又有了一個新的變化。這樣,我是不是該有 6 個類了? (2 Shape 再乘上 3 個繪圖程序。 )

          3. 再來,如果在前面基礎(chǔ)上,我現(xiàn)在又要實現(xiàn)一個新的形狀,比方說,再來個橢圓,完了,現(xiàn)在,我該有 3 3 9 個類了。

          類的數(shù)量就和滾雪球一樣,越來越多。這種情況的出現(xiàn)是因為抽象 ( 各種各樣的 Shape) 和實現(xiàn) ( 多個繪圖程序 ) 他們很緊密地耦合在一起!每種確切的形狀都要知道它所使用的繪圖程序的具體類型??礃幼?,要避免這種類在數(shù)量上的爆炸式的增長,我得需要一個能夠把抽象和實現(xiàn)分離看來的方法。讓他們可以獨立的發(fā)生變化。這樣,類的數(shù)量就可以呈現(xiàn)出線性的增長,而不是近指數(shù)形式的增長了。如圖 10-6 所示。

          o_10-6.png

          Figure 10-6 The Bridge pattern separate variations in abstraction and implementation.

          在具體講解處理方法和 Bridge 模式之前,我還想講點其他的問題。

          讓我們再次回過頭去看看圖 10-3 ,自問一下:這個設(shè)計的缺點在哪里?

          1. 這個設(shè)計看起來是不是有點累贅?

          2. 設(shè)計里的對象間是結(jié)合是否緊密呢?

          3. 對象是不是很緊密的耦合在一起呢?

          這樣的代碼,你愿意去維護(hù)嗎?

          濫用繼承

          作為一個新手,我曾經(jīng)趨向于用“特殊化”的方式來解決問題,也就是利用繼承。我曾經(jīng)很喜歡使用繼承這種方式,因為它是個新穎有功能強(qiáng)大的方法。我總是盡可能地使用繼承,這樣的情況在新手里經(jīng)常出現(xiàn),但是,這種做法是很天真的:有了“繼承”這把“榔頭”,見到啥玩意都象顆釘子,一榔頭就下去了。然而,令人感到遺憾的是,在學(xué)習(xí)面向?qū)ο笤O(shè)計的過程中,人們總是受著“特殊化”的熏陶,總是被教導(dǎo)著通過特殊化來處理一切變化,無休止的從基類派生出新類。由于過度地注重“是什么”,而使得很多設(shè)計都是建立在一些很固化的繼承鏈上的,這些設(shè)計在一開始的時候可以很好的運行,但是,隨著時間的推移,這些設(shè)計將會變得非常難以維護(hù) ( 正如在第九章,”策略模式”里面所講到的那樣 ) 。

          即使是在我經(jīng)驗已經(jīng)比較豐富的時候,我還是會把這種基于繼承的方法過度的應(yīng)用到設(shè)計當(dāng)中,只管類”是什么”,而并沒有意識到我這么做,將會使整個設(shè)計在結(jié)構(gòu)上變得多么的復(fù)雜!

          而,是設(shè)計模式的思維方法使我擺脫了這些。我學(xué)會用我的對象具有什么要嗯的職能的方式去思考問題,而不再是單從對象的結(jié)構(gòu)上去思考問題。

          真正有經(jīng)驗的面向?qū)ο蟮脑O(shè)計師,他們都會很有選擇性地使用繼承。而學(xué)習(xí)設(shè)計模式,將會使你更快的學(xué)到這些。它將帶給你一個從把每個變體特殊化到學(xué)會使用聚合的轉(zhuǎn)變。

          最初,我看到這些問題的出現(xiàn),我曾以為這一切都是因為我的繼承方式使用得不對而引起的。于是,我試著改進(jìn)了先前的繼承鏈,把它變得像圖 10-7 那樣。

          o_10-7.PNG

          Figure 10-7 An alternative implementation.

          我還是有四個類,但是,通過首先就按繪圖程序的不同而區(qū)分類的方式,減少了很多 DP1 DP2 之間的冗余內(nèi)容。

          然而,我還是不能排除兩種 Rectangle 和兩種 Circle 之間的冗余,每種 Rectangle 和每種的 Circle 都有相同的 draw() 方法。

          不管怎么說,先前的那種類數(shù)量上的爆炸式的增長,在這里又出現(xiàn)了。

          新的設(shè)計方案的執(zhí)行過程如圖 10-8 所示。

          o_10-8.PNG

          Figure 10-8 Sequence diagram for new approach.

          雖然這個方案比最初的那個要好那么一點點,但是還是存在類數(shù)量將會過大的問題,而且對象間的耦合也存在問題。

          也就是說,我還是不想維護(hù)這樣的代碼,一定還會有更好的方案的!

          替代原有設(shè)計方案

          雖然我使用的替代方案并比我的原始方案優(yōu)秀多少,但是,我還是要指出來的就是,在原有方案上尋找替代方案是很重要的。太多的程序員都是抱著最開始的東西不放。當(dāng)然了,我不是說要去深入地考慮所有可替代的方案 ( 真是這樣的話,就永遠(yuǎn)停留在分析上了 ) 。然而,在遇到困難的時候,回過頭去看看是否可以在原始方案里就能夠解決這些困難。這,是很重要的。事實上,我們只是退后一步而已,明知道原來的設(shè)計方案存在這樣那樣的問題,退回來看看有沒有什么辦法可以補(bǔ)救。正是這樣,使我領(lǐng)悟到設(shè)計模式的魅力。



          關(guān)于使用設(shè)計模式的一些觀察

          當(dāng)人們開始關(guān)注設(shè)計模式的時候,通常他們只關(guān)心的是設(shè)計模式所提供的解決方法。這看起來不無道理,因為設(shè)計模式不是一直都被說成是能夠提供一些好的辦法來解決我們手頭上的難題么?

          然而,這樣的看法是站在我們遇到一個難題的基礎(chǔ)上提出來的。實際上,在運用設(shè)計模式到設(shè)計中之間,你應(yīng)該首先明白你的設(shè)計將會做什么。正確的做法應(yīng)該是考慮一下你可以在設(shè)計中的哪些地方用到設(shè)計模式,這將告訴你要做什么,而不是什么時候我要用設(shè)計模式了,也不是為什么我要去用設(shè)計模式。

          我發(fā)現(xiàn),關(guān)心一下一個設(shè)計模式將會處理的問題的方式很有用。這樣,我就會知道什么時候,為什么要使用到這個模式。

          當(dāng)你的一個抽象有很多種實現(xiàn)方法的時候,你就會發(fā)現(xiàn) Bridge 模式的妙用。它可以讓你的抽象和實現(xiàn)各自獨立的發(fā)生變化。

          我們一直在討論的這個繪圖的問題就很符合 Bridge 模式的應(yīng)用場合。因此,我知道,我應(yīng)該在這個設(shè)計里用到 Bridge 模式了,雖然,到目前位置,我還不知道我該怎么做。讓抽象和實現(xiàn)可以實現(xiàn)獨立的變化意味著我可以任意的添加新的抽象而不用去修改原有的具體實現(xiàn)。

          而當(dāng)前的方案還不允許這樣的獨立變化。

          有一點很重要,就是即使在不知道具體怎么實現(xiàn) Bridge 模式的情況下,你還是可以肯定 Bridge 模式在這個設(shè)計中是很有用的。以后你就會發(fā)現(xiàn),這種情況在設(shè)計模式領(lǐng)域里很常見,那就是,你可以確定你應(yīng)該把某個模式應(yīng)用到你的設(shè)計當(dāng)中,盡管,你還并不知道該怎么去實現(xiàn)這個模式。


          Bridge 模式的引出

          相信你已經(jīng)很清楚我們所要處理的問題了,那么,現(xiàn)在我們就用 Bridge 模式來解決我們的問題。這個過程將幫助你更加深入地理解這個復(fù)雜而強(qiáng)大的 Bridge 模式。

          我們還是要遵循那兩個“原則”:

          1. 找到什么是變化的并封裝它。

          2. 盡量使用聚合而不是繼承。

          以前,開發(fā)人員總是依靠大量的繼承來處理程序中的變化,而第二個“原則”告訴我要盡可能的使用聚合。使用聚合的目的就是讓程序可以在單獨的類里面包含它自己的變化,這樣,今后有新變化出現(xiàn)的時候就可以通過添加新的類來包含這個變化,而不會對原先的類造成影響。要達(dá)到這個目的,我們就需要把所有的變化都包含到它們共同的抽象類里面,然后,我們再去考慮抽象類之間的聯(lián)系,而不再考慮抽象類的具體實現(xiàn)。

          再說封裝

          很多面向?qū)ο蟪绦蛟O(shè)計的開發(fā)人員都知道“封裝”就是數(shù)據(jù)隱藏。其實,這是一個很狹隘的定義!實際上,“封裝”除了數(shù)據(jù)隱藏以外,還有很多其他的用法。如果你再回過頭去看看圖 7-2 ,你會在里面發(fā)現(xiàn)“封裝”可以操縱很多個層次。當(dāng)然,那里,封裝的目的就是為每個 Shape 做數(shù)據(jù)隱藏。然而,要注意的是, Client 對象并不知道它所處理的 Shape 的具體類型 ( 到底是 Rectangle 還是 Circle) 。因此,那個具體被 Client 所處理的類就通過 Shape 而實現(xiàn)了隱藏 ( 也就是封裝 ) ,這也就是 GoF 在說到“找到什么是變化的并封裝它”的時候所指的“封裝”,讓抽象類 (Shape) 出面去和 Client 對象交互,具體的 Rectangle Circle 這些類都“躲”在了 Shape 類的后面, Client 只知道自己在和 Shape 交互,其他一無所知。

          現(xiàn)在我具體討論一下本章里一直在講的這個繪圖的例子。

          首先,找出什么是變化的。在這個例子里,也就是不同的形狀和不同的繪圖程序。而這些不同的形狀、不同的繪圖程序在概念上,分屬于“形狀”和“繪圖程序”,這樣,我就可以用 10-9 所示的類來代表這些不同的形狀和程序 ( 注意圖里面的類名是斜體的,這表示他們都是抽象類 ) 。

          o_10-9.PNG

          Figure 10-9 What is varying?

          這樣,我打算用 Shape 來封裝這個例子里的所有的不同的形狀,而代表這些不同的形狀的類將必須自己能夠完成他本身的繪制工作。 Drawing 類的對象將僅僅負(fù)責(zé)完成“線”和“圓”等具體線條的繪制工作。這些具體的繪制,都將通過在類里定義的一些方法來完成。

          現(xiàn)在我們已經(jīng)很抽象地表示出“形狀”和“繪圖程序”了,下一步,就是怎么表示具體的形狀和具體的繪圖程序的問題了。我有的“形狀”是矩形和圓,那么,我就從 Shape 里派生出 Rectangle Circle 來分別代表矩形和圓。“繪圖程序”我必須使用已有的 DP1 DP2 ,但是我又不能直接使用它們,所以,我可以從 Drawing 派生出 V1Drawing V2Drawing ,讓他們分別去使用 DP1 DP2 。這樣,就得到如圖 10-10 所示的類圖。

          o_10-10.PNG

          Figure 10-10 Represent the variations.

          到目前為止,這一切看起來還是有點抽象。我只說了 V1Drawing V2Drawing 要分別對應(yīng)地使用到 DP1 DP2 ,但是我還沒有說怎么用。我只是把整個例子中存在的一些變化找了出來而已。

          有了這兩組類,我現(xiàn)在要關(guān)心的就是它們之間怎么彼此關(guān)聯(lián)起來的問題了。我不想再出現(xiàn)一組用同樣繼承的方式出現(xiàn)的類,這樣的結(jié)果我已經(jīng)很清楚了 ( 再回過頭去看看圖 10-3 10-7 ,加深點印象 ) 。相反地,我在想,我是不是可以通過用一組類使用另一組的方式來完成 ( 這不就是用聚合了嗎? ) ?,F(xiàn)在的問題是,兩組類,到底誰用誰?

          很明顯的兩種可能性:要么在 Shape 里用到 Drawing ,要么反過來, Drawing 里用到 Shape 。

          我們先來看看后面這種 Drawing 里使用 Shape 的情況,如果繪圖程序直接使用到具體的形狀,那么這些程序就必須知道該怎么去完成這些具體形狀的繪制。但是,這些形狀僅僅只知道如何完成“線”和“圓”這些簡單線條的繪制,這,超出了它們的能力范圍。

          那么,再看看前面的那種情況。如果在 Shape 里用 Drawing 會怎么樣? Shape 將沒有必要知道它所使用的 Drawing 的確切類型,因為,我們已經(jīng)用 Drawing 封裝了 V1Drawing V2Drawing 。 這樣看起來不錯,就像圖 10-11 所示的那樣。

          o_10-11.PNG

          Figure 10-11 Tie the classes together.

          在這個設(shè)計里, Shape 通過 Drawing 來完成自己的繪制。這里,我略去了 V1Drawing 使用 DP1 V2Drawing 使用 DP2 的細(xì)節(jié),在圖 10-12 里,我把這些都表示了出來還加入了一些 protected 的方法。

          o_10-12.PNG

          Figure 10-12 Expanding the design.

          10-13 所示的是把 Shape( 抽象 ) Drawing( 實現(xiàn) ) 分離開來。

          o_10-13.PNG

          Figure 10-13 Class diagram illustrating separation of abstraction and implementation.

          一個規(guī)則,一個地方

          我們必須遵循的一個非常重要的實現(xiàn)策略就是,一個規(guī)則僅僅應(yīng)用到一個地方。也就是說,如果你有一個處理事務(wù)的規(guī)則,這個規(guī)則就僅用一次就好。很顯然這會導(dǎo)致設(shè)計里出現(xiàn)很多的小方法。但是,這么做你可以避免一些預(yù)想不到的問題的出現(xiàn),可以避免因為規(guī)則相似,而復(fù)制原有代碼再修改滿足新規(guī)則的情況,而這么做的代價是很小的。

          雖然 Rectangle 或者 draw() 方法都可以直接操縱 Shape 的任意的 Drawing 對象,我可以通過使用下面的“一個規(guī)則,一個地方”的策略來改進(jìn)這點。我讓 Shape drawLine() 方法來調(diào)用 Drawing 對象的 drawLine() 。

          當(dāng)然,凡事也不是那么的絕對。如果,我認(rèn)為某個地方應(yīng)該一直遵守某種規(guī)則的話,就像這個例子一樣。我的 Shape 類有個 drawLine() ,因為它代表著我要用 Drawing 來繪制一條直線;我同樣可以用這樣的方式創(chuàng)建一個 drawCircle() ,因為它代表著用 Drawing 繪制一個圓。

          站在各個類里面的具體方法 (method) 的角度來看,這和基于繼承的設(shè)計方案 ( 如圖 10-3) 沒什么不同。如果非要說有什么不同,那就是現(xiàn)在這些方法存在的位置不同,他們不再是在同一個類里面,而是被分散到多個類里面。

          在這一章一開始我就說了,我對 Bridge 模式的困惑是源于我對“實現(xiàn)”的誤解。我以為“實現(xiàn)”指的是怎么實現(xiàn)一個具體的抽象。

          bridge 模式讓我意識到,應(yīng)該把“實現(xiàn)”看作是一個對象以外的東西,是一個為對象所用的東西。用這樣的方式著手設(shè)計,在不同的繼承鏈里包含各自的變化。圖 10-13 左側(cè)的繼承鏈包含了抽象的變化,右側(cè)的繼承鏈包含的則是我如何實現(xiàn)這些抽象的變化。這和我們說要“盡量使用聚合而不是繼承”的原則相一致。

          雖然在整個設(shè)計里有不少的類,但是,你只要牢記一點,你一次要處理的就只有三個對象,這樣你的思路就會很清晰。

          o_10-14.PNG

          Figure 10-14 There are only three objects at a time.

          Example 10-3 Java Code Fragments

          public ? class ?Client?{
          ????
          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 模式主要功能

          目標(biāo)

          把具體實現(xiàn)從使用該實現(xiàn)的對象中分離出來。

          環(huán)境

          ( 概念 ) 抽象類的派生類需要使用多個實現(xiàn)方法而又要避免類的數(shù)量的過多。

          解決方式

          給所有實現(xiàn)定義一個公共接口并讓 ( 概念 ) 抽象的派生類使用它。

          參與方式

            Abstraction 給要實現(xiàn)的對象定義一個接口。

            Implementor 給具體的實現(xiàn)定義接口, Abstraction 的派生類使用 Implementor 的派生類而不管到底是用的哪一個 ConcreteImplementor 。

          結(jié)果

            把具體實現(xiàn)從使用該實現(xiàn)的對象中分離出來的方法增加了程序的靈活性,客戶端對象不會知道實現(xiàn)的確切類型。

          執(zhí)行方式

          1. 把所有實現(xiàn)封裝在一個抽象類里。

          2. 在要實現(xiàn)的 ( 概念 ) 抽象里包含上面的抽象類。

          o_10-15.PNG

          Figure 10-15 Generic structure of the Bridge pattern.


          posted on 2006-11-18 14:35 xiaosilent 閱讀(1898) 評論(2)  編輯  收藏 所屬分類: 設(shè)計模式

          評論:
          # re: [試譯] The Bridge Pattern. 2006-11-19 00:38 | Love橘子
          辛苦了哦!
          只想問問翻譯這么一篇文章得花多少時間?  回復(fù)  更多評論
            
          # re: [試譯] The Bridge Pattern. 2006-11-19 08:47 | xiaosilent
          一個上午吧,趕時間,從原文省略了很多的。
          可以看原文:http://www.aygfsteel.com/xiaosilent/archive/2006/11/12/80682.html  回復(fù)  更多評論
            
          主站蜘蛛池模板: 同仁县| 云浮市| 昭平县| 罗源县| 读书| 安泽县| 黑河市| 凤冈县| 兰州市| 大连市| 信丰县| 南丹县| 永安市| 铜山县| 灵寿县| 肇源县| 桐城市| 二手房| 天峻县| 德州市| 崇文区| 丹江口市| 连江县| 九龙县| 南阳市| 通江县| 呼和浩特市| 尤溪县| 三亚市| 永城市| 稻城县| 科技| 台前县| 道孚县| 大关县| 久治县| 大荔县| 吉木乃县| 潢川县| 广丰县| 莱西市|