名稱 |
Observer
|
結構 |
|
意圖 |
定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時, 所有依賴于它的對象都得到通知并被自動更新。 |
適用性 |
- 當一個抽象模型有兩個方面, 其中一個方面依賴于另一方面。將這二者封裝在獨立的對象中以使它們可以各自獨立地改變和復用。
- 當對一個對象的改變需要同時改變其它對象, 而不知道具體有多少對象有待改變。
- 當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之, 你不希望這些對象是緊密耦合的。
|
|
|
namespace
?Observer_DesignPattern

{
????
using
?System;
????
using
?System.Collections;

????
class
?Subject

????
{
????????
private
?ArrayList?list?
=
?
new
?ArrayList();

????????
private
?
string
?strImportantSubjectData?
=
?
"
Initial
"
;
????????
????????
public
?
string
?ImportantSubjectData?

????????
{
????????????
get
?

????????????
{
????????????????
return
?strImportantSubjectData;
????????????}
????????????
set
?

????????????
{
????????????????strImportantSubjectData?
=
?value;
????????????}
????????}
????????
public
?
void
?Attach(Observer?o)

????????
{
????????????list.Add(o);
????????????o.ObservedSubject?
=
?
this
;
????????}
????????
public
?
void
?Detach(Observer?o)

????????
{
????????????
????????}
????????
public
?
void
?Notify()

????????
{
????????????
foreach
?(Observer?o?
in
?list)????????

????????????
{
????????????????o.Update();
????????????}
????????????
????????}
????}
????
class
?ConcreteSubject?:?Subject

????
{
????????
public
?
void
?GetState()

????????
{
????????????
????????}
????????
public
?
void
?SetState()

????????
{
????????????
????????}
????
????}
????
abstract
?
class
?Observer?

????
{
????????
protected
?Subject?s;
????????
public
?Subject?ObservedSubject?

????????
{
????????????
get
?

????????????
{
????????????????
return
?s;????????
????????????}
????????????
set
?

????????????
{
????????????????s?
=
?value;
????????????}
????????}
????
????????
abstract
?
public
?
void
?Update();
????}
????
class
?ConcreteObserver?:?Observer?

????
{
????????
private
?
string
?observerName;
????????
????????
public
?ConcreteObserver(
string
?name)

????????
{
????????????observerName?
=
?name;
????????}
????????
override
?
public
?
void
?Update()

????????
{
????????????Console.WriteLine(
"
In?Observer?{0}:?data?from?subject?=?{1}
"
,?
????????????????observerName,?s.ImportantSubjectData);
????????}
????
????}
????
/**/
///
?
<summary>
????
///
????Summary?description?for?Client.
????
///
?
</summary>
????
public
?
class
?Client

????
{?????
????????
public
?
static
?
int
?Main(
string
[]?args)

????????
{??????????
????????????
//
?Set?up?everything
????????????ConcreteSubject?s?
=
?
new
?ConcreteSubject();
????????????ConcreteObserver?o1?
=
?
new
?ConcreteObserver(
"
first?observer
"
);
????????????ConcreteObserver?o2?
=
?
new
?ConcreteObserver(
"
second?observer
"
);

????????????s.Attach(o1);
????????????s.Attach(o2);

????????????
//
?make?changes?to?subject
????????????s.?ImportantSubjectData?
=
?
"
This?is?important?subject?data
"
;

????????????
//
?Notify?all?observers
????????????s.Notify();????????????
????????????
return
?
0
;
????????}
????}
}
比較好理解的一個例子:
//?Observer?pattern?--?Real?World?example??


using?System;
using?System.Collections;

namespace?DoFactory.GangOfFour.Observer.RealWorld


