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