GoF
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知,并获得更新。
别名:依赖(Dependents),发布—订阅(Publish—Subscribe)。
动机:将一个系统分割成一系列相互协作的类有一个常见的副作用:需要维护相关对象之间的一致性。我们不希望为了维护一致性导致各类紧密耦合,因为这样降低了他们的可用性。
例如:许多图形用户界面工具将用户应用的界面和与底下的应用数据分离。
表格对象和柱状图对象并不知道相互的存在,这样可以根据需要单独复用表格和柱状图。这一行为意味着表格对象和柱状图对象都依赖于数据对象,因此数据对象的任何状态改变都应立即通知他们。
Observer模式描述了如何建立这种关系。这一模式的关键对象为目标(subject)和观察者(Observer),一个目标可以有任意多个依赖它的观察者。一旦目标状态发生改变,所有的观察者都得到通知。作为这个通知的响应,所有的观察者都将查询目标,以使其状态与目标的状态同步。
这种交互也称为发布—订阅(publish—subscribe)。目标是通知的发布者。它发布通知时并不需知道谁是它的观察者。可以有任意数目的观察者订阅并接收通知。
适用性:
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这二者封装在独立的对象中,以使他们可以独立的改变和复用。
- 当对一个对象改变时,需要同时改变其他对象,而不知道具体有多少对象有待改变。
- 当一个对象必须通知其他对象,而又不能假定其他对象是谁,换言之,你不希望这些对象是紧密耦合的
结构:
参与者:
Subject(目标)
——目标知道它的观察者。可以有任意多个观察者观察同一目标;
——提供注册和删除观察者对象的接口。
Observer(观察者)
——为哪些在目标改变时须获得通知的对象定义一个更新接口。
ConcreteSubject(具体目标)
——将有关状态存入各ConcreteSubject对象。
——当它的状态发生改变时,它向各个观察者发出通知。
ConcreteObserver(具体观察者)
——维护一个指向ConcreteSubject对象的引用;
——存储有关状态,这些状态应与目标状态保持一致;
——实现Observer的更新接口,使自身状态与目标状态保持一致。
代码实现:
抽象目标:
public abstract class Subject
{
private IList<Observer> _observers = new List<Observer>();
//增加观察者
public void Attach(Observer observer)
{
_observers.Add(observer);
}
//移除观察者
public void Detach(Observer observer)
{
_observers.Remove(observer);
}
//通知
public void Notify()
{
foreach (Observer observer in _observers)
{
observer.Updata();
}
}
}
抽象观察者:
public abstract class Observer
{
public abstract void Updata();
}
具体目标:
public class ConcreteSubject:Subject
{
private string _subjectState;
public string SubjectState
{
get { return _subjectState; }
set { _subjectState = value; }
}
}
具体观察者:
public class ConcreteObserver:Observer
{
private string _name;
private string _observerState;
private ConcreteSubject _subject;
public ConcreteObserver(ConcreteSubject subject,string name)
{
this._subject = subject;
this._name = name;
}
public override void Updata()
{
_observerState = _subject.SubjectState;
Console.WriteLine("观察者{0}的新状态是{1}", _name, _observerState);
}
public ConcreteSubject Subject
{
get { return this._subject; }
set { this._subject = value; }
}
}
客户端:
static void Main(string[] args)
{
ConcreteSubject subject = new ConcreteSubject();
subject.Attach(new ConcreteObserver(subject, "observer1"));
subject.Attach(new ConcreteObserver(subject, "observer2"));
subject.Attach(new ConcreteObserver(subject, "observer3"));
subject.SubjectState = "ABC";
subject.Notify();
Console.ReadKey();
}
结果:
我们也可以使用抽象观察者接口实现,接口如下:
public interface IObserver
{
void Updata();
}
但是,抽象目标还是依赖抽象观察者的,并且所有的观察者都必须继承抽象观察者接口,如果没有抽象观察者这样的抽象类或接口,通知功能就不能完成。
目标和观察者之间还是存在相互引用,要降低观察者和目标的耦合。
事件的委托和实现
一个委托可以搭载多个方法,所有方法依次被唤醒,更重要的是,使委托对象所搭载的方法并不属于同一个类。
本来在目标和观察者之间的关联关系现在转移到了客户端让委托搭载多个方法,这样解决了本来与抽象观察者的耦合问题。使解决方案更加优雅。
先有观察者模式,后有委托。