{
??
??//?MainApp?test?application?

??class?MainApp

??
{
????static?void?Main()

????
{
??????//?Create?investors?
??????Investor?s?=?new?Investor("Sorros");
??????Investor?b?=?new?Investor("Berkshire");

??????//?Create?IBM?stock?and?attach?investors?
??????IBM?ibm?=?new?IBM("IBM",?120.00);
??????ibm.Attach(s);
??????ibm.Attach(b);

??????//?Change?price,?which?notifies?investors?
??????ibm.Price?=?120.10;
??????ibm.Price?=?121.00;
??????ibm.Price?=?120.50;
??????ibm.Price?=?120.75;

??????//?Wait?for?user?
??????Console.Read();
????}
??}

??//?"Subject"?

??abstract?class?Stock

??
{
????protected?string?symbol;
????protected?double?price;
????private?ArrayList?investors?=?new?ArrayList();

????//?Constructor?
????public?Stock(string?symbol,?double?price)

????
{
??????this.symbol?=?symbol;
??????this.price?=?price;
????}

????public?void?Attach(Investor?investor)

????
{
??????investors.Add(investor);
????}

????public?void?Detach(Investor?investor)

????
{
??????investors.Remove(investor);
????}

????public?void?Notify()

????
{
??????foreach?(Investor?investor?in?investors)

??????
{
????????investor.Update(this);
??????}
??????Console.WriteLine("");
????}

????//?Properties?
????public?double?Price

????
{

??????get
{?return?price;?}
??????set

??????
{
????????price?=?value;
????????Notify();?
??????}
????}

????public?string?Symbol

????
{

??????get
{?return?symbol;?}

??????set
{?symbol?=?value;?}
????}
??}

??//?"ConcreteSubject"?

??class?IBM?:?Stock

??
{
????//?Constructor?
????public?IBM(string?symbol,?double?price)
??????:?base(symbol,?price)

????
{
????}
??}

??//?"Observer"?

??interface?IInvestor

??
{
????void?Update(Stock?stock);
??}

??//?"ConcreteObserver"?

??class?Investor?:?IInvestor

??
{
????private?string?name;
????private?Stock?stock;

????//?Constructor?
????public?Investor(string?name)

????
{
??????this.name?=?name;
????}

????public?void?Update(Stock?stock)

????
{
??????Console.WriteLine("Notified?{0}?of?{1}'s?"?+
????????"change?to?{2:C}",?name,?stock.Symbol,?stock.Price);
????}

????//?Property?
????public?Stock?Stock

????
{

??????get
{?return?stock;?}

??????set
{?stock?=?value;?}
????}
??}
}?

一、?觀察者(Observer)模式
觀察者模式又叫做發布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。
觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。
一個軟件系統常常要求在某一個對象的狀態發生變化的時候,某些其它的對象做出相應的改變。做到這一點的設計方案有很多,但是為了使系統能夠易于復用,應該選擇低耦合度的設計方案。減少對象之間的耦合有利于系統的復用,但是同時設計師需要使這些低耦合度的對象之間能夠維持行動的協調一致,保證高度的協作(Collaboration)。觀察者模式是滿足這一要求的各種設計方案中最重要的一種。
四、?C#中的Delegate與Event
實際上在C#中實現Observer模式沒有這么辛苦,.NET中提供了Delegate與Event機制,我們可以利用這種機制簡化Observer模式。關于Delegate與Event的使用方法請參考相關文檔。改進后的Observer模式實現如下:
//
?Observer?pattern?--?Structural?example??
using
?System;

//
Delegate
delegate
?
void
?UpdateDelegate();?

//
Subject
class
?Subject

{
??
public
?
event
?UpdateDelegate?UpdateHandler;
??
??
//
?Methods
??
public
?
void
?Attach(?UpdateDelegate?ud?)

??
{
????UpdateHandler?
+=
?ud;
??}
??
public
?
void
?Detach(?UpdateDelegate?ud?)

??
{
????UpdateHandler?
-=
?ud;
??}
??
??
public
?
void
?Notify()

??
{
????
if
(UpdateHandler?
!=
?
null
)?UpdateHandler();
??}
}
//
ConcreteSubject
class
?ConcreteSubject?:?Subject

{
??
//
?Fields
??
private
?
string
?subjectState;

??
//
?Properties
??
public
?
string
?SubjectState

??
{

????
get
{?
return
?subjectState;?}
????
set
{?subjectState?
=
?value;?}
??}
}
//
?"ConcreteObserver"
class
?ConcreteObserver

{
??
//
?Fields
??
private
?
string
?name;
??
private
?
string
?observerState;
??
private
?ConcreteSubject?subject;

??
//
?Constructors
??
public
?ConcreteObserver(?ConcreteSubject?subject,??
????
string
?name?)

??
{
????
this
.subject?
=
?subject;
????
this
.name?
=
?name;
??}
??
//
?Methods
??
public
?
void
?Update()

??
{
????observerState?
=
?subject.SubjectState;
????Console.WriteLine(?
"
Observer?{0}'s?new?state?is?{1}
"
,
??????name,?observerState?);
??}
??
//
?Properties
??
public
?ConcreteSubject?Subject

??
{

????
get
?
{?
return
?subject;?}
????
set
?
{?subject?
=
?value;?}
??}
}
//
?"ConcreteObserver"
class
?AnotherObserver

