從Java類(lèi)庫(kù)看設(shè)計(jì)模式_第1頁(yè)
從Java類(lèi)庫(kù)看設(shè)計(jì)模式_第2頁(yè)
從Java類(lèi)庫(kù)看設(shè)計(jì)模式_第3頁(yè)
從Java類(lèi)庫(kù)看設(shè)計(jì)模式_第4頁(yè)
從Java類(lèi)庫(kù)看設(shè)計(jì)模式_第5頁(yè)
已閱讀5頁(yè),還剩23頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、從Java類(lèi)庫(kù)看設(shè)計(jì)模式(1) Observer模式Observer模式的功用,是希望兩個(gè)(或多個(gè))對(duì)象,我們稱(chēng)之為Subject和Observer,當(dāng)一方的狀態(tài)發(fā)生改變的時(shí)候,另一方能夠得 到通知。也就是說(shuō),作為Observer的一方,能夠監(jiān)視到Subject的某個(gè)特定的狀態(tài)變化,并為之做出反應(yīng)。一個(gè)簡(jiǎn)單的例子就是:當(dāng)一個(gè)用戶(hù)視圖中 的數(shù)據(jù)被用戶(hù)改變后,后端的數(shù)據(jù)庫(kù)能夠得到更新,而當(dāng)數(shù)據(jù)庫(kù)被其他方式更新后,用戶(hù)視圖中的數(shù)據(jù)顯示也會(huì)隨之改變。圖一:Obverser模式的類(lèi)圖在JDK中實(shí)際上有一個(gè)對(duì)Observer模式的簡(jiǎn)單的實(shí)現(xiàn):就是類(lèi)java.util.Observerable和接口 jav

2、a.util.Observer。java.util.Observerable類(lèi)對(duì)應(yīng)于Subject,而java.util.Observer 就是觀察者了。JDK中并沒(méi)有把這兩個(gè)部分都設(shè)計(jì)為接口,而是讓類(lèi)java.util.Observerable提供了部分的實(shí)現(xiàn),簡(jiǎn)化了許多編程的工 作。當(dāng)然,這也減少了一定的靈活性。下面列出了Observer和Observeral的函數(shù)列表,及其簡(jiǎn)單的功能說(shuō)明java.util.Observer: public void update(Observable obs, Object obj) java.util.Observer 接口很簡(jiǎn)單,只定義了這一個(gè)方法,

3、狹義的按照Observer模式的說(shuō)法,Observer應(yīng)該在這個(gè)方法中調(diào)用Subject的getXXX()方法來(lái) 取得最新的狀態(tài),而實(shí)際上,你可以只是在其中對(duì)Subject的某些事件進(jìn)行響應(yīng)。這便是Java中的代理事件模型的一個(gè)雛形-對(duì)事件進(jìn)行響應(yīng)。只不 過(guò),在Observer模式中將事件特定化為某個(gè)狀態(tài)/數(shù)據(jù)的改變了。 java.util.Observablepublic void addObserver(Observer obs)向Subject注冊(cè)一個(gè)Observer。也就是把這個(gè)Observer對(duì)象添加到了一個(gè)java.util.Observable內(nèi)部的列表 中。在JDK中對(duì)于這個(gè)列

4、表是簡(jiǎn)單的通過(guò)一個(gè)java.util.Vector類(lèi)來(lái)實(shí)現(xiàn)的,而實(shí)際上,在一些復(fù)雜的Observer模式的應(yīng)用中,需要把 這個(gè)部分單另出來(lái)形成一個(gè)Manager類(lèi),來(lái)管理Subject和Observer之間的映射。這樣,Subject和Observer進(jìn)一步的被解 藕,程序也會(huì)具有更大的靈活性。public void deleteObserver(Observer obs) 從Subject中刪除一個(gè)已注冊(cè)了Observer的引用。 protected void setChanged() 設(shè)置一個(gè)內(nèi)部的標(biāo)志以指明這個(gè)Ovserver的狀態(tài)已經(jīng)發(fā)生改變。注意這是一個(gè)protected方法,也就是

5、說(shuō)只能在Observer類(lèi)和其子類(lèi)中被調(diào)用,而在其它的類(lèi)中是看不到這個(gè)方法的。 public void notifyObservers(Object obj) 它首先檢查那個(gè)內(nèi)部的標(biāo)志,以判斷狀態(tài)是否改變,如果 是的話,它會(huì)調(diào)用注冊(cè)在Subject中的每個(gè)Observer的update()方法。在JDK中這個(gè)方法內(nèi)部是作為synchronized來(lái)實(shí)現(xiàn) 的,也就是如果發(fā)生多個(gè)線程同時(shí)爭(zhēng)用一個(gè)java.util.Observerable的notifyObservers()方法的話,他們必須按調(diào)度的 等待著順序執(zhí)行。在某些特殊的情況下,這會(huì)有一些潛在的問(wèn)題:可能在等待的過(guò)程中,一個(gè)剛剛被加入的Ob

6、server會(huì)被遺漏沒(méi)有被通知到,而一個(gè)剛剛被 刪除了的Observer會(huì)仍然收到它已經(jīng)不想要了的通知。 1packagejavamodel.observer;3importjava.util.Observable;4importjava.util.Observer;7*貓叫,老鼠跑,主人被驚醒,用代碼實(shí)現(xiàn)這個(gè)連鎖反應(yīng)。8*authorAdministrator1112publicclassTestObserver14publicstaticvoidmain(Stringargs)15Catc=newCat();16Observero1=newMouse();17Observero2=newM

