范振鈞,鞏小東,王增強
(通化師范學院 計算機學院,吉林 通化134002)
設計模式就是對象(類 )的集合,它具有解決某一類特定問題的功能,而且是解決這類問題的最優(yōu)方案[1].應用設計模式可以使軟件具備良好的可靠性、可擴展性、可復用性和可維護性.
觀察者模式是設計模式中的經典模式,它定義了對象間的一對多依賴關系.當一方的對象改變狀態(tài)時,所有的依賴者都會被通知并自動被更新[2].在觀察者模式中,被依賴的一方叫做目標或主題(Subject),依賴方叫做觀察者(Observers).觀察者模式可以讓多個觀察者同時監(jiān)聽同一個主題,當主題對象發(fā)生變化時,通知所有觀察者,使各個觀察者能作出相應的響應.主題和觀察者之間是松耦合,主題可隨時增加或減少觀察者而自身不受影響.觀察者根據主題狀態(tài)的改變,對自身的行為和數據做相應的變化,這種變化對主題沒有任何影響.使用觀察者模式主要目的就是減少組件間耦合度,減少對象之間的依賴關系.
在Observer 模式中有3 種基本的操作, 即注冊、通知和注銷.
(1)注冊.觀察者類調用主題的注冊方法,在主題類中登記.
(2)通知.當主題的數據變化時,主題向注冊的觀察者發(fā)送消息.
(3)注銷.當觀察者不需要繼續(xù)觀察主題,執(zhí)行主題類的注銷操作,解除了對主題的觀察.
傳統(tǒng)方法一般都采用接口的方法.抽象觀察者類提供了與主題中的數據變化相適應的更新方法,所有觀察者類都要實現這個接口,用來實現觀察者自身行為或外觀的變化.主題接口提供了注冊、注銷以及通知操作等三個抽象方法,充當主題的類必須實現這三個方法.傳統(tǒng)的觀察者實現代碼如下:
Public interface iRaditionalObserver // 觀察者接口
{void ChangeUpdate(object anobject) //觀察者更新方法 }
//主題接口
Public Interface Subject
{ void Logon(RaditionalObserver anObserver);//注冊}
void logout(IRationalObserver anObserver);//注銷 }
//觀察者實例
public class Observer:iRaditionalObserver
{ public void ChangeUpdate (object anObject)
{ //觀察者接口中的更新方法, 實現自身外觀或行為的改變 } }
//主題實例
public class RaditionalSubject:Subject
protected Hashtable observerContainer= new Hashtable( ) ;//在主題類中有個列表,作為觀察者的容器來存放觀察者
public void logon( RaditionalObserver anObserver)
{ observerContainer.Add(RaditionalObserver anObserver) //注冊 }
public void logout( RaditionalObserver anObserver)
{ observerContainer.Remove(anObserver) //注銷 }
public void Notify(Object anObject)
{ foreach(RaditionalObserver anObserver in ObserverContainer)
{ anObserver.Notify(anObject) ;//觀察者鏈表的循環(huán)訪問通知.}
在上述觀察者模式實現方法中,抽象出觀察者接口后,目標和觀察者就只是在抽象層面上耦合,也就是說目標只是知道觀察者接口,并不知道具體的觀察者的類,從而實現目標類和具體的觀察者類之間解耦,完成了觀察者模式的主要功能.
具體主題類RaditonalSubject中包含了一個私有的哈希對象obserContainer,它是一個保存觀察者對象的容器.主題類中通過logon方法將觀察者加入到哈希表中實現注冊操作,通過logout方法撤銷觀察者和主體的聯系,實現注銷操作,當主題的狀態(tài)變化時,通過Notify方法遍歷哈希表中的所有觀察者,通知其數據的改變,實現通知操作.在傳統(tǒng)方法中存在著如下缺點:
(1)主題類要保存觀察者的列表,從而實現注冊、注銷等服務,所以觀察者和觀察者之間還存在著間接的依賴關系.
(2) 如果一個被觀察者對象有很多的觀察者的話,通知會花費很多時間.因為存在著觀察者的列表,存在著列表的循環(huán)遍歷,通知的效率取決于列表的規(guī)模,當觀察者的數目很大時,容易形成擁塞.
為了在Observer 模式中實現進一步的低耦合,可以利用.net提供的委托和事件技術實現Observer 模式[3].
委托是一個類,它定義了方法的類型,使得可以將方法當作另一個方法的參數來進行傳遞,這種將方法動態(tài)地賦給參數的做法,可以避免在程序中大量使用選擇語句,使程序具有更好的可擴展性.委托可以不知道自己封裝方法所屬類的詳細信息,只要調用委托的方法的參數類型和返回類型與委托的相匹配,該方法就可以被調用.我們可以充分利用委托的這一重要屬性,結合事件機制實現觀察者模式,進一步地降低對象間的耦合性,將靜態(tài)的組合關系變?yōu)檫\行時的動態(tài)組合關系.
委托的另一個重要的屬性是多播.通過多播屬性可以實現觀察者的注冊和注銷.
在.net平臺中,事件就是對委托的進一步封裝,事件是當對象發(fā)生某些變化時,用于向該類的客戶提供通知的一種結構.事件基于委托,為委托提供一種發(fā)布訂閱機制[4],事件可以簡單地分成兩個部分,事件發(fā)生的類和事件接收處理的類.事件發(fā)生的類就是在這個類中觸發(fā)了一個事件,但這個類并不知道哪個對象或方法將會收到并處理它觸發(fā)的事件,觸發(fā)事件時調用的處理程序方法需要定義,其參數由委托類型來定義.事件發(fā)生的類就可以看做主題,事件接收處理的類作為觀察者.發(fā)送方和接收方之間可以通過委托作為媒介,來完成事件的處理.
(1)實現方案.①主題類的設計.在主題類中建立一個代理, 將更新函數的簽名傳遞給這個代理,然后在主題類中用這個代理聲明一個事件,定義事件觸發(fā)機制.②觀察者類的設計.觀察者類需要提供一個更新函數,確保該更新函數的簽名和代理的簽名一致.③注冊和注銷操作.在程序運行時,觀察者類通過+=操作將更新函數注冊到主題類中,當觀察者不需要觀察主題對象時,可以從委托中刪除方法調用實現注銷操作.
(2)具體實現例子.通過一個簡單的實例來說明.net下觀察者模式的實現過程.在一個股票推薦的網站中,當注冊的投資者所投資的股票價格在股票市場發(fā)生變化時,投資者可以自動得到通知.
//聲明委托作為事件與觀察者的中介
Public delegate void delegateUpdate(double price)
//定義主題類
class Stock
{ Public event delegateUpdate handlerPriceChange; //引入事件,使用委托作為事件代理
Protected double price; //股票價格
Public double Price // Properties
{set //寫屬性
{price=value;
if(handlerPriceChange!=null)//事件引發(fā),通過委托發(fā)給觀察者
delegateUpdate(value);
}
}
class Investor//觀察者角色
{public void Update(double price)//和委托的簽名必須相同
{Console.WriteLine("Price is changed {0}",price);}
|
主程序:
Stock ibm=new stock();//主題類實例
Investor s=new Investor();//觀察者實例
Ibm.HandlerPriceChange+=s.Update; //注冊觀察者s
ibm.HandlrPriceChange+=b.Update; //注冊觀察者b
//Change price,which notifies investors
ibm.Price=121.00;//主題類觸發(fā)事件,自動通知所有注冊的觀察者
Ibm.HandlerPriceChange-=s.Update; //注銷s
Ibm.HandlrPriceChange-=b.Update; //注銷b
在上述代碼中,主題類和觀察者類都沒有實現任何接口,主題類只需提供一個代理和一個事件,就能將數據變化的信息逐一發(fā)送到每個觀察者對象,并且還能更加容易的增加新的觀察者對象,根本不需要考慮它從何處繼承而來,也不需要統(tǒng)一他們的接口調用方法.觀察者類只需要實現針對主題變化的更新方法,并把該方法注冊到主題類中提供的事件中去,當觀察者不需要觀察主題類中的變化時,通過-=操作實現注銷功能.通過代理和事件機制,改進了傳統(tǒng)方法中的鏈表及相應的遍歷操作帶來的缺陷,將靜態(tài)的組合關系轉變?yōu)檫\行時的動態(tài)組合關系,由于沒有使用接口,提高了系統(tǒng)的靈活性.
在.net平臺下的觀察者模式實現方案中,注冊、注銷、通知三種基本操作可以通過委托結合事件機制動態(tài)進行,解除了目標對象對觀察者的依賴,使目標與觀察者的藕合性更低,增加了設計的靈活性.
參考文獻:
[1]林碧英,曲俊華.設計模式在電子商務交易網站中的應用[J].計算機系統(tǒng)應用,2005(1).
[2]Erich Gamma, Richard Helm, Ralph Johnson.設計模式:可復用面向對象軟件的基礎[M].李英軍,馬曉星,蔡敏,等譯.北京: 機械工業(yè)出版社,2005.
[3]喻金平,劉孝會.C#中的委托在Observer 設計模式中的應用研究[J].軟件導刊,2010,9(11).
[4]ChrisTlan Nagel .C#高級編程[M].北京:清華大學出版社,2011.