每日一得

          不求多得,只求一得 about java,hibernate,spring,design,database,Ror,ruby,快速開發
          最近關心的內容:SSH,seam,flex,敏捷,TDD
          本站的官方站點是:顛覆軟件

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            220 隨筆 :: 9 文章 :: 421 評論 :: 0 Trackbacks

          一、 引子

          在大學的數據結構這門課上,樹是最重要的章節之一。還記得樹是怎么定義的嗎?樹 (Tree) n(n≥0) 個結點的有限集 T T 為空時稱為空樹,否則它滿足如下兩個條件:

          (1)??? 有且僅有一個特定的稱為根 (Root) 的結點;

          (2)?? 其余的結點可分為 m(m≥0) 個互不相交的子集 Tl T2 Tm ,其中每個子集本身又是一棵樹,并稱其為根的子樹 (SubTree)

          上面給出的遞歸定義刻畫了樹的固有特性:一棵非空樹是由若干棵子樹構成的,而子樹又可由若干棵更小的子樹構成。而這里的子樹可以是葉子也可以是分支。

          今天要學習的組合模式就是和樹型結構以及遞歸有關系。

          ?

          二、 定義與結構

          組合 (Composite) 模式的其它翻譯名稱也很多,比如合成模式、樹模式等等。在《設計模式》一書中給出的定義是:將對象以樹形結構組織起來,以達成 部分-整體 的層次結構,使得客戶端對單個對象和組合對象的使用具有一致性。

          從定義中可以得到使用組合模式的環境為: 在設計中想表示對象的 部分 整體 層次結構;希望用戶忽略組合對象與單個對象的不同,統一地使用組合結構中的所有對象。

          看下組合模式的組成。

          1)???????? 抽象構件角色 Component :它為組合中的對象聲明接口,也可以為共有接口實現缺省行為。

          2)?????? 樹葉構件角色 Leaf :在組合中表示葉節點對象 —— 沒有子節點,實現抽象構件角色聲明的接口。

          3)?????? 樹枝構件角色 Composite :在組合中表示分支節點對象 —— 有子節點,實現抽象構件角色聲明的接口;存儲子部件。

          下圖為組合模式的類圖表示。

          ?

          如圖所示:一個 Composite 實例可以像一個簡單的 Leaf 實例一樣,可以把它傳遞給任何使用 Component 的方法或者對象,并且它表現的就像是一個 Leaf 一樣。

          可以看出來,使用組合模式使得這個設計結構非常靈活,在下面的例子中會得到進一步的印證。

          ??????

          三、 安全性與透明性

          組合模式中必須提供對子對象的管理方法,不然無法完成對子對象的添加刪除等等操作,也就失去了靈活性和擴展性。但是管理方法是在 Component 中就聲明還是在 Composite 中聲明呢?

          一種方式是在 Component 里面聲明所有的用來管理子類對象的方法,以達到 Component 接口的最大化(如下圖所示)。目的就是為了使客戶看來在接口層次上樹葉和分支沒有區別 —— 透明性。但樹葉是不存在子類的,因此 Component 聲明的一些方法對于樹葉來說是不適用的。這樣也就帶來了一些安全性問題。

          ?

          另一種方式就是只在 Composite 里面聲明所有的用來管理子類對象的方法(如下圖所示)。這樣就避免了上一種方式的安全性問題,但是由于葉子和分支有不同的接口,所以又失去了透明性。

          ????


          ????《設計模式》一書認為:在這一模式中,相對于安全性,我們比較強調透明性。對于第一種方式中葉子節點內不需要的方法可以使用空處理或者異常報告的方式來解決。

          ?

          四、 舉例

          這里以 JUnit 中的組合模式的應用為例(JUnit)。

          JUnit 是一個單元測試框架,按照此框架下的規范來編寫測試代碼,就可以使單元測試自動化。為了達到“自動化”的目的, JUnit 中定義了兩個概念: TestCase TestSuite TestCase 是對一個類或者 jsp 等等編寫的測試類;而 TestSuite 是一個不同 TestCase 的集合,當然這個集合里面也可以包含 TestSuite 元素,這樣運行一個 TestSuite 會將其包含的 TestCase 全部運行。

          然而在真實運行測試程序的時候,是不需要關心這個類是 TestCase 還是 TestSuite ,我們只關心測試運行結果如何。這就是為什么 JUnit 使用組合模式的原因。

          JUnit 為了采用組合模式將 TestCase TestSuite 統一起來,創建了一個 Test 接口來扮演抽象構件角色,這樣原來的 TestCase 扮演組合模式中樹葉構件角色,而 TestSuite 扮演組合模式中的樹枝構件角色。下面將這三個類的有關代碼分析如下:

          ?

          //Test 接口 —— 抽象構件角色

          public interface Test {

          ?????? /**

          ?????? ?* Counts the number of test cases that will be run by this test.

          ?????? ?*/

          ?????? public abstract int countTestCases();

          ?????? /**

          ?????? ?* Runs a test and collects its result in a TestResult instance.

          ?????? ?*/

          ?????? public abstract void run(TestResult result);

          }

          ?

          //TestSuite 類的部分有關源碼 ——Composite 角色,它實現了接口 Test

          public class TestSuite implements Test {

          // 用了較老的 Vector 來保存添加的 test

          ?????? private Vector fTests= new Vector(10);

          ?????? private String fName;

          ?????? ……?

          /**

          ?????? ?* Adds a test to the suite.

          ?????? ?*/

          ?????? public void addTest(Test test) {??????????

          // 注意這里的參數是 Test 類型的。這就意味著 TestCase TestSuite 以及以后實現 Test 接口的任何類都可以被添加進來

          ????????????? fTests.addElement(test);

          ?????? }

          ?????? ……

          ?????? /**

          ?????? ?* Counts the number of test cases that will be run by this test.

          ?????? ?*/

          ?????? public int countTestCases() {

          ????????????? int count= 0;

          ????????????? for (Enumeration e= tests(); e.hasMoreElements(); ) {

          ???????????????????? Test test= (Test)e.nextElement();

          ???????????????????? count= count + test.countTestCases();

          ????????????? }

          ????????????? return count;

          ?????? }

          ?????? /**

          ?????? ?* Runs the tests and collects their result in a TestResult.

          ?????? ?*/

          ?????? public void run(TestResult result) {

          ????????????? for (Enumeration e= tests(); e.hasMoreElements(); ) {

          ?????? ? ?????????? if (result.shouldStop() )

          ?????? ? ????????????????? break;

          ???????????????????? Test test= (Test)e.nextElement();
          ?????????????????????????? //關鍵在這個方法上面

          ???????????????????? runTest(test, result);

          ????????????? }

          ?????? }
          ????????????//這個方法里面就是遞歸的調用了,至于你的Test到底是什么類型的只有在運行的時候得知
          ????????????public void runTest(Test test, TestResult result) {
          ?????????????????? test.run(result);
          ????????????}

          ……

          }

          ?

          //TestCase 的部分有關源碼 ——Leaf 角色,你編寫的測試類就是繼承自它

          public abstract class TestCase extends Assert implements Test {

          ?????? ……

          ?????? /**

          ?????? ?* Counts the number of test cases executed by run(TestResult result).

          ?????? ?*/

          ?????? public int countTestCases() {

          ????????????? return 1;

          ?????? }

          /**

          ?????? ?* Runs the test case and collects the results in TestResult.

          ?????? ?*/

          ?????? public void run(TestResult result) {

          ????????????? result.run(this);

          ?????? }

          ……

          }

          ?????? 可以看出這是一個偏重安全性的組合模式。因此在使用TestCase和TestSuite時,不能使用Test來代替。

          ?

          五、 優缺點

          從上面的舉例中可以看到,組合模式有以下優點:

          1)???????? 使客戶端調用簡單,客戶端可以一致的使用組合結構或其中單個對象,用戶就不必關心自己處理的是單個對象還是整個組合結構,這就簡化了客戶端代碼。

          2)?????? 更容易在組合體內加入對象部件 . 客戶端不必因為加入了新的對象部件而更改代碼。這一點符合開閉原則的要求,對系統的二次開發和功能擴展很有利!

          當然組合模式也少不了缺點:組合模式不容易限制組合中的構件。

          ?

          六、 總結

          組合模式是一個應用非常廣泛的設計模式,在前面已經介紹過的解釋器模式、享元模式中都是用到了組合模式。它本身比較簡單但是很有內涵,掌握了它對你的開發設計有很大的幫助。

          這里寫下了我學習組合模式的總結,希望能給你帶來幫助,也希望您能給與指正。
          posted on 2006-08-29 21:44 Alex 閱讀(462) 評論(0)  編輯  收藏 所屬分類: design
          主站蜘蛛池模板: 玉门市| 抚顺市| 新沂市| 会理县| 临沧市| 岱山县| 马关县| 金秀| 新沂市| 醴陵市| 陇川县| 同仁县| 若羌县| 锡林浩特市| 汝南县| 夏津县| 平乐县| 色达县| 龙州县| 仁寿县| 巴马| 南投市| 隆德县| 华宁县| 英吉沙县| 房产| 亚东县| 唐河县| 大连市| 天全县| 香河县| 定边县| 杭州市| 南郑县| 清水河县| 惠州市| 承德市| 中牟县| 咸宁市| 克拉玛依市| 旌德县|