7、an();18c.addObserver(o1);19Mouseoo1=(Mouse)o1;/向下轉(zhuǎn)型20oo1.addObserver(o2);21c.CatSay();22242526classCatextendsObservable/貓被老鼠觀察,貓是被觀察者28publicvoidCatSay()29System.out.println(貓叫了);30this.setChanged();31this.notifyObservers();32333435classMouseextendsObservableimplementsObserver/老鼠觀察貓,貓是觀察者,對(duì)于人,老鼠是被觀察

8、者37publicvoidupdate(Observablearg0,Objectarg1)38/TODOAuto-generatedmethodstub39System.out.println(貓叫了,老鼠跑了);40this.setChanged();41this.notifyObservers();424446classManimplementsObserver/人觀察老鼠,人是觀察者48publicvoidupdate(Observablearg0,Objectarg1)49/TODOAuto-generatedmethodstub50System.out.println(老鼠跑了,人

9、驚醒了);5152我認(rèn)為在JDK中這個(gè)Observer模式的實(shí)現(xiàn),對(duì)于一般的Observer模式的應(yīng)用,已經(jīng)是非常的足夠了的。但是一方面它用一個(gè)類(lèi)來(lái)實(shí)現(xiàn)了 Subject,另一方面它使用Vector來(lái)保存Subject對(duì)于Observer的引用,這雖然簡(jiǎn)化了編程的過(guò)程,但會(huì)限制它在一些需要更為靈活, 復(fù)雜的設(shè)計(jì)中的應(yīng)用,有時(shí)候(雖然這種情況不多),我們還不得不重新編寫(xiě)新的Subject對(duì)象和額外的Manager對(duì)象來(lái)實(shí)現(xiàn)更為復(fù)雜的 Observer模式的應(yīng)用。小結(jié):觀察者模式的應(yīng)用場(chǎng)景:1、對(duì)一個(gè)對(duì)象狀態(tài)的更新,需要其他對(duì)象同步更新,而且其他對(duì)象的數(shù)量動(dòng)態(tài)可變。2、對(duì)象僅需要將自己的更新通知給

10、其他對(duì)象而不需要知道其他對(duì)象的細(xì)節(jié)。觀察者模式的優(yōu)點(diǎn):1、Subject和Observer之間是松偶合的,分別可以各自獨(dú)立改變。2、Subject在發(fā)送廣播通知的時(shí)候,無(wú)須指定具體的Observer,Observer可以自己決定是否要訂閱Subject的通知。3、遵守大部分GRASP原則和常用設(shè)計(jì)原則,高內(nèi)聚、低偶合。觀察者模式的缺陷:1、松偶合導(dǎo)致代碼關(guān)系不明顯,有時(shí)可能難以理解。(廢話)2、如果一個(gè)Subject被大量Observer訂閱的話,在廣播通知的時(shí)候可能會(huì)有效率問(wèn)題。(畢竟只是簡(jiǎn)單的遍歷)從Java類(lèi)庫(kù)看設(shè)計(jì)模式(2) Command模式在設(shè)計(jì)一般用途的軟件的時(shí)候,在C或者C+語(yǔ)

11、言中,用的很多的一個(gè)技巧就是回調(diào)函數(shù)(Callback),所謂的回調(diào)函數(shù),意指先在系統(tǒng)的某個(gè)地 方對(duì)函數(shù)進(jìn)行注冊(cè),讓系統(tǒng)知道這個(gè)函數(shù)的存在,然后在以后,當(dāng)某個(gè)事件發(fā)生時(shí),再調(diào)用這個(gè)函數(shù)對(duì)事件進(jìn)行響應(yīng)。在C或者C+中,實(shí)現(xiàn)的回調(diào)函數(shù)方法是使 用函數(shù)指針。但是在Java中,并不支持指針,因而就有了Command模式,這一回調(diào)機(jī)制的面向?qū)ο蟀姹?。Command模式用來(lái)封裝一個(gè)命令/請(qǐng)求,簡(jiǎn)單的說(shuō),一個(gè)Command對(duì)象中包含了待執(zhí)行的一個(gè)動(dòng)作(語(yǔ)句)序列,以執(zhí)行特定的任務(wù)。當(dāng)然,并不是隨便怎么樣的語(yǔ)句序列都可以構(gòu)成一個(gè)Command對(duì)象的,按照Command模式的設(shè)計(jì),Command對(duì)象和它的調(diào)用者

12、Incvoker之間應(yīng)該 具有接口約定的。也就是說(shuō),Invoker得到Command對(duì)象的引用,并調(diào)用其中定義好的方法,而當(dāng)Command對(duì)象改變(或者是對(duì)象本身代碼改 變,或者干脆完全另外的一個(gè)Command對(duì)象)之后,Invoker中的代碼可以不用更改。這樣,通過(guò)封裝請(qǐng)求,可以把任務(wù)和任務(wù)的實(shí)現(xiàn)加以分離。圖二:Command模式的類(lèi)圖而對(duì)于請(qǐng)求的處理又有兩種不同的方法,一種是Command只充當(dāng)代理,將請(qǐng)求轉(zhuǎn)發(fā)給某個(gè)接受者對(duì)象,還有一種是Command對(duì)象自己處理完所有的請(qǐng)求操作。當(dāng)然,這只是兩個(gè)極端,更多的情況是Command完成一部分的工作,而另外的一部分這則交給接受者對(duì)象來(lái)處理。在新

13、的JDK的代理事件模型中,就可以看作是這樣的一個(gè)Command模式。在那個(gè)模型中,一個(gè)事件監(jiān)聽(tīng)者類(lèi)EventListener監(jiān)聽(tīng)某個(gè)事 件,并根據(jù)接口定義,實(shí)現(xiàn)特定的操作。比如,當(dāng)用Document對(duì)象的addDocumentListener(DocumentListener listener) 方法注冊(cè)了一個(gè)DocumentListener后,以后如果在Document對(duì)象中發(fā)生文本插入的事件,DocumentListener中實(shí)現(xiàn)的 insertUpdate(DocumentEvent e)方法就會(huì)被調(diào)用,如果發(fā)生文本刪除事件,removeUpdate(DocumentEvent e)方法

