名称 | Observer |
结构 | |
意图 | 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。 |
适用性 |
- 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
|
|
|
比较好理解的一个例子:
//
Observer pattern -- Real World example
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/None.gif)
using
System;
using
System.Collections;
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/None.gif)
namespace
DoFactory.GangOfFour.Observer.RealWorld
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
// MainApp test application
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
class MainApp
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
static void Main()
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// Create investors
Investor s = new Investor("Sorros");
Investor b = new Investor("Berkshire");
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// Create IBM stock and attach investors
IBM ibm = new IBM("IBM", 120.00);
ibm.Attach(s);
ibm.Attach(b);
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// Change price, which notifies investors
ibm.Price = 120.10;
ibm.Price = 121.00;
ibm.Price = 120.50;
ibm.Price = 120.75;
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// Wait for user
Console.Read();
}
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// "Subject"
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
abstract class Stock
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
protected string symbol;
protected double price;
private ArrayList investors = new ArrayList();
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// Constructor
public Stock(string symbol, double price)
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this.symbol = symbol;
this.price = price;
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
public void Attach(Investor investor)
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
investors.Add(investor);
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
public void Detach(Investor investor)
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
investors.Remove(investor);
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
public void Notify()
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
foreach (Investor investor in investors)
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
investor.Update(this);
}
Console.WriteLine("");
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// Properties
public double Price
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return price; }
set
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
price = value;
Notify();
}
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
public string Symbol
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return symbol; }
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ symbol = value; }
}
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// "ConcreteSubject"
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
class IBM : Stock
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// Constructor
public IBM(string symbol, double price)
: base(symbol, price)
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// "Observer"
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
interface IInvestor
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
void Update(Stock stock);
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// "ConcreteObserver"
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
class Investor : IInvestor
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
private string name;
private Stock stock;
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// Constructor
public Investor(string name)
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this.name = name;
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
public void Update(Stock stock)
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Console.WriteLine("Notified {0} of {1}'s " +
"change to {2:C}", name, stock.Symbol, stock.Price);
}
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
// Property
public Stock Stock
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return stock; }
![](http://top.oa.com/apis/imgcache.php/http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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;
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/None.gif)
//
Delegate
delegate
void
UpdateDelegate();
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/None.gif)
//
Subject
class
Subject
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
public event UpdateDelegate UpdateHandler;
// Methods
public void Attach( UpdateDelegate ud )
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
UpdateHandler += ud;
}
public void Detach( UpdateDelegate ud )
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
UpdateHandler -= ud;
}
public void Notify()
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (UpdateHandler != null ) UpdateHandler();
}
}
//
ConcreteSubject
class
ConcreteSubject : Subject
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
// Fields
private string subjectState;
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// Properties
public string SubjectState
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return subjectState; }
set
{ subjectState = value; }
}
}
//
"ConcreteObserver"
class
ConcreteObserver
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
// Fields
private string name;
private string observerState;
private ConcreteSubject subject;
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// Constructors
public ConcreteObserver( ConcreteSubject subject,
string name )
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this .subject = subject;
this .name = name;
}
// Methods
public void Update()
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
observerState = subject.SubjectState;
Console.WriteLine( " Observer {0}'s new state is {1} " ,
name, observerState );
}
// Properties
public ConcreteSubject Subject
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return subject; }
set
{ subject = value; }
}
}
//
"ConcreteObserver"
class
AnotherObserver
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
// Methods
public void Show()
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Console.WriteLine( " AnotherObserver got an Notification! " );
}
}
public
class
Client
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
public static void Main( string [] args)
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
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));
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
s.SubjectState = " ABC " ;
s.Notify();
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Console.WriteLine( " -------------------------- " );
s.Detach( new UpdateDelegate(o1.Update));
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
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 )
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
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()
![](http://top.oa.com/apis/imgcache.php/http://zhenyulu.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
if (UpdateHandler != null ) UpdateHandler();
}
只要UpdateHandler != null(表示有订阅者),就可以触发事件(UpdateHandler()),所有的订阅者便会接到通知。
六、 观察者模式的优缺点
Observer模式的优点是实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,类别清晰,并抽象了更新接口,使得可以有各种各样不同的表示层(观察者)。
但是其缺点是每个外观对象必须继承这个抽像出来的接口类,这样就造成了一些不方便,比如有一个别人写的外观对象,并没有继承该抽象类,或者接口不对,我们又希望不修改该类直接使用它。虽然可以再应用Adapter模式来一定程度上解决这个问题,但是会造成更加复杂烦琐的设计,增加出错几率。
观察者模式的效果有以下几个优点:
(1)观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体现察者聚集,每一个具体现察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
(2)观察者模式支持广播通信。被观察者会向所有的登记过的观察者发出通知。
观察者模式有下面的一些缺点:
(1)如果一个被观察者对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
(2)如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察考模式时要特别注意这一点。
(3)如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
(4)虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。