23個(gè)設(shè)計(jì)模式C代碼第18觀察者模式OberverPattern_第1頁(yè)
23個(gè)設(shè)計(jì)模式C代碼第18觀察者模式OberverPattern_第2頁(yè)
23個(gè)設(shè)計(jì)模式C代碼第18觀察者模式OberverPattern_第3頁(yè)
23個(gè)設(shè)計(jì)模式C代碼第18觀察者模式OberverPattern_第4頁(yè)
23個(gè)設(shè)計(jì)模式C代碼第18觀察者模式OberverPattern_第5頁(yè)
已閱讀5頁(yè),還剩32頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

1、.NET設(shè)計(jì)模式(19):觀察者模式(ObserverPattern) 概述 在軟件構(gòu)建過程中,我們需要為某些對(duì)象建立一種“通知依賴關(guān)系”一一一個(gè)對(duì)象(目標(biāo)對(duì)象) 的狀態(tài)發(fā)生改變,所有的依賴對(duì)象(觀察者對(duì)象)都將得到通知。如果這樣的依賴關(guān)系過于緊密, 將使軟件不能很好地抵御變化。使用面向?qū)ο蠹夹g(shù),可以將這種依賴關(guān)系弱化,并形成一種穩(wěn)定 的依賴關(guān)系。從而實(shí)現(xiàn)軟件體系結(jié)構(gòu)的松耦合。 意圖 定義對(duì)象間的一種一又t多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得 到通知并被自動(dòng)更新。GOF設(shè)計(jì)模式 結(jié)構(gòu)圖 圖1Observer模式結(jié)構(gòu)圖 生活中的例子 觀察者定義了對(duì)象間一對(duì)多的關(guān)系,當(dāng)

2、一個(gè)對(duì)象的狀態(tài)變化時(shí),所有依賴它的對(duì)象都得到通知并 且自動(dòng)地更新。拍賣演示了這種模式。每個(gè)投標(biāo)人都有一個(gè)標(biāo)有數(shù)字的牌子用于出價(jià)。拍賣師開 始拍賣時(shí),他觀察是否有牌子舉起出價(jià)。每次接受一個(gè)新的出價(jià)都改變了拍賣的當(dāng)前價(jià)格,并且 廣播給所有的投標(biāo)人進(jìn)行新的出價(jià)。 拍手麻(主題) I 接接受出榆受出榆| |z.E新價(jià)格新價(jià)格 IJI 競(jìng)標(biāo)入C貌察者) 圖2使用拍賣例子的觀察者模式 Observer模式解說 下面通過一個(gè)例子來說明Observer模式。監(jiān)控某一個(gè)公司的股票價(jià)格變化,可以有多種方式, 通知的對(duì)象可以是投資者,或者是發(fā)送到移動(dòng)設(shè)備,還有電子郵件等。一開始我們先不考慮Obs erver模式,通