14、就會(huì)被調(diào)用。怎么樣,想想看,這是不是一個(gè)Command模式的應(yīng)用呢?然而,最經(jīng)典的Command模式的應(yīng)用,莫過(guò)于Swing中的Action接口。Action實(shí)際上繼承的是ActionListener,也就 是說(shuō),它也是一個(gè)事件監(jiān)聽(tīng)者(EventListener)。但是Action作為一種ActionListener的擴(kuò)展機(jī)制,提供了更多的功能。它可 以在其中包含對(duì)這個(gè)Action動(dòng)作的一個(gè)或者多個(gè)文字的或圖標(biāo)的描敘,它提供了Enable/Disable的功能許可性標(biāo)志。并且,一個(gè)Action 對(duì)象可以被多個(gè)Invoker,比如實(shí)現(xiàn)相同功能的按鈕,菜單,快捷方式所共享。而這些Invoker都知

15、道如何加入一個(gè)Action,并充分利用它所提 供的擴(kuò)展機(jī)制。可以說(shuō),在這兒Action更像一個(gè)對(duì)象了,因?yàn)樗粌H僅提供了對(duì)方法的實(shí)現(xiàn),更提供了對(duì)方法的描敘和控制??梢苑奖愕拿钄⑷魏蔚氖聞?wù),這 更是面向?qū)ο蠓椒ǖ耐λ?。下面我們看一個(gè)Command模式的應(yīng)用的例子。假設(shè)要實(shí)現(xiàn)這樣的一個(gè)任務(wù):Task Schedule。也就是說(shuō),我想對(duì)多個(gè)任務(wù)進(jìn)行安排,比如掃描磁盤(pán),我希望它每1個(gè)小時(shí)進(jìn)行一次,而備份數(shù)據(jù),我希望它半個(gè)小時(shí)進(jìn)行一次,等等等等。但 是,我并不希望作為T(mén)askSchedule的類(lèi)知道各個(gè)任務(wù)的細(xì)節(jié)內(nèi)容,TaskSchedule應(yīng)該只是知道Task本身,而對(duì)具體的實(shí)現(xiàn)任務(wù)的細(xì)節(jié) 并不理

16、會(huì)。因而在這兒,我們就需要對(duì)TaskSchedule和Task進(jìn)行解耦,將任務(wù)和具體的實(shí)現(xiàn)分離出來(lái),這不正是Command模式的用武之地 嗎?圖三:Command模式的應(yīng)用例子程序清單:/抽象的Task接口,作為回調(diào)的Command模式的主體publicinterfaceTaskpublicvoidtaskPerform();/具體的實(shí)現(xiàn)了Task接口的子類(lèi),實(shí)現(xiàn)特定的操作。publicclassBackupTaskimplementsTaskpublicvoidtaskPerform()System.out.println(BackupTaskhasbeenperformed);/具體的實(shí)現(xiàn)

17、了Task接口的子類(lèi),實(shí)現(xiàn)特定的操作。publicclassScanDiskTaskimplementsTaskpublicvoidtaskPerform()System.out.println(ScanDiskTaskhasbeenperformed);/一個(gè)封裝了Task的一個(gè)封裝類(lèi),提供了一些與Task相關(guān)的內(nèi)容,也可以把這些內(nèi)容/這兒不過(guò)為了突出Command模式而把它單另出來(lái),實(shí)際上可以和Task合并。publicclassTaskEntryprivateTasktask;privatelongtimeInterval;privatelongtimeLastDone;publicTa

18、skgetTask()returntask;publicvoidsetTask(Tasktask)this.task=task;publicvoidsetTimeInterval(longtimeInterval)this.timeInterval=timeInterval;publiclonggetTimeInterval()returntimeInterval;publiclonggetTimeLastDone()returntimeLastDone;publicvoidsetTimeLastDone(longtimeLastDone)this.timeLastDone=timeLastD

19、one;publicTaskEntry(Tasktask,longtimeInteral)this.task=task;this.timeInterval=timeInteral;/調(diào)度管理Task的類(lèi),繼承Thread只是為了調(diào)用其sleep()方法,/實(shí)際上,如果真的作Task調(diào)度的話,每個(gè)Task顯然應(yīng)該用單獨(dú)的Thread來(lái)實(shí)現(xiàn)。publicclassTaskScheduleextendsjava.lang.Threadprivatejava.util.VectortaskList=newjava.util.Vector();privatelongsleeptime=100000000

