每日一得

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

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            220 隨筆 :: 9 文章 :: 421 評論 :: 0 Trackbacks
          <2006年8月>
          303112345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          常用鏈接

          留言簿(23)

          隨筆分類(240)

          隨筆檔案(219)

          文章分類(9)

          文章檔案(9)

          收藏夾(15)

          java link

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          一、引子

          裝飾模式?肯定讓你想起又黑又火的家庭裝修來。其實(shí)兩者在道理上還是有很多相像的地方。家庭裝修無非就是要掩蓋住原來實(shí)而不華的墻面,抹上一層華而不實(shí)的涂料,讓生活多一點(diǎn)色彩。而墻還是那堵墻,他的本質(zhì)一點(diǎn)都沒有變,只是多了一層外衣而已。

          那設(shè)計(jì)模式中的裝飾模式,是什么樣子呢?

          ?

          二、定義與結(jié)構(gòu)

          裝飾模式( Decorator )也叫包裝器模式( Wrapper )。 GOF 在《設(shè)計(jì)模式》一書中給出的定義為:動(dòng)態(tài)地給一個(gè)對象添加一些額外的職責(zé)。就增加功能來說, Decorator 模式相比生成子類更為靈活。

          ?????? 讓我們來理解一下這句話。我們來設(shè)計(jì) 這個(gè)類。假設(shè)你根據(jù)需求為 類作了如下定義:

          現(xiàn)在,在系統(tǒng)的一個(gè)地方需要一個(gè)能夠報(bào)警的 Door ,你來怎么做呢?你或許寫一個(gè) Door 的子類 AlarmDoor ,在里面添加一個(gè)子類獨(dú)有的方法 alarm() 。嗯,那在使用警報(bào)門的地方你必須讓客戶知道使用的是警報(bào)門,不然無法使用這個(gè)獨(dú)有的方法。而且,這個(gè)還違反了 Liskov 替換原則。

          也許你要說,那就把這個(gè)方法添加到 Door 里面,這樣不就統(tǒng)一了?但是這樣所有的門都必須有警報(bào),至少是個(gè) 啞巴 警報(bào)。而當(dāng)你的系統(tǒng)僅僅在一兩個(gè)地方使用了警報(bào)門,這明顯是不合理的 —— 雖然可以使用缺省適配器來彌補(bǔ)一下。

          ?????? 這時(shí)候,你可以考慮采用裝飾模式來給門動(dòng)態(tài)的添加些額外的功能。

          ?????? 下面我們來看看裝飾模式的組成,不要急著去解決上面的問題,到了下面自然就明白了!

          1) ??????? 抽象構(gòu)件角色( Component ):定義一個(gè)抽象接口,以規(guī)范準(zhǔn)備接收附加責(zé)任的對象。

          2) ??????? 具體構(gòu)件角色 (Concrete Component) :這是被裝飾者,定義一個(gè)將要被裝飾增加功能的類。

          3) ??????? 裝飾角色 (Decorator) :持有一個(gè)構(gòu)件對象的實(shí)例,并定義了抽象構(gòu)件定義的接口。

          4) ??????? 具體裝飾角色 (Concrete Decorator) :負(fù)責(zé)給構(gòu)件添加增加的功能。

          看下裝飾模式的類圖:

          圖中 ConcreteComponent 可能繼承自其它的體系,而為了實(shí)現(xiàn)裝飾模式,他還要實(shí)現(xiàn) Component 接口。整個(gè)裝飾模式的結(jié)構(gòu)是按照組合模式來實(shí)現(xiàn)的,但是注意兩者的目的是截然不同的(關(guān)于兩者的不同請關(guān)注我以后的文章)。

          ?

          三、舉例

          這個(gè)例子還是來自我最近在研究的 JUnit ,如果你對 JUnit 還不太了解,可以參考 JUnit入門》 JUnit源碼分析(一)》 JUnit源碼分析(二)》 JUnit源碼分析(三)》 。不愧是由 GoF 之一的 Erich Gamma 親自開發(fā)的,小小的東西使用了 N 種的模式在里面。下面就來看看 JUnit 中的裝飾模式。

          ?????? JUnit 中, TestCase 是一個(gè)很重要的類,允許對其進(jìn)行功能擴(kuò)展。

          ?????? junit.extensions 包中, TestDecorator RepeatedTest 便是對 TestCase 的裝飾模式擴(kuò)展。下面我們將它們和上面的角色對號入座。

          ?????? 呵呵,看看源代碼吧,這個(gè)來的最直接!

          ?????? // 這個(gè)就是抽象構(gòu)件角色

          ?????? 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);

          }

          ?

          // 具體構(gòu)件對象,但是這里是個(gè)抽象類

          public abstract class TestCase extends Assert implements Test {

          ?????? ……

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

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

          ?????? }

          ?????? ……

          ?????? public TestResult run() {

          ????????????? TestResult result= createResult();

          ????????????? run(result);

          ????????????? return result;

          ?????? }

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

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

          ?????? }

          ?????? ……

          }

          ?

          // 裝飾角色

          public class TestDecorator extends Assert implements Test {

          ?????? // 這里按照上面的要求,保留了一個(gè)對構(gòu)件對象的實(shí)例

          ?????? protected Test fTest;

          ?

          ?????? public TestDecorator(Test test) {

          ????????????? fTest= test;

          ?????? }

          ?????? /**

          ?????? ?* The basic run behaviour.

          ?????? ?*/

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

          ????????????? fTest.run(result);

          ?????? }

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

          ????????????? return fTest.countTestCases();

          ?????? }

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

          ????????????? basicRun(result);

          ?????? }

          ?????? public String toString() {

          ????????????? return fTest.toString();

          ?????? }

          ?????? public Test getTest() {

          ????????????? return fTest;

          ?????? }

          }

          ?

          ?????? // 具體裝飾角色,這個(gè)類的增強(qiáng)作用就是可以設(shè)置測試類的執(zhí)行次數(shù)

          public class RepeatedTest extends? TestDecorator {

          ??? private int fTimesRepeat;

          ?

          ??? public RepeatedTest(Test test, int repeat) {

          ?????????? super(test);

          ?????????? if (repeat < 0)

          ????????????????? throw new IllegalArgumentException("Repetition count must be > 0");

          ?????????? fTimesRepeat= repeat;

          ??? }

          ??? // 看看怎么裝飾的吧

          ??? public int countTestCases() {

          ?????????? return super.countTestCases()*fTimesRepeat;

          ??? }

          ??? public void run(TestResult result) {

          ?????????? for (int i= 0; i < fTimesRepeat; i++) {

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

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

          ????????????????? super.run(result);

          ?????????? }

          ??? }

          ??? public String toString() {

          ?????????? return super.toString()+"(repeated)";

          ??? }

          }

          ?????? 使用的時(shí)候,就可以采用下面的方式:

          TestDecorator test = new RepeatedTest(new TestXXX() , 3);

          讓我們在回想下上面提到的 的問題,這個(gè)警報(bào)門采用了裝飾模式后,可以采用下面的方式來產(chǎn)生。

          DoorDecorator alarmDoor = new AlarmDoor(new Door());

          ?

          ?

          四、應(yīng)用環(huán)境

          ?????? GOF 書中給出了以下使用情況:

          1) ??????? 在不影響其他對象的情況下,以動(dòng)態(tài)、透明的方式給單個(gè)對象添加職責(zé)。

          2) ??????? 處理那些可以撤消的職責(zé)。

          3) ??????? 當(dāng)不能采用生成子類的方法進(jìn)行擴(kuò)充時(shí)。一種情況是,可能有大量獨(dú)立的擴(kuò)展,為支持每一種組合將產(chǎn)生大量的子類,使得子類數(shù)目呈爆炸性增長。另一種情況可能是因?yàn)轭惗x被隱藏,或類定義不能用于生成子類。

          來分析下 JUnit 的使用是屬于哪種情況。首先實(shí)現(xiàn)了比靜態(tài)繼承更加靈活的方式,動(dòng)態(tài)的增加功能。試想為 Test 的所有實(shí)現(xiàn)類通過繼承來增加一個(gè)功能,意味著要添加不少的功能類似的子類,這明顯是不太合適的。

          而且,這就避免了高層的類具有太多的特征,比如上面提到的帶有警報(bào)的抽象門類。

          ?

          五、透明和半透明

          ?????? 對于面向接口編程,應(yīng)該盡量使客戶程序不知道具體的類型,而應(yīng)該對一個(gè)接口操作。這樣就要求裝飾角色和具體裝飾角色要滿足 Liskov 替換原則。像下面這樣:

          Component c = new ConcreteComponent();

          Component c1 = new ConcreteDecorator(c);

          JUnit 中就屬于這種應(yīng)用,這種方式被稱為透明式。而在實(shí)際應(yīng)用中,比如 java.io 中往往因?yàn)橐獙υ薪涌谧鎏嗟臄U(kuò)展而需要公開新的方法(這也是為了重用)。所以往往不能對客戶程序隱瞞具體的類型。這種方式稱為 半透明式

          java.io 中,并不是純裝飾模式的范例,它是裝飾模式、適配器模式的混合使用。

          ?

          六、其它

          采用 Decorator 模式進(jìn)行系統(tǒng)設(shè)計(jì)往往會(huì)產(chǎn)生許多看上去類似的小對象,這些對象僅僅在他們相互連接的方式上有所不同,而不是它們的類或是它們的屬性值有所不同。盡管對于那些了解這些系統(tǒng)的人來說,很容易對它們進(jìn)行定制,但是很難學(xué)習(xí)這些系統(tǒng),排錯(cuò)也很困難。這是 GOF 提到的裝飾模式的缺點(diǎn),你能體會(huì)嗎?他們所說的小對象我認(rèn)為是指的具體裝飾角色。這是為一個(gè)對象動(dòng)態(tài)添加功能所帶來的副作用。

          ?

          七、總結(jié)

          ?????? 終于寫完了,不知道說出了本意沒有。請指正!
          posted on 2006-08-29 18:57 Alex 閱讀(509) 評論(0)  編輯  收藏 所屬分類: design
          主站蜘蛛池模板: 济阳县| 凌云县| 六枝特区| 潮安县| 北安市| 阜阳市| 高雄市| 醴陵市| 屏东市| 龙江县| 江都市| 宜都市| 庆阳市| 新竹县| 香港| 曲麻莱县| 沛县| 斗六市| 忻州市| 托克逊县| 和政县| 北流市| 克东县| 镇宁| 孟津县| 景谷| 隆安县| 通河县| 米易县| 竹山县| 托里县| 阳信县| 合作市| 太和县| 南丹县| 闻喜县| 鸡西市| 莱阳市| 百色市| 祁阳县| 宁南县|