{
??
//
?Methods
??
public
?
void
?Show()

??
{
????Console.WriteLine(
"
AnotherObserver?got?an?Notification!
"
);
??}
}
public
?
class
?Client

{?
??
public
?
static
?
void
?Main(
string
[]?args)

??
{?
????ConcreteSubject?s?
=
?
new
?ConcreteSubject();
????ConcreteObserver?o1?
=
?
new
?ConcreteObserver(s,?
"
1
"
);
????ConcreteObserver?o2?
=
?
new
?ConcreteObserver(s,?
"
2
"
);
????AnotherObserver?o3?
=
?
new
?AnotherObserver();
????
????s.Attach(
new
?UpdateDelegate(o1.Update));
????s.Attach(
new
?UpdateDelegate(o2.Update));
????s.Attach(
new
?UpdateDelegate(o3.Show));

????s.SubjectState?
=
?
"
ABC
"
;
????s.Notify();

????Console.WriteLine(
"
--------------------------
"
);
????s.Detach(
new
?UpdateDelegate(o1.Update));

????s.SubjectState?
=
?
"
DEF
"
;
????s.Notify();
??}
}
其中,關鍵的代碼如下:
delegate
?
void
?UpdateDelegate();?
定義一個Delegate,用來規范函數結構。不管是ConcreteObserver類的Update方法還是AnotherObserver類的Show方法都符合該Delegate。這不象用Observer接口來規范必須使用Update方法那么嚴格。只要符合Delegate所指定的方法結構的方法都可以在后面被事件所處理。
public
?
event
?UpdateDelegate?UpdateHandler;
定義一個事件,一旦觸發,可以調用一組符合UpdateDelegate規范的方法。
??
public
?
void
?Attach(?UpdateDelegate?ud?)

??
{
????UpdateHandler?
+=
?ud;
??}
訂閱事件。只要是一個滿足UpdateDelegate的方法,就可以進行訂閱操作(如下所示)。
????s.Attach(
new
?UpdateDelegate(o1.Update));
????s.Attach(
new
?UpdateDelegate(o2.Update));
????s.Attach(
new
?UpdateDelegate(o3.Show));
在Notify方法中:
??
public
?
void
?Notify()

??
{
????
if
(UpdateHandler?
!=
?
null
)?UpdateHandler();
??}
只要UpdateHandler != null(表示有訂閱者),就可以觸發事件(UpdateHandler()),所有的訂閱者便會接到通知。
六、?觀察者模式的優缺點
Observer模式的優點是實現了表示層和數據邏輯層的分離,并定義了穩定的更新消息傳遞機制,類別清晰,并抽象了更新接口,使得可以有各種各樣不同的表示層(觀察者)。
但是其缺點是每個外觀對象必須繼承這個抽像出來的接口類,這樣就造成了一些不方便,比如有一個別人寫的外觀對象,并沒有繼承該抽象類,或者接口不對,我們又希望不修改該類直接使用它。雖然可以再應用Adapter模式來一定程度上解決這個問題,但是會造成更加復雜煩瑣的設計,增加出錯幾率。
觀察者模式的效果有以下幾個優點:
(1)觀察者模式在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者角色所知道的只是一個具體現察者聚集,每一個具體現察者都符合一個抽象觀察者的接口。被觀察者并不認識任何一個具體觀察者,它只知道它們都有一個共同的接口。由于被觀察者和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。
(2)觀察者模式支持廣播通信。被觀察者會向所有的登記過的觀察者發出通知。
觀察者模式有下面的一些缺點:
(1)如果一個被觀察者對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
(2)如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰。在使用觀察考模式時要特別注意這一點。
(3)如果對觀察者的通知是通過另外的線程進行異步投遞的話,系統必須保證投遞是以自恰的方式進行的。
(4)雖然觀察者模式可以隨時使觀察者知道所觀察的對象發生了變化,但是觀察者模式沒有相應的機制使觀察者知道所觀察的對象是怎么發生變化的。