20、00l;/最短睡眠時(shí)間publicvoidaddTask(TaskEntrytaskEntry)taskList.add(taskEntry);taskEntry.setTimeLastDone(System.currentTimeMillis();if(sleeptimetaskEntry.getTimeInterval()sleeptime=taskEntry.getTimeInterval();/執(zhí)行任務(wù)調(diào)度publicvoidschedulePermorm()trysleep(sleeptime);Enumeratione=taskList.elements();while(e.has

21、MoreElements()TaskEntryte=(TaskEntry)e.nextElement();if(te.getTimeInterval()+te.getTimeLastDone()System.currentTimeMillis()te.getTask().taskPerform();te.setTimeLastDone(System.currentTimeMillis();catch(Exceptione1)e1.printStackTrace();publicstaticvoidmain(Stringargs)TaskScheduleschedule=newTaskSched

22、ule();TaskEntrytaks1=newTaskEntry(newScanDiskTask(),10000);TaskEntrytaks2=newTaskEntry(newBackupTask(),3000);schedule.addTask(taks1);schedule.addTask(taks2);while(true)schedule.schedulePermorm();程序本身其實(shí)沒(méi)有多大的意義,因而,程序在編碼的時(shí)候也只是用的最簡(jiǎn)單的方法來(lái)實(shí)現(xiàn)的,如果要做一個(gè)真正的TaskSchedule的話,這個(gè)程序除了結(jié)構(gòu)上的,其它沒(méi)有什么好值得參考的了。AbstractFactory

23、 和 FactoryMethod基本上來(lái)說(shuō),AbstractFacotry模式和FactoryMethod模式所作的事情是一樣的,都是用來(lái)創(chuàng)建與具體程序代碼無(wú)關(guān)的對(duì)象,只 是面對(duì)的對(duì)象層次不一樣,AbstractFactory創(chuàng)建一系列的對(duì)象組,這些對(duì)象彼此相關(guān)。而FactoryMethod往往只是創(chuàng)建單個(gè)的對(duì)象。再開(kāi)始這兩個(gè)模式之前,有必要先陳敘一個(gè)在設(shè)計(jì)模式,或者說(shuō)在整個(gè)面向?qū)ο笤O(shè)計(jì)領(lǐng)域所遵循的一個(gè)設(shè)計(jì)原則:針對(duì)接口編程,而不是針對(duì)具體的實(shí)現(xiàn)。這 個(gè)思想可以說(shuō)是設(shè)計(jì)模式的基石之一?,F(xiàn)在的很多對(duì)象模型,比如EJB,COM+等等,無(wú)不是遵照這個(gè)基本原則來(lái)設(shè)計(jì)的。針對(duì)接口編程的好處有很多,通過(guò)接

24、 口來(lái)定義對(duì)象的抽象功能,方便實(shí)現(xiàn)多態(tài)和繼承;通過(guò)接口來(lái)指定對(duì)象調(diào)用之間的契約,有助于協(xié)調(diào)對(duì)象之間的關(guān)系;通過(guò)接口來(lái)劃分對(duì)象的職責(zé),有助于尋找對(duì) 象,等等。AbstractFactory和FactoryMethod,還有其他的一些創(chuàng)建型的設(shè)計(jì)模式,都是為了實(shí)現(xiàn)這個(gè)目的而設(shè)計(jì)出來(lái)的。它們創(chuàng)建一個(gè) 個(gè)符合接口規(guī)范的對(duì)象/對(duì)象組,使得用同一個(gè)Factory創(chuàng)建出來(lái)的對(duì)象/對(duì)象組可以相互替換。這種可替換性就稱(chēng)為多態(tài),是面向?qū)ο蟮暮诵乃枷胫?。?多態(tài),是通過(guò)動(dòng)態(tài)綁定來(lái)實(shí)現(xiàn)的。圖四:AbstractFactory模式的類(lèi)圖 客戶(hù)程序使用具體的AbstractFacotry對(duì)象(ConcreteFacto

25、ryX)調(diào)用CreateProductX()方法,生成具體 的ConcreteProductX。每個(gè)AbstractFactory所能生成的對(duì)象,組成一個(gè)系列的對(duì)象組,他們可能是相互相關(guān)的,緊耦合的。應(yīng)為 各個(gè)AbstractFactory對(duì)象所能夠生成的對(duì)象組都遵循一組相同的接口(AbstractProductX),因而當(dāng)程序是針對(duì)接口進(jìn)行編程的 時(shí)候,這些實(shí)現(xiàn)方法各不相同的對(duì)象組卻可以相互的替換。實(shí)際上,客戶(hù)程序本身并不關(guān)心,也不知道具體使用的是那些產(chǎn)品對(duì)象。它甚至能夠不理會(huì)到底是哪個(gè)AbstractFactory對(duì)象被創(chuàng)建。在這種 情況下,你可能會(huì)問(wèn),那么一個(gè)AbstractFactor

26、y又該如何生成呢?這時(shí)候,就該用該FactoryMethod模式了。前面有說(shuō)過(guò),AbstractFactory著重于創(chuàng)建一系列相關(guān)的對(duì)象,而這些對(duì)象與具體的AbstractFactory相關(guān)。而 FactoryMethod則著重于創(chuàng)建單個(gè)的對(duì)象,這個(gè)對(duì)象決定于一個(gè)參數(shù)或者一個(gè)外部的環(huán)境變量的值;或者,在一個(gè)抽象類(lèi)中定義一個(gè)抽象的工廠方法 (也成為虛擬構(gòu)造器),然后再實(shí)現(xiàn)的子類(lèi)中返回具體的產(chǎn)品對(duì)象。FactoryMethod可以借助一個(gè)參數(shù)或者一個(gè)外部的標(biāo)志來(lái)判斷該具體生成的哪一個(gè)子類(lèi)的實(shí)例。比如對(duì)于不同的具體情況,需要有不同的 AbstractFactory來(lái)生成相應(yīng)的對(duì)象組。這時(shí)候,F(xiàn)act

27、oryMethod通常作為一個(gè)AbstractFactory對(duì)象的靜態(tài)方法出 現(xiàn),使得其能夠在具體的對(duì)象被創(chuàng)建之前就能夠被調(diào)用。在JAVA中,應(yīng)用這兩個(gè)模式的地方實(shí)在太多,下面我們來(lái)看一個(gè)在JAXP中這兩個(gè)模式的應(yīng)用。JAXP是用來(lái)處理XML文檔的一個(gè)API。我們都 知道XML文件的一個(gè)特點(diǎn)就是其平臺(tái)無(wú)關(guān),流通性能好。因而往往也需要處理他們的程序具有更好的平臺(tái)無(wú)關(guān)性。Java語(yǔ)言是一個(gè)比較好的平臺(tái)無(wú)關(guān)語(yǔ)言,可 以作為一個(gè)選擇,但是對(duì)XML進(jìn)行解析的解析器確有很多。有時(shí)候需要在不同的解析器之間進(jìn)行切換,這時(shí)候,JAXP的良好設(shè)計(jì)就能夠體現(xiàn)出來(lái)了。它能夠允 許在不同解析器之間竟進(jìn)行切換的時(shí)候,不用

28、更改程序的代碼。我們就拿JAXP中的DOM解析器來(lái)作為例子,來(lái)例示AbstractFactory和FactoryMethod的用法。圖五:DOM中工廠模式的應(yīng)用 上圖中為了方便起見(jiàn),只畫(huà)出了抽象類(lèi)和接口,DocumentBuilderFactory和DocumentBuilder都是抽象類(lèi)。DocumentBuilderFactory的靜態(tài)方法newInstance()方法根據(jù)一個(gè)外部的環(huán)境變量 javax.xml.parsers.DocumentBuilderFactory的值來(lái)確定具體生成DocumentBuilderFactory的 哪一個(gè)子類(lèi)。這兒的newInstance()是一個(gè)工廠

29、方法。當(dāng)DocumentBuilderFactory被創(chuàng)建后,可以調(diào)用其 newDocumentBuilder()來(lái)創(chuàng)建具體一個(gè)DocumentBuilder的子類(lèi)。然后再由DocumentBuilder來(lái)生成 Document等DOM對(duì)象。下面是創(chuàng)建一個(gè)DOM對(duì)象的代碼片段:/第一步:創(chuàng)建一個(gè)DocumentBuilderFactory。DocumentBuilderFactorydbf=DocumentBuilderFactory.newInstance();/第二步:創(chuàng)建一個(gè)DocumentBuilderDocumentBuilderdb=dbf.newDocumentBuilder()

30、;/第三步:解析XML文件得到一個(gè)Document對(duì)象Documentdoc=db.parse(newFile(filename);在這兒,DocumentBuilder,Document,Node等等對(duì)象所組成的一個(gè)產(chǎn)品組,是和具體的DocumentBuilderFactory相關(guān)的。這也就是AbstractFactory模式的含義所在。當(dāng)然,F(xiàn)actoryMethod模式應(yīng)用的很廣。這是一個(gè)具體的例子,但他不應(yīng)該限制我們的思路,F(xiàn)actoryMethod和AbstractFactory是解決面向?qū)ο笤O(shè)計(jì)中一個(gè)基本原則-面向接口編程的主要方法。Singleton模式Singleton模式要解

31、決的是對(duì)象的唯一性問(wèn)題。由Singleton模式創(chuàng)建的對(duì)象在整個(gè)的應(yīng)用程序的范圍內(nèi),只允許有一個(gè)對(duì)象的實(shí)例存在。 這樣的情況在Java程序設(shè)計(jì)的過(guò)程中其實(shí)并不少見(jiàn),比如處理JDBC請(qǐng)求的連接池(Connection Pool),再比如一個(gè)全局的注冊(cè)表(Register),等等,這都需要使用到Singleton,單件模式。在Java中,最簡(jiǎn)單的實(shí)現(xiàn)Singleton模式的方法是使用static修飾符,static可以用在內(nèi)部類(lèi)上,也可以用在方法和屬性上,當(dāng)一 個(gè)類(lèi)需要被創(chuàng)建成Singleton時(shí),可以把它所有的成員都定義成static,然后再用final和private來(lái)修飾其構(gòu)造函數(shù),使其不能

32、夠被創(chuàng) 建和重載。這在程序語(yǔ)法上保證了只會(huì)有一個(gè)對(duì)象實(shí)例被創(chuàng)建。比如java.util.Math就是這樣的一個(gè)類(lèi)。而Singleton模式所作的顯然要比上面介紹的解決方法要復(fù)雜一些,也更為安全一些。它基本的思路也還是使用static變量,但是它用一個(gè)類(lèi) 來(lái)封裝這個(gè)static變量,并攔截對(duì)象創(chuàng)建方法,保證只有一個(gè)對(duì)象實(shí)例被創(chuàng)建,這兒的關(guān)鍵在于使用一個(gè)private或者protected的構(gòu)造函 數(shù),而且你必須提供這樣的一個(gè)構(gòu)造函數(shù),否則編譯器會(huì)自動(dòng)的為你創(chuàng)建一個(gè)public的構(gòu)造函數(shù),這就達(dá)不到我們想要的目的了。publicclassSingleton/保存唯一實(shí)例的static變量stati

33、cprivateSingleton_instance=null;/*為了防止對(duì)象被創(chuàng)建,可以為構(gòu)造函數(shù)加上private修飾符,但是這同樣也防止了子類(lèi)的對(duì)象被創(chuàng)建,因而,可以選用protected修飾符來(lái)替代private。*/protectedSingleton()/./static方法用來(lái)創(chuàng)建/訪問(wèn)唯一的對(duì)象實(shí)例,這兒可以對(duì)對(duì)象的創(chuàng)建進(jìn)行控制,使得可/以很容易的實(shí)現(xiàn)只允許指定個(gè)數(shù)的對(duì)象存在的泛化的Singleton模式。staticpublicSingletoninstance()if(null=_instance)_instance=newSingleton();return_insta

34、nce;/.對(duì)象創(chuàng)建的方法,除了使用構(gòu)造函數(shù)之外,還可以使用Object對(duì)象的clone()方法,因而在Singleton中也要注意這一點(diǎn)。如果 Singleton類(lèi)直接繼承于Object,因?yàn)槔^承于Object的clone()方法仍保留有其protected修飾,因而不能夠被其他外部類(lèi)所 調(diào)用,所以可以不用管它,但是如果Singleton繼承于一個(gè)其他的類(lèi),而這個(gè)類(lèi)又有重載clone()方法,這時(shí)就需要在Singleton中再重載 clone()方法,并在其中拋出CloneNotSupportedException,這樣就可以避免多個(gè)Singleton的實(shí)例被創(chuàng)建了。在JDK1.2以前的版本

35、中使用Singleton模式的時(shí)候有一些需要額外注意的地方,因?yàn)镾ingleton類(lèi)并沒(méi)有被任何其他的對(duì)象所引用, 所以這個(gè)類(lèi)在創(chuàng)建后一段時(shí)間會(huì)被unload,Singleton類(lèi)的靜態(tài)方法就會(huì)出現(xiàn)問(wèn)題,這是由于Java中垃圾收集機(jī)制造成的。解決的方法也很容 易,只需要為其創(chuàng)建一個(gè)引用就行了。而在JDK1.2以后的版本中,Sun重新定義了Java規(guī)范,改正了其垃圾收集機(jī)制中的一些問(wèn)題,這個(gè)問(wèn)題也就不復(fù) 存在了,這兒指出只是為了提起大家的主意。小結(jié):Command模式用來(lái)封裝請(qǐng)求,也描敘了一致性的發(fā)送請(qǐng)求的接口,允許你配置客戶(hù)端以處理不同的請(qǐng)求,為程序增添了更大的靈活性。 Singleton模式

36、為提供對(duì)象的單一入口提供了幫助。AbstractFactory和FactoryMethod模式在功能上比較類(lèi)似,都是用來(lái)處 理對(duì)象的創(chuàng)建的,但應(yīng)用在不同的層面上。在創(chuàng)建型模式中,還有Builder模式和Prototype模式,這兒不打算詳細(xì)的討論了,簡(jiǎn)單的 說(shuō),Builder模式用來(lái)處理對(duì)象創(chuàng)建的細(xì)節(jié)。在兩個(gè)工廠模式中都沒(méi)有涉及到對(duì)象創(chuàng)建的具體細(xì)節(jié),都是通過(guò)接口來(lái)返回一個(gè)給定類(lèi)型的對(duì)象。而 Builder模式則需要對(duì)創(chuàng)建一個(gè)給定類(lèi)型對(duì)象的過(guò)程進(jìn)行建模。這對(duì)創(chuàng)建復(fù)雜對(duì)象時(shí)很有用,使得創(chuàng)建對(duì)象的算法獨(dú)立于對(duì)象各個(gè)組成部分的創(chuàng)建。而 Prototype模式使用原型機(jī)制,通過(guò)創(chuàng)建簡(jiǎn)單原型的拷貝來(lái)創(chuàng)建對(duì)

37、象。從Java類(lèi)庫(kù)看設(shè)計(jì)模式(3) Bridge模式當(dāng)初Java剛剛推出來(lái)的時(shí)候,AWT可是一個(gè)比較熱的話題,雖然現(xiàn)在有被Swing取代的趨勢(shì)。但是我一直都覺(jué)得AWT也有其優(yōu)勢(shì),至少它使用的 本地代碼就要比Swing快上許多,而且,可以為用戶(hù)提供熟悉的本地操作系統(tǒng)界面。如果在Windows XP中運(yùn)行基于AWT的程序的話,XP中絢爛多變的界面Theme可以輕易應(yīng)用到AWT程序中,而Swing就不行了,因?yàn)锳WT所調(diào)用的是本帶代碼,使 用的是本地的窗體控件。當(dāng)然,Swing也有其好處,不可一概而論。簡(jiǎn)單來(lái)講,AWT提供對(duì)程序員的是對(duì)窗體界面系統(tǒng)的抽象,而在內(nèi)部實(shí)現(xiàn)中,針對(duì)每一種操作系統(tǒng),分別有不

