34观察者模式(Observer Pattern)
生活随笔
收集整理的這篇文章主要介紹了
34观察者模式(Observer Pattern)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
動機(Motivate):
????在軟件構建 過程中,我們需要為某些對象建立一種“通知依賴關系” --------一個對象(目標對象)的狀態發生改變,所有的依賴對象(觀察者對象)都將得到通知。如果這樣的依賴關系過于緊密,將使軟件不能很好地抵御變化。使用面 向對象技術,可以將這種依賴關系弱化,并形成一種穩定的依賴關系。從而實現軟件體系結構的松耦合。
意圖(Intent):
????定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時, 所有依賴于它的對象都得到通知并被自動更新。
??? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -------《設計模式》GOF
結構圖(Struct):
??? ?? ?? ???
適用性:
生活中的例子:??
代碼實現: ?1?????public?class?BankAccount
?2?????{
?3?????????Emailer?emailer;??//強信賴關系
?4?????????Mobile??phoneNumber;????//強信賴關系
?5????????
?6?????????private?double?_money;
?7?
?8?????????public?Emailer?Emailer
?9?????????{
10?????????????get?{?return?emailer;?}
11?????????????set?{?this.emailer?=?value;?}
12?????????}
13?????????public?Mobile?PhoneNumber
14?????????{
15?????????????get?{?return?phoneNumber;?}
16?????????????set?{?this.phoneNumber?=?value;?}
17?????????}
18?????????public?double?Money
19?????????{
20?????????????get?{?return?_money;?}
21?????????????set?{?this._money?=?value;?}
22?????????}
23???
24?????????public?void?WithDraw()
25?????????{
26?????????????emailer.SendEmail(this);
27?????????????phoneNumber.SendNotification(this);
28?????????}
29?
30?????} ? ?1?????public?class?Emailer
?2?????{
?3????????private?string?_emailer;
?4????????public?Emailer(string?emailer)
?5????????{
?6????????????this._emailer?=?emailer;
?7????????}
?8?????????public?void?SendEmail(BankAccount?ba)
?9?????????{
10?????????????//..
11?????????????Console.WriteLine("Notified?:?Emailer?is?{0},?You?withdraw??{1:C}?",?_emailer,?ba.Money);
12?????????}
13?????} ? ?1?????public?class?Mobile
?2?????{
?3?????????private?long?_phoneNumber;
?4?????????public?Mobile(long?phoneNumber)
?5?????????{
?6?????????????this._phoneNumber?=?phoneNumber;
?7?????????}
?8?????????public?void?SendNotification(BankAccount?ba)
?9?????????{
10?????????????Console.WriteLine("Notified?:Phone?number?is?{0}?You?withdraw??{1:C}?",?_phoneNumber,?ba.Money);
11?????????}
12?????} 此時簡單的客戶端調用如下: ? ?1?class?Test
?2?{
?3?????static?void?Main(string[]?args)
?4?????{
?5?????????BankAccount?ba?=?new?BankAccount();
?6?????????Emailer?emailer?=?new?Emailer("abcdwxc@163.com");???????
?7?????????Mobile?mobile?=?new?Mobile(13901234567);
?8?????????ba.Emailer?=?emailer;
?9?????????ba.PhoneNumber?=?mobile;
10?????????ba.Money?=?2000;???????
11?????????ba.WithDraw();
12?????}
13?} 運行結果如下:
??? 由此可見程序可以正常運行,但請注意BandAccount和Emailer及Mobile之間形成了一種雙向的依賴關系,即BankAccount調用了Emailer及Mobile的方法,而Emailer及Mobile調用了BnadAccount類的屬性。如果有其中一個類變化,有可能會引起另一個的變化。如果又需添加一種新的通知方式,就得在BankAccount的WithDraw()方法中增加對該中通知方式的調用。
??? 顯然這樣的設計極大的違背了“開放-封閉”原則,這不是我們所想要的,僅僅是新增加了一種通知對象,就需要對原有的BankAccount類進行修改,這樣的設計是很糟糕的。對此做進一步的抽象,既然出現了多個通知對象,我們就為這些對象之間抽象出一個接口,用它來取消BankAccount和具體的通知對象之間依賴。
由此我們由左圖轉換到右圖。
??? ?? ?? ???
實例代碼如下: 1???public?interface?IObserverAccount
2?????????{
3?????????????void?Update(BankAccount?ba);
4?????????} ? ?1??public?class?BankAccount
?2?????????{
?3?????????????IObserverAccount?emailer;????????//依賴于接口
?4?????????????IObserverAccount?phoneNumber;????//依賴于接口
?5?
?6?????????????private?double?_money;
?7?
?8?????????????public?IObserverAccount?Emailer
?9?????????????{
10?????????????????get?{?return?emailer;?}
11?????????????????set?{?this.emailer?=?value;?}
12?????????????}
13?????????????public?IObserverAccount?PhoneNumber
14?????????????{
15?????????????????get?{?return?phoneNumber;?}
16?????????????????set?{?this.phoneNumber?=?value;?}
17?????????????}
18?????????????public?double?Money
19?????????????{
20?????????????????get?{?return?_money;?}
21?????????????????set?{?this._money?=?value;?}
22?????????????}
23?
24?????????????public?void?WithDraw()
25?????????????{
26?????????????????emailer.Update(this);
27?????????????????phoneNumber.Update(this);
28?????????????}
29?
30?????????} ? ?1???????public?class?Emailer?:?IObserverAccount
?2?????????{
?3?????????????private?string?_emailer;
?4?????????????public?Emailer(string?emailer)
?5?????????????{
?6?????????????????this._emailer?=?emailer;
?7?????????????}
?8?????????????public?void?Update(BankAccount?ba)
?9?????????????{
10?????????????????//..
11?????????????????Console.WriteLine("Notified?:?Emailer?is?{0},?You?withdraw??{1:C}?",?_emailer,?ba.Money);
12?????????????}
13?????????} ? ?1???????public?class?Mobile?:?IObserverAccount
?2?????????{
?3?????????????private?long?_phoneNumber;
?4?????????????public?Mobile(long?phoneNumber)
?5?????????????{
?6?????????????????this._phoneNumber?=?phoneNumber;
?7?????????????}
?8?????????????public?void?Update(BankAccount?ba)
?9?????????????{
10?????????????????Console.WriteLine("Notified?:Phone?number?is?{0}?You?withdraw??{1:C}?",?_phoneNumber,?ba.Money);
11?????????????}
12?????????} 客戶端與上方相同,其運行結果也相同。但BankAccount增加和刪除通知對象時,還需對其進行修改。對此我們再做如下重構,在BankAccount中維護一個IObserver列表,同時提供相應的維護方法。 ?1?????public?class?BankAccount
?2?????{
?3?????????private?List<IObserverAccount>?Observers?=?new?List<IObserverAccount>();
?4?
?5?
?6?????????private?double?_money;
?7?
?8?????????public?double?Money
?9?????????{
10?????????????get?{?return?_money;?}
11?????????????set?{?this._money?=?value;?}
12?????????}
13?
14?????????public?void?WithDraw()
15?????????{
16?????????????foreach?(IObserverAccount?ob?in?Observers)
17?????????????{
18?????????????????ob.Update(this);
19?
20?????????????}
21?????????}
22?????????public?void?AddObserver(IObserverAccount?observer)
23?????????{
24?????????????Observers.Add(observer);
25?????????}
26?????????public?void?RemoverObserver(IObserverAccount?observer)
27?????????{
28?????????????Observers.Remove(observer);
29?????????}
30?
31?????} 此時客戶端代碼如下: ?1???class?Test
?2?????{
?3?????????static?void?Main(string[]?args)
?4?????????{
?5?????????????BankAccount?ba?=?new?BankAccount();
?6?????????????IObserverAccount?emailer?=?new?Emailer("abcdwxc@163.com");
?7?????????????IObserverAccount?mobile?=?new?Mobile(13901234567);
?8?
?9?????????????ba.Money?=?2000;
10?????????????ba.AddObserver(emailer);
11?????????????ba.AddObserver(mobile);
12?
13?????????????ba.WithDraw();
14?????????}
15?????} ??? 走到這一步,已經有了Observer模式的影子了,BankAccount類不再依賴于具體的Emailer或Mobile,而是依賴于抽象的IObserverAccount。存在著的一個問題是Emailer或Mobile仍然依賴于具體的BankAccount,解決這樣的問題很簡單,只需要再對BankAccount類做一次抽象。如下圖:
??? ?? ????1??public?abstract?class?Subject
?2?????{
?3?????????private?List<IObserverAccount>?Observers?=?new?List<IObserverAccount>();
?4?
?5?????????private?double?_money;
?6?????????public?Subject(double?money)
?7?????????{
?8?????????????this._money?=?money;
?9?????????}
10?
11?????????public?double?Money
12?????????{
13?????????????get?{?return?_money;?}
14?????????}
15??????
16?????????public?void?WithDraw()
17?????????{
18?????????????foreach?(IObserverAccount?ob?in?Observers)
19?????????????{
20?????????????????ob.Update(this);
21?
22?????????????}
23?????????}
24?????????public?void?AddObserver(IObserverAccount?observer)
25?????????{
26?????????????Observers.Add(observer);
27?????????}
28?????????public?void?RemoverObserver(IObserverAccount?observer)
29?????????{
30?????????????Observers.Remove(observer);
31?????????}
32?
33?????} ? 1?????public?interface?IObserverAccount
2?????{
3?????????void?Update(Subject?subject);
4?????} ? 1?????public?class?BankAccount?:?Subject
2?????{
3?????????public?BankAccount(double?money)
4?????????????:?base(money)
5?????????{?}
6?
7?????} ? ?1?????public?class?Emailer?:?IObserverAccount
?2?????{
?3?????????private?string?_emalier;??????
?4?????????public?Emailer(string?emailer?)
?5?????????{
?6?????????????this._emalier?=?emailer;???????????
?7?????????}
?8?????????public?void?Update(Subject?subject)
?9?????????{????????????
10?????????????Console.WriteLine("Notified?:?Emailer?is?{0},?You?withdraw??{1:C}?",?_emalier,?subject.Money);
11?????????}
12?????} ? ?1????public?class?Mobile?:?IObserverAccount
?2?????{
?3?????????private?long?_phoneNumber;????????
?4?????????public?Mobile(long?phoneNumber)
?5?????????{
?6?????????????this._phoneNumber?=?phoneNumber;????????????
?7?????????}
?8?????????public?void?Update(Subject?subject)
?9?????????{
10?????????????Console.WriteLine("Notified?:Phone?number?is?{0}?You?withdraw??{1:C}?",?_phoneNumber,?subject.Money);
11?????????}
12?????} 此時客戶端實現如下: ?1????class?Test
?2?????{
?3?????????static?void?Main(string[]?args)
?4?????????{
?5?????????????Subject?subject?=?new?BankAccount(2000);
?6?????????????subject.AddObserver(new?Emailer("abcdwxc@163.com"));
?7?????????????subject.AddObserver(new?Mobile(13901234567));
?8?
?9?????????????subject.WithDraw();
10?????????}
11?????}
推模式與拉模式
??? 對于發布-訂閱模型,大家都很容易能想到推模式與拉模式,用SQL Server做過數據庫復制的朋友對這一點很清楚。在Observer模式中同樣區分推模式和拉模式,我先簡單的解釋一下兩者的區別:推模式是當有消息時,把消息信息以參數的形式傳遞(推)給所有觀察者,而拉模式是當有消息時,通知消息的方法本身并不帶任何的參數,是由觀察者自己到主體對象那兒取回(拉)消息。知道了這一點,大家可能很容易發現上面我所舉的例子其實是一種推模式的Observer模式。我們先看看這種模式帶來了什么好處:當有消息時,所有的 觀察者都會直接得到全部的消息,并進行相應的處理程序,與主體對象沒什么關系,兩者之間的關系是一種松散耦合。但是它也有缺陷,第一是所有的觀察者得到的 消息是一樣的,也許有些信息對某個觀察者來說根本就用不上,也就是觀察者不能“按需所取”;第二,當通知消息的參數有變化時,所有的觀察者對象都要變化。鑒于以上問題,拉模式就應運而生了,它是由觀察者自己主動去取消息,需要什么信息,就可以取什么,不會像推模式那樣得到所有的消息參數。
拉模式實現如下: ?1?public?abstract?class?Subject
?2?????{
?3?????????private?List<IObserverAccount>?Observers?=?new?List<IObserverAccount>();
?4?
?5?
?6?????????private?double?_money;
?7?
?8?????????public?double?Money
?9?????????{
10?????????????get?{?return?_money;?}????????????
11?????????}
12?????????public?Subject(double?money)
13?????????{
14?????????????this._money?=?money;
15?????????}
16?????????public?void?WithDraw()
17?????????{
18?????????????foreach?(IObserverAccount?ob?in?Observers)
19?????????????{
20?????????????????ob.Update();
21?
22?????????????}
23?????????}
24?????????public?void?AddObserver(IObserverAccount?observer)
25?????????{
26?????????????Observers.Add(observer);
27?????????}
28?????????public?void?RemoverObserver(IObserverAccount?observer)
29?????????{
30?????????????Observers.Remove(observer);
31?????????}
32?
33?????} ? 1????public?interface?IObserverAccount
2?????{
3?????????void?Update();
4?????} ? 1?????public?class?BankAccount?:Subject
2?????{
3?????????public?BankAccount(double?money)
4?????????????:?base(money)
5?????????{?}
6????????
7?????} ? ?1?????public?class?Emailer?:?IObserverAccount
?2?????{
?3?????????private?string?_emalier;
?4?????????private?Subject?_subject;
?5?????????public?Emailer(string?emailer,Subject?subject)
?6?????????{
?7?????????????this._emalier?=?emailer;
?8?????????????this._subject?=?subject;
?9?????????}
10?????????public?void?Update()
11?????????{
12?????????????//..
13?????????????Console.WriteLine("Notified?:?Emailer?is?{0},?You?withdraw??{1:C}?",?_emalier,_subject.Money);
14?????????}
15?????} ? ?1?????public?class?Mobile?:?IObserverAccount
?2?????{
?3?????????private?long?_phoneNumber;
?4?????????private?Subject?_subject;
?5?????????public?Mobile(long?phoneNumber,Subject?subject)
?6?????????{
?7?????????????this._phoneNumber?=?phoneNumber;
?8?????????????this._subject?=?subject;
?9?????????}
10?????????public?void?Update()
11?????????{
12?????????????Console.WriteLine("Notified?:Phone?number?is?{0}?You?withdraw??{1:C}?",?_phoneNumber,_subject.Money);
13?????????}
14?????} 此時客戶端調用如下: ?1????class?Test
?2?????{
?3?????????static?void?Main(string[]?args)
?4?????????{
?5????????????Subject?subject=?new?BankAccount(2000);??????????
?6?????????????subject.AddObserver(new?Emailer("abcdwxc@163.com",subject));
?7?????????????subject.AddObserver(new?Mobile(13901234567,subject));
?8?
?9?????????????subject.WithDraw();
10?????????}
11?????} .NET中Observer實現:
????用事件和委托來實現Observer模式我認為更加的簡單和優雅,也是一種更好的解決方案。 ?1??public??class?Subject
?2?????{
?3?????????public?event?NotifyEventHandler?NotifyEvent;
?4?
?5?????????private?double?_money;
?6?????????public?Subject(double?money)
?7?????????{
?8?????????????this._money?=?money;
?9?????????}
10?
11?????????public?double?Money
12?????????{
13?????????????get?{?return?_money;?}
14?????????}
15?
16?????????public?void?WithDraw()
17?????????{
18?????????????OnNotifyChange();
19?????????}
20?????????public?void?OnNotifyChange()
21?????????{
22?????????????if?(NotifyEvent?!=?null)
23?????????????{
24?????????????????NotifyEvent(this);
25?????????????}
26?
27?????????}
28?
29?????} ? ?1?????public?class?Emailer
?2?????{
?3?????????private?string?_emalier;
?4?????????public?Emailer(string?emailer)
?5?????????{
?6?????????????this._emalier?=?emailer;
?7?????????}
?8?????????public?void?Update(object?obj)
?9?????????{
10?????????????if?(obj?is?Subject)
11?????????????{
12?????????????????Subject?subject?=?(Subject)obj;
13?
14?????????????????Console.WriteLine("Notified?:?Emailer?is?{0},?You?withdraw??{1:C}?",?_emalier,?subject.Money);
15?????????????}
16?????????}
17?} public?delegate?void?NotifyEventHandler(object?sender); 客戶端調用如下: ?1?????class?Test
?2?????????{
?3?????????????static?void?Main(string[]?args)
?4?????????????{
?5?????????????????Subject?subject?=?new?Subject(2000);
?6?????????????????Emailer?emailer?=?new?Emailer("abcdwxc@163.com");
?7?????????????????subject.NotifyEvent?+=?new?NotifyEventHandler(emailer.Update);
?8????????????
?9?
10?????????????????subject.WithDraw();
11?????????????}
12?????????}
Observer實現要點:
??
?? ? #12樓?2009-04-17 14:30?soxyunyi 是否體現推拉,是由觀察者類的update方法參數決定的。 支持(0)?反對(0)
?? ? #13樓?2009-04-17 14:34?soxunyi public void update(Observable obs, Object arg)?
這是Java中Obersver接口方法,?
如果在方法體中通過obs來獲取主題的狀態變化,則是拉模式;?
如果在方法體中通過arg來獲取主題的狀態變化,則是推模式。?
因為,通過obs獲取是在觀察者類中實現的,故為拉。?
而arg則是由主題類傳遞給觀察者的,故為推。
????在軟件構建 過程中,我們需要為某些對象建立一種“通知依賴關系” --------一個對象(目標對象)的狀態發生改變,所有的依賴對象(觀察者對象)都將得到通知。如果這樣的依賴關系過于緊密,將使軟件不能很好地抵御變化。使用面 向對象技術,可以將這種依賴關系弱化,并形成一種穩定的依賴關系。從而實現軟件體系結構的松耦合。
意圖(Intent):
????定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時, 所有依賴于它的對象都得到通知并被自動更新。
??? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -------《設計模式》GOF
結構圖(Struct):
??? ?? ?? ???
適用性:
1.當一個抽象模型有兩個方面, 其中一個方面依賴于另一方面。將這二者封裝在獨立的對象中以使它們可以各自獨立地改變和復用。
2.當對一個對象的改變需要同時改變其它對象, 而不知道具體有多少對象有待改變。
3.當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之, 你不希望這些對象是緊密耦合的。生活中的例子:??
??? 觀 察者定義了對象間一對多的關系,當一個對象的狀態變化時,所有依賴它的對象都得到通知并且自動地更新。在ATM取款,當取款成功后,以手機、郵件等方式進行通知。
?
?? ?? ??? ??? ??? ??? ???????代碼實現: ?1?????public?class?BankAccount
?2?????{
?3?????????Emailer?emailer;??//強信賴關系
?4?????????Mobile??phoneNumber;????//強信賴關系
?5????????
?6?????????private?double?_money;
?7?
?8?????????public?Emailer?Emailer
?9?????????{
10?????????????get?{?return?emailer;?}
11?????????????set?{?this.emailer?=?value;?}
12?????????}
13?????????public?Mobile?PhoneNumber
14?????????{
15?????????????get?{?return?phoneNumber;?}
16?????????????set?{?this.phoneNumber?=?value;?}
17?????????}
18?????????public?double?Money
19?????????{
20?????????????get?{?return?_money;?}
21?????????????set?{?this._money?=?value;?}
22?????????}
23???
24?????????public?void?WithDraw()
25?????????{
26?????????????emailer.SendEmail(this);
27?????????????phoneNumber.SendNotification(this);
28?????????}
29?
30?????} ? ?1?????public?class?Emailer
?2?????{
?3????????private?string?_emailer;
?4????????public?Emailer(string?emailer)
?5????????{
?6????????????this._emailer?=?emailer;
?7????????}
?8?????????public?void?SendEmail(BankAccount?ba)
?9?????????{
10?????????????//..
11?????????????Console.WriteLine("Notified?:?Emailer?is?{0},?You?withdraw??{1:C}?",?_emailer,?ba.Money);
12?????????}
13?????} ? ?1?????public?class?Mobile
?2?????{
?3?????????private?long?_phoneNumber;
?4?????????public?Mobile(long?phoneNumber)
?5?????????{
?6?????????????this._phoneNumber?=?phoneNumber;
?7?????????}
?8?????????public?void?SendNotification(BankAccount?ba)
?9?????????{
10?????????????Console.WriteLine("Notified?:Phone?number?is?{0}?You?withdraw??{1:C}?",?_phoneNumber,?ba.Money);
11?????????}
12?????} 此時簡單的客戶端調用如下: ? ?1?class?Test
?2?{
?3?????static?void?Main(string[]?args)
?4?????{
?5?????????BankAccount?ba?=?new?BankAccount();
?6?????????Emailer?emailer?=?new?Emailer("abcdwxc@163.com");???????
?7?????????Mobile?mobile?=?new?Mobile(13901234567);
?8?????????ba.Emailer?=?emailer;
?9?????????ba.PhoneNumber?=?mobile;
10?????????ba.Money?=?2000;???????
11?????????ba.WithDraw();
12?????}
13?} 運行結果如下:
??? 由此可見程序可以正常運行,但請注意BandAccount和Emailer及Mobile之間形成了一種雙向的依賴關系,即BankAccount調用了Emailer及Mobile的方法,而Emailer及Mobile調用了BnadAccount類的屬性。如果有其中一個類變化,有可能會引起另一個的變化。如果又需添加一種新的通知方式,就得在BankAccount的WithDraw()方法中增加對該中通知方式的調用。
??? 顯然這樣的設計極大的違背了“開放-封閉”原則,這不是我們所想要的,僅僅是新增加了一種通知對象,就需要對原有的BankAccount類進行修改,這樣的設計是很糟糕的。對此做進一步的抽象,既然出現了多個通知對象,我們就為這些對象之間抽象出一個接口,用它來取消BankAccount和具體的通知對象之間依賴。
由此我們由左圖轉換到右圖。
??? ?? ?? ???
實例代碼如下: 1???public?interface?IObserverAccount
2?????????{
3?????????????void?Update(BankAccount?ba);
4?????????} ? ?1??public?class?BankAccount
?2?????????{
?3?????????????IObserverAccount?emailer;????????//依賴于接口
?4?????????????IObserverAccount?phoneNumber;????//依賴于接口
?5?
?6?????????????private?double?_money;
?7?
?8?????????????public?IObserverAccount?Emailer
?9?????????????{
10?????????????????get?{?return?emailer;?}
11?????????????????set?{?this.emailer?=?value;?}
12?????????????}
13?????????????public?IObserverAccount?PhoneNumber
14?????????????{
15?????????????????get?{?return?phoneNumber;?}
16?????????????????set?{?this.phoneNumber?=?value;?}
17?????????????}
18?????????????public?double?Money
19?????????????{
20?????????????????get?{?return?_money;?}
21?????????????????set?{?this._money?=?value;?}
22?????????????}
23?
24?????????????public?void?WithDraw()
25?????????????{
26?????????????????emailer.Update(this);
27?????????????????phoneNumber.Update(this);
28?????????????}
29?
30?????????} ? ?1???????public?class?Emailer?:?IObserverAccount
?2?????????{
?3?????????????private?string?_emailer;
?4?????????????public?Emailer(string?emailer)
?5?????????????{
?6?????????????????this._emailer?=?emailer;
?7?????????????}
?8?????????????public?void?Update(BankAccount?ba)
?9?????????????{
10?????????????????//..
11?????????????????Console.WriteLine("Notified?:?Emailer?is?{0},?You?withdraw??{1:C}?",?_emailer,?ba.Money);
12?????????????}
13?????????} ? ?1???????public?class?Mobile?:?IObserverAccount
?2?????????{
?3?????????????private?long?_phoneNumber;
?4?????????????public?Mobile(long?phoneNumber)
?5?????????????{
?6?????????????????this._phoneNumber?=?phoneNumber;
?7?????????????}
?8?????????????public?void?Update(BankAccount?ba)
?9?????????????{
10?????????????????Console.WriteLine("Notified?:Phone?number?is?{0}?You?withdraw??{1:C}?",?_phoneNumber,?ba.Money);
11?????????????}
12?????????} 客戶端與上方相同,其運行結果也相同。但BankAccount增加和刪除通知對象時,還需對其進行修改。對此我們再做如下重構,在BankAccount中維護一個IObserver列表,同時提供相應的維護方法。 ?1?????public?class?BankAccount
?2?????{
?3?????????private?List<IObserverAccount>?Observers?=?new?List<IObserverAccount>();
?4?
?5?
?6?????????private?double?_money;
?7?
?8?????????public?double?Money
?9?????????{
10?????????????get?{?return?_money;?}
11?????????????set?{?this._money?=?value;?}
12?????????}
13?
14?????????public?void?WithDraw()
15?????????{
16?????????????foreach?(IObserverAccount?ob?in?Observers)
17?????????????{
18?????????????????ob.Update(this);
19?
20?????????????}
21?????????}
22?????????public?void?AddObserver(IObserverAccount?observer)
23?????????{
24?????????????Observers.Add(observer);
25?????????}
26?????????public?void?RemoverObserver(IObserverAccount?observer)
27?????????{
28?????????????Observers.Remove(observer);
29?????????}
30?
31?????} 此時客戶端代碼如下: ?1???class?Test
?2?????{
?3?????????static?void?Main(string[]?args)
?4?????????{
?5?????????????BankAccount?ba?=?new?BankAccount();
?6?????????????IObserverAccount?emailer?=?new?Emailer("abcdwxc@163.com");
?7?????????????IObserverAccount?mobile?=?new?Mobile(13901234567);
?8?
?9?????????????ba.Money?=?2000;
10?????????????ba.AddObserver(emailer);
11?????????????ba.AddObserver(mobile);
12?
13?????????????ba.WithDraw();
14?????????}
15?????} ??? 走到這一步,已經有了Observer模式的影子了,BankAccount類不再依賴于具體的Emailer或Mobile,而是依賴于抽象的IObserverAccount。存在著的一個問題是Emailer或Mobile仍然依賴于具體的BankAccount,解決這樣的問題很簡單,只需要再對BankAccount類做一次抽象。如下圖:
??? ?? ????1??public?abstract?class?Subject
?2?????{
?3?????????private?List<IObserverAccount>?Observers?=?new?List<IObserverAccount>();
?4?
?5?????????private?double?_money;
?6?????????public?Subject(double?money)
?7?????????{
?8?????????????this._money?=?money;
?9?????????}
10?
11?????????public?double?Money
12?????????{
13?????????????get?{?return?_money;?}
14?????????}
15??????
16?????????public?void?WithDraw()
17?????????{
18?????????????foreach?(IObserverAccount?ob?in?Observers)
19?????????????{
20?????????????????ob.Update(this);
21?
22?????????????}
23?????????}
24?????????public?void?AddObserver(IObserverAccount?observer)
25?????????{
26?????????????Observers.Add(observer);
27?????????}
28?????????public?void?RemoverObserver(IObserverAccount?observer)
29?????????{
30?????????????Observers.Remove(observer);
31?????????}
32?
33?????} ? 1?????public?interface?IObserverAccount
2?????{
3?????????void?Update(Subject?subject);
4?????} ? 1?????public?class?BankAccount?:?Subject
2?????{
3?????????public?BankAccount(double?money)
4?????????????:?base(money)
5?????????{?}
6?
7?????} ? ?1?????public?class?Emailer?:?IObserverAccount
?2?????{
?3?????????private?string?_emalier;??????
?4?????????public?Emailer(string?emailer?)
?5?????????{
?6?????????????this._emalier?=?emailer;???????????
?7?????????}
?8?????????public?void?Update(Subject?subject)
?9?????????{????????????
10?????????????Console.WriteLine("Notified?:?Emailer?is?{0},?You?withdraw??{1:C}?",?_emalier,?subject.Money);
11?????????}
12?????} ? ?1????public?class?Mobile?:?IObserverAccount
?2?????{
?3?????????private?long?_phoneNumber;????????
?4?????????public?Mobile(long?phoneNumber)
?5?????????{
?6?????????????this._phoneNumber?=?phoneNumber;????????????
?7?????????}
?8?????????public?void?Update(Subject?subject)
?9?????????{
10?????????????Console.WriteLine("Notified?:Phone?number?is?{0}?You?withdraw??{1:C}?",?_phoneNumber,?subject.Money);
11?????????}
12?????} 此時客戶端實現如下: ?1????class?Test
?2?????{
?3?????????static?void?Main(string[]?args)
?4?????????{
?5?????????????Subject?subject?=?new?BankAccount(2000);
?6?????????????subject.AddObserver(new?Emailer("abcdwxc@163.com"));
?7?????????????subject.AddObserver(new?Mobile(13901234567));
?8?
?9?????????????subject.WithDraw();
10?????????}
11?????}
推模式與拉模式
??? 對于發布-訂閱模型,大家都很容易能想到推模式與拉模式,用SQL Server做過數據庫復制的朋友對這一點很清楚。在Observer模式中同樣區分推模式和拉模式,我先簡單的解釋一下兩者的區別:推模式是當有消息時,把消息信息以參數的形式傳遞(推)給所有觀察者,而拉模式是當有消息時,通知消息的方法本身并不帶任何的參數,是由觀察者自己到主體對象那兒取回(拉)消息。知道了這一點,大家可能很容易發現上面我所舉的例子其實是一種推模式的Observer模式。我們先看看這種模式帶來了什么好處:當有消息時,所有的 觀察者都會直接得到全部的消息,并進行相應的處理程序,與主體對象沒什么關系,兩者之間的關系是一種松散耦合。但是它也有缺陷,第一是所有的觀察者得到的 消息是一樣的,也許有些信息對某個觀察者來說根本就用不上,也就是觀察者不能“按需所取”;第二,當通知消息的參數有變化時,所有的觀察者對象都要變化。鑒于以上問題,拉模式就應運而生了,它是由觀察者自己主動去取消息,需要什么信息,就可以取什么,不會像推模式那樣得到所有的消息參數。
拉模式實現如下: ?1?public?abstract?class?Subject
?2?????{
?3?????????private?List<IObserverAccount>?Observers?=?new?List<IObserverAccount>();
?4?
?5?
?6?????????private?double?_money;
?7?
?8?????????public?double?Money
?9?????????{
10?????????????get?{?return?_money;?}????????????
11?????????}
12?????????public?Subject(double?money)
13?????????{
14?????????????this._money?=?money;
15?????????}
16?????????public?void?WithDraw()
17?????????{
18?????????????foreach?(IObserverAccount?ob?in?Observers)
19?????????????{
20?????????????????ob.Update();
21?
22?????????????}
23?????????}
24?????????public?void?AddObserver(IObserverAccount?observer)
25?????????{
26?????????????Observers.Add(observer);
27?????????}
28?????????public?void?RemoverObserver(IObserverAccount?observer)
29?????????{
30?????????????Observers.Remove(observer);
31?????????}
32?
33?????} ? 1????public?interface?IObserverAccount
2?????{
3?????????void?Update();
4?????} ? 1?????public?class?BankAccount?:Subject
2?????{
3?????????public?BankAccount(double?money)
4?????????????:?base(money)
5?????????{?}
6????????
7?????} ? ?1?????public?class?Emailer?:?IObserverAccount
?2?????{
?3?????????private?string?_emalier;
?4?????????private?Subject?_subject;
?5?????????public?Emailer(string?emailer,Subject?subject)
?6?????????{
?7?????????????this._emalier?=?emailer;
?8?????????????this._subject?=?subject;
?9?????????}
10?????????public?void?Update()
11?????????{
12?????????????//..
13?????????????Console.WriteLine("Notified?:?Emailer?is?{0},?You?withdraw??{1:C}?",?_emalier,_subject.Money);
14?????????}
15?????} ? ?1?????public?class?Mobile?:?IObserverAccount
?2?????{
?3?????????private?long?_phoneNumber;
?4?????????private?Subject?_subject;
?5?????????public?Mobile(long?phoneNumber,Subject?subject)
?6?????????{
?7?????????????this._phoneNumber?=?phoneNumber;
?8?????????????this._subject?=?subject;
?9?????????}
10?????????public?void?Update()
11?????????{
12?????????????Console.WriteLine("Notified?:Phone?number?is?{0}?You?withdraw??{1:C}?",?_phoneNumber,_subject.Money);
13?????????}
14?????} 此時客戶端調用如下: ?1????class?Test
?2?????{
?3?????????static?void?Main(string[]?args)
?4?????????{
?5????????????Subject?subject=?new?BankAccount(2000);??????????
?6?????????????subject.AddObserver(new?Emailer("abcdwxc@163.com",subject));
?7?????????????subject.AddObserver(new?Mobile(13901234567,subject));
?8?
?9?????????????subject.WithDraw();
10?????????}
11?????} .NET中Observer實現:
????用事件和委托來實現Observer模式我認為更加的簡單和優雅,也是一種更好的解決方案。 ?1??public??class?Subject
?2?????{
?3?????????public?event?NotifyEventHandler?NotifyEvent;
?4?
?5?????????private?double?_money;
?6?????????public?Subject(double?money)
?7?????????{
?8?????????????this._money?=?money;
?9?????????}
10?
11?????????public?double?Money
12?????????{
13?????????????get?{?return?_money;?}
14?????????}
15?
16?????????public?void?WithDraw()
17?????????{
18?????????????OnNotifyChange();
19?????????}
20?????????public?void?OnNotifyChange()
21?????????{
22?????????????if?(NotifyEvent?!=?null)
23?????????????{
24?????????????????NotifyEvent(this);
25?????????????}
26?
27?????????}
28?
29?????} ? ?1?????public?class?Emailer
?2?????{
?3?????????private?string?_emalier;
?4?????????public?Emailer(string?emailer)
?5?????????{
?6?????????????this._emalier?=?emailer;
?7?????????}
?8?????????public?void?Update(object?obj)
?9?????????{
10?????????????if?(obj?is?Subject)
11?????????????{
12?????????????????Subject?subject?=?(Subject)obj;
13?
14?????????????????Console.WriteLine("Notified?:?Emailer?is?{0},?You?withdraw??{1:C}?",?_emalier,?subject.Money);
15?????????????}
16?????????}
17?} public?delegate?void?NotifyEventHandler(object?sender); 客戶端調用如下: ?1?????class?Test
?2?????????{
?3?????????????static?void?Main(string[]?args)
?4?????????????{
?5?????????????????Subject?subject?=?new?Subject(2000);
?6?????????????????Emailer?emailer?=?new?Emailer("abcdwxc@163.com");
?7?????????????????subject.NotifyEvent?+=?new?NotifyEventHandler(emailer.Update);
?8????????????
?9?
10?????????????????subject.WithDraw();
11?????????????}
12?????????}
Observer實現要點:
1.使用面向對象的抽象,Observer模式使得我們可以獨立地改變目標與觀察者,從而使二者之間的依賴關系達到松耦合。
2.目標發送通知時,無需指定觀察者,通知(可以攜帶通知信息作為參數)會自動傳播。觀察者自己決定是否需要訂閱通知。目標對象對此一無所知。
3.在C#中的Event。委托充當了抽象的Observer接口,而提供事件的對象充當了目標對象,委托是比抽象Observer接口更為松耦合的設計。 #9樓?2007-12-25 16:08?wycg_cnh20 拉模式和推模式的差別在概念上講得很清楚,但是,從代碼上沒看出推和拉的差別來/ 支持(0)?反對(0)??
?? ? #12樓?2009-04-17 14:30?soxyunyi 是否體現推拉,是由觀察者類的update方法參數決定的。 支持(0)?反對(0)
?? ? #13樓?2009-04-17 14:34?soxunyi public void update(Observable obs, Object arg)?
這是Java中Obersver接口方法,?
如果在方法體中通過obs來獲取主題的狀態變化,則是拉模式;?
如果在方法體中通過arg來獲取主題的狀態變化,則是推模式。?
因為,通過obs獲取是在觀察者類中實現的,故為拉。?
而arg則是由主題類傳遞給觀察者的,故為推。
總結
以上是生活随笔為你收集整理的34观察者模式(Observer Pattern)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如果人民币成为主要国际货币,会不会更值钱
- 下一篇: 电动车充电起火 5人死亡:判刑6年!