3、過一步步地重構(gòu),最終重構(gòu)為Observer模式?,F(xiàn)在有這樣兩個(gè)類:Microsoft 和Investor,如下圖所示: Bicrosoft 口睇士 3Fields 一Properti良工Price 中Symbol feIMethods 501319127 Investor Investor Class EFields -Methods VInvestor VSendlata 7Update 圖3UML靜態(tài)圖示例 它們的實(shí)現(xiàn)如下: public class Microsoft ULJ( private Investor _investor; private String _symbol; pri

4、vate double _price; public voidUpdate() _investor.SendData(this); public Investor Investor get( return_investor; set( investor value; 卜; public String Symbol get return_symbol; set _symbol value; public double Price get return_price; set _price value; _public class Investor UU( private string_name;

5、public Investor(stringname) name; public voidSendData(Microsoft ms) Console.WriteLine(Notified 0of1s +changet o2:C”, _name,ms.Symbol,ms.Price); 簡(jiǎn)單的客戶端實(shí)現(xiàn): classProgram UU staticvoidMain(stringargs) T 口口( L IInvestorinvestor=newInvestor(Jom); T IMicrosoftms=newMicrosoft(); I Ims.Investor=inv

6、estor; I Ims.Symbol=Microsoft; I Ims.Price=120.00; T Ims.Update(); 1 IConsole.ReadLine(); T ) 運(yùn)行后結(jié)果如下: NotifiedJomofMicrosoftschangeto120 可以看到,這段代碼運(yùn)行并沒有問題,也確實(shí)實(shí)現(xiàn)了我們最初的設(shè)想的功能,把Microsoft的股 票價(jià)格變化通知到了Jom投資者那兒。但是這里面出現(xiàn)了如下幾個(gè)問題: 1 .Microsoft和Investor之間形成了一種雙向的依賴關(guān)系,即Microsoft調(diào)用了Investor的 方法,而Investor調(diào)用了Microso

7、ft類的屬性。如果有其中一個(gè)類變化,有可能會(huì)引起另一個(gè) 的變化。 2 .當(dāng)出現(xiàn)一種的通知對(duì)象,比如說是移動(dòng)設(shè)備Mobile: classMobile privatestring_no; publicMobile(stringNo) this._no=No; ) publicvoidSendData(Microsoftms)public UU 仃由( IConsole.WriteLine(Notified0of1s+changet o2:C,_no,ms.Symbol,ms.Price); 1 這時(shí)候又t應(yīng)的Microsoft的類就應(yīng)該改變?yōu)槿缦麓a,在Microsot類中增加Mobile,同時(shí)

8、修改 Update()方法使其可以通知到移動(dòng)設(shè)備: publicclassMicrosoft ULJ T 1 1T 1 private Investor _investor; private Mobile _mobile; private String _symbol; private double _price; public voidUpdate() _investor.SendData( this); _mobile.SendData( this); I I 帕 酶 1 醯 I 1 publicMobileMobile getreturn_mobile; set_mobile=value

9、; publicInvestorInvestor 帕 m get 述1 set 卜 1 publicString T 郵1 幽 T get 1 醐 set 卜 I I publicdouble 1 鼓 甄 get 丁 黠 set return_investor; _investor=value; Symbol return_symbol; _symbol=value; Price return_price; _price=value;顯然這樣的設(shè)計(jì)極大的違背了“開放-封閉”原則,這不是我們所想要的,僅僅是新增加了一種 通知對(duì)象,就需要對(duì)原有的Microsoft類進(jìn)行修改,這樣的設(shè)計(jì)是很糟糕的。

10、對(duì)此做進(jìn)一步的抽 象,既然出現(xiàn)了多個(gè)通知對(duì)象,我們就為這些對(duì)象之間抽象出一個(gè)接口,用它來取消Microsoft 和具體的通知對(duì)象之間依賴。 圖4靜態(tài)UML圖示例 實(shí)現(xiàn)代碼如下: publicinterfacelObserver LJLJ( IvoidSendData(Microsoftms); publicclassInvestor:IObserver UU( privatestring_name; I |publicInvestor(stringname) T 前 this._name=name; T IpublicvoidSendData(Microsoftms) 1 口口 IConsol

11、e.WriteLine(Notified0of1s+changet o2:C,_name,ms.Symbol,ms.Price); 1 _publicclassMicrosoft UU privatelObserver_investor; privateString_symbol; _investor.SendData(this); set_symbol=value;1 I T L L privatedouble_price; publicvoidUpdate() public String Symbol get return_symbol; publicdoublePrice L鼓雨 1麟卜

12、 I 丁 T1函函 I甌I上 TL getreturn_price; set_price=value; publicIObserverInvestor getreturn_investor; set_investor=value; 做到這一步,可以看到,我們?cè)诮档蛢烧叩囊蕾囆陨弦呀?jīng)邁進(jìn)了一小步,正在朝著弱依賴性這個(gè) 方向變化。在Microsoft類中已經(jīng)不再依賴于具體的Investor,而是依賴于接口lObserver, 但同時(shí)我們看到,再新出現(xiàn)一個(gè)移動(dòng)設(shè)備這樣的通知對(duì)象,Microsoft類仍然需要改變,對(duì)此我 們?cè)僮鋈缦轮貥?gòu),在Microsoft中維護(hù)一個(gè)lObserver列表,同時(shí)提供相

13、應(yīng)的維護(hù)方法。 圖5靜態(tài)UMK:例圖 Microsoft類的實(shí)現(xiàn)代碼如下: publicclassMicrosoft LJLJ( IprivateListobservers=newList(); privateString_symbol; 1 privatedouble_price; public voidUpdate() foreach(IObserverobin observers) ob.SendData(this); public voidAddObserver(IObserver observer) observers.Add(observer); public voidRemove

14、Observer(IObserver observer) observers.Remove(observer); publicString I 前( 門口get 1 nnset ) L Ipublicdouble T 口口( 門口get I 門口set ) 1 Symbol return_symbol; _symbol=value; Price return_price; _price=value; 此時(shí)客戶端的調(diào)用代碼: classProgram UU voidMain(string口args) ms.AddObserver(investor1); ms.AddObserver(invest

15、or2); ms.Update(); Console.ReadLine();static lObserver investorl newInvestor(Jom); lObserver investor2 newInvestor(TerryLee); Microsoft ms newMicrosoft(); ms.Symbol Microsoft; ms.Price 120.00; 是依賴于抽象的lOberver。存在著的一個(gè)問題是Investor仍然依賴于具體的公司Microsoft 況且公司還會(huì)有很多舊M,Google等,解決這樣的問題很簡(jiǎn)單,只需要再對(duì)Microsoft類做一次 抽象。如

16、下圖所示: 圖6靜態(tài)UMK:例圖 實(shí)現(xiàn)代碼如下: privateListobservers=newList(); privateString_symbol;走到這一步,已經(jīng)有了 Observer模式的影子了, Microsoft類不再依賴于具體的Investor,而 publicabstract classStock UU I 1 privatedouble_price; publicStock(Stringsymbol,doubleprice) ( this._symbol=symbol; this._price=price; publicvoidUpdate() foreach (IObs

17、erverobin observers) 鼓 1 I ob.SendData(this); public void AddObserver(IObserver observer) T I 鼓 I observers.Add(observer); public voidRemoveObserver(IObserver observer) observers.Remove(observer); public String Symbol get return _symbol; public double Price get return _price; ) publicclassMicrosoft:

18、Stock ULJ( T IpublicMicrosoft(Stringsymbol,double L I:base(symbol,price) T 口口) ) publicinterfaceIObserver UU IvoidSendData(Stockstock); ) _publicclassInvestor:IObserver UU privatestring_name; price) publicInvestor(stringname) 帷 this._name=name; I IpublicvoidSendData(Stockstock) 口口 IConsole.WriteLine