38、同實(shí)現(xiàn),這就是同位體(Peer)的概念。 當(dāng)程序員調(diào)用AWT對(duì)象時(shí),調(diào)用被轉(zhuǎn)發(fā)到對(duì)象所對(duì)應(yīng)的一個(gè)Peer上,在由Peer調(diào)用本地對(duì)象方法,完成對(duì)象的顯示。例如,如果你使用AWT創(chuàng)建了一個(gè) Menu類(lèi)的實(shí)例,那么在程序運(yùn)行時(shí)會(huì)創(chuàng)建一個(gè)菜單同位體的實(shí)例,而由創(chuàng)建的同位體的來(lái)實(shí)際執(zhí)行菜單的現(xiàn)實(shí)和管理。不同的系統(tǒng),有不同的同位體實(shí) 現(xiàn),Solaris JDK將產(chǎn)生一個(gè)Motif菜單的同位體,Windows下的JDK將產(chǎn)生一個(gè)Windows的菜單的同位體,等等。同位體的使用,使得交叉平臺(tái)窗口工具 的開(kāi)發(fā)變得極為迅速,因?yàn)橥惑w的使用可以避免重新實(shí)現(xiàn)本地窗口控件中已經(jīng)包含的方法。圖九:AWT中的組件和其對(duì)

