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