19、(Notified0of1s+changet o2:C,_name,stock.Symbol,stock.Price); 客戶端程序代碼如下: classProgram HH IstaticvoidMain(string口args) II 的( IStockms=newMicrosoft(Microsoft,120.00); I ms.AddObserver(newInvestor(Jom); I ms.AddObserver(newInvestor(TerryLee); ms.Update(); I Console.ReadLine(); I I L 到這里我們可以看到,通過不斷的重構(gòu),不斷

20、地抽象,我們由一開始的很糟糕的設(shè)計(jì),逐漸重構(gòu) 為使用Observer模式的這樣一個(gè)方案。在這個(gè)例子里面,IOberser充當(dāng)了觀察者的角色,而S tock則扮演了主題對(duì)象角色,在任何時(shí)候,只要調(diào)用了Stock的Update()方法,它就會(huì)通知它的所有觀察者對(duì)象。同時(shí)可以看到,通過Observer模式,取消了直接依賴,變?yōu)殚g接依賴,這樣大大提供了系統(tǒng)的可維護(hù)性和可擴(kuò)展性。 推模式與拉模式 對(duì)于發(fā)布-訂閱模型,大家都很容易能想到推模式與拉模式,用SQLServer做過數(shù)據(jù)庫(kù)復(fù)制的朋 友對(duì)這一點(diǎn)很清楚。在Observer模式中同樣區(qū)分推模式和拉模式,我先簡(jiǎn)單的解釋一下兩者的 區(qū)別:推模式是當(dāng)有消息時(shí)

21、,把消息信息以參數(shù)的形式傳遞(推)給所有觀察者,而拉模式是當(dāng) 有消息時(shí),通知消息的方法本身并不帶任何的參數(shù),是由觀察者自己到主體對(duì)象那兒取回(拉) 消息。知道了這一點(diǎn),大家可能很容易發(fā)現(xiàn)上面我所舉的例子其實(shí)是一種推模式的Observer模 式。我們先看看這種模式帶來了什么好處:當(dāng)有消息時(shí),所有的觀察者都會(huì)直接得到全部的消息, 并進(jìn)行相應(yīng)的處理程序,與主體對(duì)象沒什么關(guān)系,兩者之間的關(guān)系是一種松散耦合。但是它也有 缺陷,第一是所有的觀察者得到的消息是一樣的,也許有些信息對(duì)某個(gè)觀察者來說根本就用不上, 也就是觀察者不能“按需所取”;第二,當(dāng)通知消息的參數(shù)有變化時(shí),所有的觀察者對(duì)象都要變 化。鑒于以上

22、問題,拉模式就應(yīng)運(yùn)而生了,它是由觀察者自己主動(dòng)去取消息,需要什么信息,就可以取什么,不會(huì)像推模式那樣得到所有的消息參數(shù)。OK說到這兒,你是否對(duì)于推模式和拉模 式有了一點(diǎn)了解呢?我把前面的例子修改為了拉模式,供大家參考,可以看到通知方法是沒有任 abstractclassStock privateListobservers=newList(); privateString_symbol; privatedouble_price; publicStock(Stringsymbol,doubleprice)this._symbol=symbol;何參數(shù)的: public UU 1 1 1T 1函 t

23、his._price price; public voidUpdate() foreach(IObserverobin observers) ob.SendData(); public void AddObserver(IObserver observer) observers.Add(observer); public voidRemoveObserver(IObserver observer) “日 publicMicrosoft(String symbol,doubleprice)粉 Iobservers.Remove(observer); ) L T 前 郵I publicString

24、Symbol getreturn_symbol; ) publicdoublePrice 1 脂 帕 ) getreturn_price; publicclassMicrosoft Stock base(symbol,price) 日日() ) publicinterfacelObserver 日日 IvoidSendData(); ) UU I I L 口口 this._name=name;publicclassInvestor IObserver public Investor( stringname,Stock stock) privatestring_name; privateStoc