39、等體 實(shí)際上,從設(shè)計(jì)的角度來(lái)看,這是一個(gè)抽象和實(shí)現(xiàn)分離的過(guò)程-AWT是抽象,同位體是實(shí)現(xiàn),抽象和實(shí)現(xiàn)各自成為一個(gè)對(duì)象體系,它們由一個(gè)橋連接起 來(lái),可以各自發(fā)展各自的對(duì)象層次,而不必顧慮另一方面。這就是Bridge模式所提供的思想。Bridge模式更可以提供在各個(gè)不同的實(shí)現(xiàn)中動(dòng)態(tài)的進(jìn)行切 換,而不必從新編譯程序。通常,Bridge模式和AbstractFactory模式一起工作,由AbstractFactory來(lái)創(chuàng)建一個(gè)具體實(shí)現(xiàn)的對(duì)象體系。特殊的, 當(dāng)只有一個(gè)實(shí)現(xiàn)的時(shí)候,可以將Implementor抽象類(lèi)去掉。這樣,在抽象和實(shí)現(xiàn)之間建立起了一一對(duì)應(yīng)的關(guān)系,但這并不損害Bridge模式的內(nèi)涵。 這

40、被稱(chēng)為退化了的Bridge模式。很多時(shí)候,Abstraction層次和Implementor層次之間的方法都不是一一對(duì)應(yīng)的,也就是說(shuō),在Abstraction和 Implementor之不是簡(jiǎn)單的的消息轉(zhuǎn)發(fā)。通常,我們會(huì)將Abstraction作為一個(gè)抽象類(lèi)(而不是接口)來(lái)實(shí)現(xiàn)。在Implementor層 次中定義底層的,或者稱(chēng)之為原子方法,而在Abstraction層次中定義一些中高層的基于原子方法的抽象方法。這樣,就能更為清晰的劃分 Abstraction和Implementor,類(lèi)的結(jié)構(gòu)也更為清晰。圖十:Bridge模式對(duì)系統(tǒng)的劃分 下面,我們來(lái)看一個(gè)Bridge模式的具體應(yīng)用??紤]這樣

