【摘要】設(shè)計(jì)模式是面向?qū)ο蟮能浖O(shè)計(jì)和實(shí)現(xiàn)的關(guān)鍵技術(shù),正確的理解設(shè)計(jì)模式是應(yīng)用設(shè)計(jì)模式的前提。本文介紹了組合、觀察者兩種設(shè)計(jì)模式的組成、特點(diǎn)和使用條件,分析了其各自在面向?qū)ο笳Z(yǔ)言Java類庫(kù)中的應(yīng)用。
【關(guān)鍵詞】設(shè)計(jì)模式;面向?qū)ο?;耦合度;擴(kuò)展性
1.引言
從上世紀(jì)六七時(shí)年代起,由于軟件對(duì)生產(chǎn)的巨大推動(dòng)作用,各種大型的、復(fù)雜的軟件系統(tǒng)相繼問(wèn)世。但與此同時(shí),隨著軟件規(guī)模和復(fù)雜性的激增,軟件開發(fā)手段缺乏善可陳,因此造成所需投入的人力、物力和時(shí)間也越來(lái)越龐大,而軟件系統(tǒng)的質(zhì)量和可靠性卻得不到保證,軟件危機(jī)出現(xiàn)了。這一情況持續(xù)到上世紀(jì)80年代,軟件開發(fā)采用面向?qū)ο箝_發(fā)語(yǔ)言和思想方法,才得以緩解。可是采用面向?qū)ο蟮姆椒▉?lái)開發(fā)軟件也需要正確成熟的經(jīng)驗(yàn)、原則來(lái)指導(dǎo)開發(fā)工作,否則,開發(fā)的軟件將不可避免帶有各種各樣的缺陷,諸如:系統(tǒng)僵硬,不能適應(yīng)新的需求;系統(tǒng)復(fù)用率低,代碼粘黏度過(guò)高等。軟件設(shè)計(jì)模式的提出,為有效避免上述問(wèn)題,設(shè)計(jì)出具備良好可擴(kuò)展性、可復(fù)用性、易維護(hù)的軟件系統(tǒng)提供了良好的解決方案。
下面通過(guò)分析組合和觀察者兩種設(shè)計(jì)模式的形式、特點(diǎn),各自在Java類庫(kù)中的一些應(yīng)用,和一些使用的想法,加深對(duì)設(shè)計(jì)模式結(jié)構(gòu)、方法和作用的認(rèn)識(shí)。
2.設(shè)計(jì)模式簡(jiǎn)介
設(shè)計(jì)模式是一套經(jīng)過(guò)分類編目的、反復(fù)使用驗(yàn)證的、過(guò)往軟件成熟設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。通過(guò)使用設(shè)計(jì)模式可以簡(jiǎn)單復(fù)用成功的設(shè)計(jì)和體系結(jié)構(gòu),而將已驗(yàn)證的技術(shù)表述為設(shè)計(jì)模式也使得設(shè)計(jì)者的思路更加清晰,代碼更容易理解,幫助開發(fā)者們做出有利的復(fù)用選擇。達(dá)到既提高開發(fā)效率,又保證交付軟件質(zhì)量的目的。
2.1 觀察者設(shè)計(jì)模式(Observer)
觀察者(Observer)模式又名發(fā)布-訂閱(Publish/Subscribe)模式。它的解釋是:定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。觀察者模式由以下四個(gè)部分組成:
①抽象目標(biāo)角色(Subject):目標(biāo)角色知道它的觀察者,可以有任意多個(gè)觀察者觀察同一個(gè)目標(biāo),并且提供注冊(cè)和刪除觀察者對(duì)象的接口,目標(biāo)角色往往由抽象類或者接口來(lái)實(shí)現(xiàn)。
②抽象觀察者角色(Observer):為那些在目標(biāo)發(fā)生改變時(shí)需要獲得通知的對(duì)象定義一個(gè)更新接口,抽象觀察者角色主要由抽象類或者接口來(lái)實(shí)現(xiàn)。
③具體目標(biāo)角色(Concrete Subject):將有關(guān)狀態(tài)存入各個(gè)具體的對(duì)象,當(dāng)它的狀態(tài)發(fā)生改變時(shí),向它的各個(gè)觀察者發(fā)出通知;
④具體觀察者角色(Concrete Observer):存儲(chǔ)有關(guān)狀態(tài),這些狀態(tài)應(yīng)與目標(biāo)的狀態(tài)保持一致,實(shí)現(xiàn)觀察者的更新接口以使自身狀態(tài)與目標(biāo)的狀態(tài)保持一致。
用類圖表示觀察模式如圖1所示。
在Subject這個(gè)抽象類中存在一個(gè)通知方法:notify(通知),而具體目標(biāo)角色ConcreteSubject注冊(cè)和維護(hù)與其相關(guān)的觀察者隊(duì)列。當(dāng)ConcreteSubject的狀態(tài)發(fā)生改變,按照約定會(huì)去調(diào)用通知方法notify,在這個(gè)方法中根據(jù)目標(biāo)角色中注冊(cè)的具體Observer觀察者名單來(lái)逐個(gè)調(diào)用統(tǒng)一的update接口方法,這樣就完成了向所有注冊(cè)的ConcreteObserver觀察者的消息廣播。
通過(guò)上面的說(shuō)明,可以看到觀察者模式在觀察者和被觀察者之間建立了一個(gè)抽象的耦合。被觀察者所知道的只是一個(gè)具體觀察者的聚集,每一個(gè)具體觀察者都符合一個(gè)抽象觀察者的接口,被觀察者并不知道任意一個(gè)具體的觀察者,它只使用所有觀察者共同的接口。這樣被觀察者和觀察者就沒有緊密的耦合在一起,它們屬于不同的抽象層次;其次觀察者模式支持廣播通信,被觀察者會(huì)向所有登記過(guò)的觀察者發(fā)出通知。可以想象一個(gè)軟件系統(tǒng)當(dāng)其狀態(tài)發(fā)生變化時(shí),某些其他對(duì)象會(huì)發(fā)生相應(yīng)的改變,為了減少對(duì)象之間的耦合以利于軟件復(fù)用,同時(shí)需要這些低耦合的對(duì)象協(xié)調(diào)一致,觀察者模式正好能滿足這一類要求。
2.2 組合設(shè)計(jì)模式
組合模式又稱為合成模式或者樹模式。它的定義是:將對(duì)象以樹形結(jié)構(gòu)組合起來(lái),以達(dá)到“部分——整體”的層次結(jié)構(gòu),使得客戶端對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。
組合模式有如下幾部分構(gòu)成:
①抽象構(gòu)件角色(Component):它為組合中的對(duì)象聲明接口,也可以為共有接口實(shí)現(xiàn)缺省行為;
②樹葉構(gòu)件角色(Leaf):在組合中表示葉節(jié)點(diǎn)對(duì)象——沒有子節(jié)點(diǎn),實(shí)現(xiàn)抽象構(gòu)件角色聲明的接口;
③樹枝構(gòu)件角色(Composite):在組合中表示分支節(jié)點(diǎn)對(duì)象——有子節(jié)點(diǎn),實(shí)現(xiàn)抽象構(gòu)件角色聲明的接口;存儲(chǔ)子部件。
從圖2中可以得到,組合模式的使用場(chǎng)景是:設(shè)計(jì)中想表示對(duì)象的“部分-整體”層次結(jié)構(gòu);用戶能忽略組合對(duì)象與單個(gè)對(duì)象的不同,統(tǒng)一地使用組合結(jié)構(gòu)中的所有對(duì)象。
3.Java簡(jiǎn)介
Java語(yǔ)言是Sun公司推出的一款簡(jiǎn)單、面向?qū)ο?、分布式、解釋性、健壯、安全與系統(tǒng)無(wú)關(guān)的、可移植性的高性能、多線程動(dòng)態(tài)語(yǔ)言。由于其突出的各項(xiàng)優(yōu)勢(shì),從1995年首版推出就迅速成為全球的主流開發(fā)語(yǔ)言,至今已廣泛應(yīng)用于商業(yè)、搜索、游戲、移動(dòng)等幾乎所有的軟件開發(fā)和應(yīng)用領(lǐng)域。作為其諸多突出特色中最重要一環(huán)的面向?qū)ο蠹夹g(shù),對(duì)其取得的軟件業(yè)地位則完全可以用不可或缺來(lái)形容。Java是一款真正意義的面向?qū)ο缶幊陶Z(yǔ)言,其面向?qū)ο蟮姆庋b性、繼承性、多態(tài)性概念與應(yīng)用隨處可見。當(dāng)然,作為面向?qū)ο蠹夹g(shù)應(yīng)用成熟經(jīng)驗(yàn)總結(jié)精華的設(shè)計(jì)模式自然也就不可或缺了。
3.1 Java中的觀察者模式
在Java的類庫(kù)(JDK)中實(shí)際上有一個(gè)對(duì)Observer模式的簡(jiǎn)單實(shí)現(xiàn):就是類java.util.Observerable和接口java.util.Observer。java.util.Observerable類對(duì)應(yīng)于Subject,而java.util.Observer就是觀察者了。一個(gè)被觀察者Observerable對(duì)應(yīng)至少一個(gè)觀察者Observer,Observerable通過(guò)方法addObserver將它的所有觀察者Observer注冊(cè),當(dāng)Observerable狀態(tài)變化時(shí),自動(dòng)調(diào)用注冊(cè)觀察者隊(duì)列中對(duì)象的notifyObservers方法,將變化后的狀態(tài)向所有注冊(cè)的觀察者進(jìn)行廣播。
接下來(lái)面對(duì)假定場(chǎng)景:貓叫了一聲;老鼠發(fā)現(xiàn)貓后跑了;接著人驚醒了。應(yīng)用Observer和Observerable類構(gòu)造一個(gè)觀察者模式來(lái)進(jìn)行表述。
首先人作為老鼠的觀察者:
class Man implements Observer
{public void update(Observable arg0,Object arg1){
System.out.println(“老鼠跑了,人驚醒了”);
}
其次,老鼠作為貓的觀察者:
class Mouse extends Observable implements Observer
{
public void update(Observable arg0,Object arg1){
System.out.println(“貓叫了,老鼠跑了”);
this.setChanged();
this.notifyObservers();
}
}
然后,貓作為老鼠的被觀察者:
class Cat extends Observable{
public void CatSay(){
System.out.println(\"貓叫了\");
this.setChanged();
this.notifyObservers();
}
}
最后是事件的描述:
Cat cat=new Cat();
Observer mouse=new Mouse();
Observer man=new Man();
cat.addObserver(mouse);
mouse.addObserver(man);
cat.CatSay();
首先Cat和mouse通過(guò)使用addObserver方法,對(duì)各自的觀察者進(jìn)行注冊(cè),接著,隨著CatSay的調(diào)用整個(gè)事件被觸發(fā),消息通過(guò)被觀察者的notifyObservers方法沿著被觀察者——>觀察者的路徑,從貓到老鼠,再?gòu)睦鲜笞詈髠鬟_(dá)到人。
從中可以看出,每個(gè)被觀察者會(huì)維護(hù)一個(gè)自身的觀察者隊(duì)列,這個(gè)隊(duì)列中的觀察者對(duì)象都具有一個(gè)統(tǒng)一的觀察者接口,而被觀察者本身并不知道這個(gè)隊(duì)列中有那些具體的對(duì)象,它的責(zé)任只是在自身狀態(tài)變化時(shí),調(diào)用統(tǒng)一接口向隊(duì)列中所有對(duì)象發(fā)送廣播消息。觀察者模式的好處有兩個(gè):首先被觀察者不知道觀察者是誰(shuí)以及有多少,降低代碼的耦合度;其次是靈活,可以按照實(shí)現(xiàn)目的的需要,向觀察者隊(duì)列中隨時(shí)添加或者刪除注冊(cè),使得程序的數(shù)據(jù)管道發(fā)生變化,而觀察者和被觀察者類對(duì)象代碼卻能保持不變。
3.2 Java中的組合設(shè)計(jì)模式
Java類庫(kù)的抽象窗口工具包——AWT提供的Component-Container體系就是一個(gè)很好的Composite模式的例子。Container繼承于Component,而Container中有可以包含有多個(gè)Component,因?yàn)镃ontainer實(shí)際上也是Component,因而Container也可以包含Container。這樣通過(guò)Component-Container結(jié)構(gòu)的對(duì)象組合,形成一個(gè)樹狀的層次結(jié)構(gòu)。這正是Composite模式所要做的。
Composite模式是為了簡(jiǎn)化編程而提出的,它最大的好處就是透明。比如在一個(gè)Container中放置一個(gè)Component,不需要知道這個(gè)Component到底是一個(gè)Container,或者就是一個(gè)一般的Component,在父級(jí)容器中所要做的,只是記錄一個(gè)Component的引用,在需要的時(shí)候調(diào)用Component的繪制方法來(lái)顯示這個(gè)Component。當(dāng)這個(gè)Component確實(shí)是一個(gè)Container的時(shí)候,它可以通過(guò)Container重載后的繪制方法,完成對(duì)這個(gè)容器的顯示,并把繪制消息傳遞給到它的子對(duì)象去。也就是說(shuō),對(duì)一個(gè)父級(jí)容器而言,它并不關(guān)心其子對(duì)象到底是一個(gè)Component,還是Container。它們都具有相同的調(diào)用接口。
4.兩種設(shè)計(jì)模式的應(yīng)用
4.1 觀察者設(shè)計(jì)模式應(yīng)用
結(jié)合設(shè)備軟件開發(fā)中往往面臨多個(gè)外部接口的情況,當(dāng)設(shè)備的狀態(tài)改變時(shí),常常需要向多個(gè)外部接口進(jìn)行同時(shí)匯報(bào)。那么將設(shè)備軟件當(dāng)作被觀察者,把外部接口作為觀察者,設(shè)備軟件對(duì)外部接口對(duì)象執(zhí)行注冊(cè)。當(dāng)設(shè)備狀態(tài)發(fā)生變化時(shí),設(shè)備軟件就會(huì)對(duì)注冊(cè)的觀察者自動(dòng)調(diào)用統(tǒng)一的接口,將變化后的狀態(tài)向所有觀察者廣播。這樣的好處首先是透明性,設(shè)備軟件安并不知道觀察者隊(duì)列中具體是哪些接口,因?yàn)樗皇褂盟杏^察者都具備的統(tǒng)一接口就能完成消息廣播;其次是擴(kuò)展性和靈活性,當(dāng)設(shè)備接口情況變化時(shí),比如需要添加一個(gè)新的指控接口,只需要單獨(dú)實(shí)現(xiàn)一個(gè)指控接口類,再將其注冊(cè)進(jìn)觀察者隊(duì)列就可以了,無(wú)需在對(duì)已有的其他接口進(jìn)行改動(dòng)。
4.2 組合設(shè)計(jì)模式應(yīng)用
以電子戰(zhàn)無(wú)源干擾作戰(zhàn)的干擾樣式為例,無(wú)源干擾中存在多種干擾資源和多種作戰(zhàn)決策組合使用的情況,由此形成了諸如箔條沖淡、箔條質(zhì)心、紅外沖淡、紅外質(zhì)心干擾、復(fù)合質(zhì)心和復(fù)合沖淡等多種干擾樣式。其中箔條沖淡和紅外沖淡分別使用箔條和紅外兩種單一的干擾資源,可以將它們看作沒有子節(jié)點(diǎn)的leaf,復(fù)合質(zhì)心目前使用箔條和紅外兩種干擾資源,將其看作由箔條質(zhì)心和紅外質(zhì)心的簡(jiǎn)單組合,這樣它就是具有子節(jié)點(diǎn)的Container。這樣的好處也是兩點(diǎn):首先不管leaf還是Container,目的都是進(jìn)行戰(zhàn)術(shù)解算得出干擾決策,其數(shù)據(jù)輸入和輸出的接口完全一致,因此可以由基類Component提供統(tǒng)一接口,外部使用時(shí)也只是調(diào)用Component的接口,無(wú)需顯示的調(diào)用具體的解算類,這就降低了代碼的耦合度;其次還是擴(kuò)展性,因?yàn)殡S著新技術(shù)新產(chǎn)品的開發(fā),未來(lái)為了應(yīng)對(duì)新制導(dǎo)技術(shù)需要使用新干擾資源和干擾樣式時(shí),只需要在干擾決策樹中添加新的leaf或者Container類就能達(dá)到目的,而原有的干擾樣式不需改變,外部調(diào)用接口也不用改變。
5.結(jié)論
通過(guò)對(duì)組合、觀察者兩種設(shè)計(jì)模式各自特點(diǎn)的敘述,和在Java類庫(kù)中應(yīng)用實(shí)例的分析,得出設(shè)計(jì)模式的兩個(gè)重要優(yōu)點(diǎn):透明性和擴(kuò)展性。透明性降低了代碼模塊之間的耦合度,使得軟件系統(tǒng)在外部需求變更時(shí),盡可能的減少了代碼改動(dòng)量和后續(xù)測(cè)試的工作量,相應(yīng)也就增加了交付軟件的可靠性;擴(kuò)展性使得在同系列、需求差別不大的軟件系統(tǒng)實(shí)現(xiàn)中,添加新模塊時(shí)無(wú)需對(duì)舊模塊進(jìn)行改動(dòng),而可以象拆裝積木一樣按照需求靈活的選擇舊模塊,同時(shí)這個(gè)替換和添加的過(guò)程對(duì)外部使用者而言卻是完全透明的。
要掌握和領(lǐng)會(huì)設(shè)計(jì)模式的精髓,是一個(gè)循序漸進(jìn)的長(zhǎng)期過(guò)程,需要不斷的學(xué)習(xí)和應(yīng)用。而清晰的理解設(shè)計(jì)模式的優(yōu)點(diǎn),明確設(shè)計(jì)模式的目的,則是正確理解和使用設(shè)計(jì)模式,最終學(xué)以致用的前提。
參考文獻(xiàn)
[1]鄒娟,田玉敏.軟件設(shè)計(jì)模式的選擇與實(shí)現(xiàn)[J].計(jì)算機(jī)工程,2004.
[2]楊年禮,張禮平.JAVA類庫(kù)中的設(shè)計(jì)模式[J].計(jì)算機(jī)應(yīng)用與軟件,2004.
[3]劉巖海,鎖志海.設(shè)計(jì)模式及其在軟件設(shè)計(jì)中的應(yīng)用研究[J].西安交通大學(xué)學(xué)報(bào),2005.
[4]鐘茂生,王明文.軟件設(shè)計(jì)模式及其應(yīng)用[J].計(jì)算機(jī)應(yīng)用,2002.
[5]崔立劍,吳平.Java多線程設(shè)計(jì)模式研究[J].計(jì)算機(jī)與現(xiàn)代化,2006.