25、k_stock; this._stock=stock; ) T IpublicvoidSendData() 1 而 IConsole.WriteLine(Notified0of1s+change o2:C,_name,_stock.Symbol,_stock.Price); 1 I classProgram UU Stockms=newMicrosoft(Microsoft,120.00); ms.AddObserver(newInvestor(Jom,ms);ms.AddObserver(newInvestor(TerryLee,ms); T Ims.Update(); static voi

26、dMain(string口 args) 1 IConsole.ReadLine(); L 當(dāng)然拉模式也是有一些缺點(diǎn)的,主體對(duì)象和觀察者之間的耦合加強(qiáng)了,但是這可以通過抽象的手 段使這種耦合關(guān)系減到最小。感謝idior的意見 .NET中的Observer模式 在.NET中,相信大家對(duì)于事件和委托都已經(jīng)不陌生了,這里就不具體多說了。利用事件和委托 來實(shí)現(xiàn)Observer模式我認(rèn)為更加的簡(jiǎn)單和優(yōu)雅,也是一種更好的解決方案。因?yàn)樵谏厦娴氖纠?中我們可以看到,雖然取消了直接耦合,但是又引入了不必要的約束(暫且這么說吧)。即那些 子類必須都繼承于主題父類,還有觀察者接口等。網(wǎng)上有很多這方面的例子,上面的例

27、子簡(jiǎn)單的 用事件和委托實(shí)現(xiàn)如下,僅供大家參考: classProgram UU IstaticvoidMain(string口args) 口口 IStockstock=newStock(Microsoft,120.00); Investorinvestor=newInvestor(Jom); T Istock.NotifyEvent+=newNotifyEventHandler(investor.SendData); 1 Istock.Update(); T IConsole.ReadLine(); publicdelegatevoidNotifyEventHandler(objectsender); publicclassStock 日日 IpublicNotifyEventHandlerNotifyEvent; I privateString_symbol; privatedouble_price; publicStock(Stringsymbol,doubleprice) 口口 this._symbol=symbol; T this._price=price; 1 IpublicvoidUpd

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論