41、的一個(gè)問(wèn)題,需要生成一份報(bào)告,但是報(bào)告的格式并沒(méi)有確定,可能是HTML文件,也可能是 純ASCII文本。報(bào)告本身也可能分為很多種,財(cái)務(wù)報(bào)表,貨物報(bào)表,等等問(wèn)題很簡(jiǎn)單,用繼承也較容易實(shí)現(xiàn),因?yàn)橄嗷ブg的組合關(guān)系并不是很多。但是,我們 現(xiàn)在需要用Bridge的觀點(diǎn)來(lái)看問(wèn)題。在Bridge模式中,使用一個(gè)Report類(lèi)來(lái)描敘一個(gè)報(bào)告的抽象,用一個(gè)Reporter類(lèi)來(lái)描敘Report的實(shí)現(xiàn),它的子類(lèi)有 HTMLReporter和ASCIIReporter,用來(lái)分別實(shí)現(xiàn)HTML格式和ASCII格式的報(bào)告。在Report層次下面,有具體的一個(gè) StockListReport子類(lèi),用來(lái)表示貨物清單報(bào)告。pu

42、blicabstractclassReportReporterreporter;publicReport(Reporterreporter)this.reporter=reporter;/抽象類(lèi)使用橋接對(duì)象的方法來(lái)實(shí)現(xiàn)一個(gè)任務(wù)publicvoidaddReportItem(Objectitem)reporter.addLine(item.toString();publicvoidaddReportItems(Listitems)Iteratoriterator=items.iterator();while(iterator.hasNext()reporter.addLine(iterator.

43、next().toString();publicStringreport()returnreporter.getReport();publicclassStockListReportextendsReportArrayListstock=newArrayList();publicStockListReport(Reporterreporter)super(reporter);publicvoidaddStockItem(StockItemstockItem)stock.add(stockItem);addReportItem(stockItem);/實(shí)現(xiàn)層次的抽象父類(lèi)定義原子方法,供抽象層次的

44、類(lèi)調(diào)用publicabstractclassReporterStringheader=;Stringtrailer=;Stringreport=;publicabstractvoidaddLine(Stringline);publicvoidsetHeader(Stringheader)this.header=header;publicvoidsetTrailer(Stringtrailer)this.trailer=trailer;publicStringgetReport()returnheader+report+trailer;publicclassHTMLReporterextends

45、ReporterpublicHTMLReporter()setHeader( );setTrailer( );publicvoidaddLine(Stringline)report+=line+ ;publicclassASCIIReporterextendsReporterpublicvoidaddLine(Stringline)report+=line+ ;實(shí)際上,Bridge模式是一個(gè)很強(qiáng)大的模式,可以應(yīng)用在很多方面。其基本思想:分離抽象和實(shí)現(xiàn),是設(shè)計(jì)模式的基礎(chǔ)之一。正如GOF所提到的:找 到變化的部分,并將其封裝起來(lái);更多的考慮用對(duì)象組合機(jī)制,而不是用對(duì)象繼承機(jī)制。Bridge模式很好

46、的體現(xiàn)了這幾點(diǎn)。Decorator模式在使用Java中的IO類(lèi)庫(kù)的時(shí)候,是不是快要被它那些功能相似,卻又絕對(duì)可稱(chēng)得上龐雜的類(lèi)搞得要發(fā)瘋了?或許你很不明白為什么要做這么多功能相似的幾十個(gè)類(lèi)出來(lái),這就是Decorator模式將要告訴你的了。在IO處理中,Java將數(shù)據(jù)抽象為流(Stream)。在IO庫(kù)中,最基本的是InputStream和OutputStream兩個(gè)分別處理輸 出和輸入的對(duì)象(為了敘述簡(jiǎn)便起見(jiàn),這兒只涉及字節(jié)流,字符流和其完全相似),但是在InputStream和OutputStream中之提供了最簡(jiǎn)單 的流處理方法,只能讀入/寫(xiě)出字符,沒(méi)有緩沖處理,無(wú)法處理文件,等等。它們只是提

47、供了最純粹的抽象,最簡(jiǎn)單的功能。如何來(lái)添加功能,以處理更為復(fù)雜的事情呢?你可能會(huì)想到用繼承。不錯(cuò),繼承確實(shí)可以解決問(wèn)題,但是繼承也帶來(lái)更大的問(wèn)題,它對(duì)每一個(gè)功能,都需要一 個(gè)子類(lèi)來(lái)實(shí)現(xiàn)。比如,我先實(shí)現(xiàn)了三個(gè)子類(lèi),分別用來(lái)處理文件,緩沖,和讀入/寫(xiě)出數(shù)據(jù),但是,如果我需要一個(gè)既能處理文件,又具有緩沖功能的類(lèi)呢?這時(shí)候 又必須在進(jìn)行一次繼承,重寫(xiě)代碼。實(shí)際上,僅僅這三種功能的組合,就已經(jīng)是一個(gè)很大的數(shù)字,如果再加上其它的功能,組合起來(lái)的IO類(lèi)庫(kù),如果只用繼承來(lái)實(shí) 現(xiàn)的話,恐怕你真的是要被它折磨瘋了。圖六:JDK中IO流的類(lèi)層次 Decorator模式可以解決這個(gè)問(wèn)題。Decorator字面的意思是

48、裝飾的意思,在原有的基礎(chǔ)上,每添加一個(gè)裝飾,就可以增加一種功能。這就 是Decorator的本意。比如,對(duì)于上面的那個(gè)問(wèn)題,只需要三個(gè)Decorator類(lèi),分別代表文件處理,緩沖和數(shù)據(jù)讀寫(xiě)三個(gè)功能,在此基礎(chǔ)上所衍生 的功能,都可以通過(guò)添加裝飾來(lái)完成,而不必需要繁雜的子類(lèi)繼承了。更為重要的是,比較繼機(jī)制承而言,Decorator是動(dòng)態(tài)的,可以在運(yùn)行時(shí)添加或者去 除附加的功能,因而也就具有比繼承機(jī)制更大的靈活性。上面就是Decorator的基本思想,下面的是Decorator模式的靜態(tài)結(jié)構(gòu)圖:圖七:Decorator模式的類(lèi)圖 可以看到,一個(gè)Decorator與裝飾的Subject對(duì)象有相同的接口

49、,并且除了接口中給出的方法外,每個(gè)Decorator均有自己添加的方 法,來(lái)添加對(duì)象功能。每個(gè)Decorator均有一個(gè)指向Subject對(duì)象的引用,附加的功能被添加在這個(gè)Subject對(duì)象上。而Decorator 對(duì)象本身也是一個(gè)Subject對(duì)象,因而它也能夠被其他的Decorator所修飾,提供組合的功能。在Java IO操作中,經(jīng)??梢钥吹街T如如下的語(yǔ)句:myStringBuffer=newStringBuffer(Thisisasamplestringtoberead);FilterInputStreammyStream=newLineNumberInputStream(newBuf

50、ferInputStream(newStringBufferInputStream(myStringBuffer);myStream.read();myStream.line();多個(gè)的Decorator被層疊在一起,最后得到一個(gè)功能強(qiáng)大的流。既能夠被緩沖,又能夠得到行數(shù),這就是Decorator的威力!不僅僅如此,Java中的IO還允許你引入自定義的Decorator,來(lái)實(shí)現(xiàn)自己想要的功能。在良好的設(shè)計(jì)背景下,這做起并不復(fù)雜,只需要4步:1. 創(chuàng)建兩個(gè)分別繼承了FilterInputStream和 FilterOutputStream的子類(lèi) 2. 重載read()和write()方法來(lái)實(shí)現(xiàn)自

51、己想要的功能。 3. 可以定義或者重載其它方法來(lái)提供附加功能。 4. 確定這兩個(gè)類(lèi)會(huì)被一起使用,因?yàn)樗鼈冊(cè)诠δ苌鲜菍?duì)稱(chēng)的。 就這樣,你就可以無(wú)限的擴(kuò)展IO的功能了。在了解了IO中的Decorator后,我們?cè)賮?lái)看一個(gè)Decorator模式應(yīng)用的具體的例子。這個(gè)例子原本是出現(xiàn)在GOF書(shū)中的,這兒稍作改動(dòng),引來(lái)示例。在一個(gè)圖形用戶(hù)界面(GUI)中,一個(gè)組件有時(shí)候需要用到邊框或者滾動(dòng)條,而有時(shí)候又不需要,有時(shí)候可能兩者都要用到。當(dāng)需要?jiǎng)討B(tài)的去處或者添加職 能的時(shí)候,就可以考慮使用Decorator模式了。這兒對(duì)于一個(gè)VisualComponent組件對(duì)象,我們引入了兩個(gè)Decorator 類(lèi):Bod

52、erDecorator和ScrollDecorator,分別用來(lái)為組件添加邊框和處理滾動(dòng)。程序類(lèi)圖如下:圖八:Decorator模式的應(yīng)用例子 程序?qū)懙煤芎?jiǎn)單,沒(méi)有包括具體的代碼,只是有一個(gè)可以運(yùn)行的框架以供參考。代碼如下:/Client類(lèi)用來(lái)創(chuàng)建窗體和組件對(duì)象,這兒可以看到Decorator是如何組合和應(yīng)用的classClientpublicstaticvoidmain(Stringargs)Windowwindow=newWindow();TextViewtextView=newTextView();window.setContents(newBorderDecorator(newScrollDecorator(textView,500),1);/Windows類(lèi)用來(lái)容納組件對(duì)象classWindowVisualComponentcontents;publicWind

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論