观察者模式也叫订阅发布模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
场景描述
员工上班期间,很多同事会趁老板不在偷偷看股票,但是又害怕老板突然回来出现在身后,而被逮个正着。于是乎。公司同事开始和前台小美眉不断献殷勤,希望前台能在老板会公司的时候打个电话到部门办公室,这样大家就都知道老板回来了,可以马上把股票界面关掉。
其实像上面这个场景就是一个典型的观察者模式案例。我们先把它写成程序。
前台就相当于观察者模式当中的主题,它负责给所有观察者也就是公司同事发送老板回来了的消息。
代码实现
首先定义一个主题接口
//主题接口
interface ISubject
{
//增加观察者,公司同事
void Attach(Observer observer);
//移除观察者
void Detach(Observer observer);
//消息(如果老板回来了,马上向所有观察者也就是公司同事发送消息)
void Notify();
//发布状态(老板是否回来的状态)
string SubjectState
{
get;
set;
}
}
然后定义一个具体的主题,这里就是前台,让它继承于主题接口
//具体主题
class Secretary : ISubject
{
//观察者列表
private IList<Observer> observers = new List<Observer>();
private string action;
public void Attach(Observer observer)
{
observers.Add(observer);
}
public void Detach(Observer observer)
{
observers.Remove(observer);
}
//遍历所有观察者,给每一个观察者发送消息
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
接着定义抽象观察者
//抽象观察者
abstract class Observer
{
//同事姓名
protected string name;
//观察的主题
protected ISubject sub;
public Observer(string name, ISubject sub)
{
this.name = name;
this.sub = sub;
}
//收到消息后的更新行为
public abstract void Update();
}
这里之所以要定义抽象观察者,是为了提高程序的扩展性,因为不一定所有同事都看股票,比如还有同事看NBA的,那只要让他们都继承与这个抽象类就好了
//看股票的同事类
class StockObserver : Observer
{
public StockObserver(string name, ISubject sub) :base(name, sub )
{
}
public override void Update()
{
Console.WriteLine("{0},{1},closeStock",sub.SubjectState,name);
}
}
//看NBA的同事类
class NBAObserver : Observer
{
public NBAObserver(string name, ISubject sub)
: base(name, sub)
{
}
public override void Update()
{
Console.WriteLine("{0},{1},closeNBA", sub.SubjectState, name);
}
}
客户端
static void Main(string[] args)
{
Secretary sec = new Secretary();
sec.Attach(new StockObserver("allen",sec));
sec.Attach(new NBAObserver("kobe", sec));
sec.SubjectState = "boss is back";
sec.Notify();
}
类图
观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
用委托实现观察者模式
在具体应用中,不大会出现所有的观察者都是具备同一行为(关闭股票或者NBA界面),可能不同的观察者会有完全不同的行为,所以这个时候我们就会通过委托来实现观察者模式
修改前台主题类
//定义一个委托
delegate void EventHandler();
class Secretary : ISubject
{
//声明一个事件,类型为委托EventHandler
public event EventHandler Update;
private string action;
public void Notify()
{
Update();
}
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
股票同事类中,可以用自己定义的行为
class StockObserver : Observer
{
public StockObserver(string name, ISubject sub) :base(name, sub )
{
}
//自己定义的方法
public void CloseStockMarket()
{
Console.WriteLine("{0},{1},closeStock",sub.SubjectState,name);
}
}
NBA同事类也一样
class NBAObserver : Observer
{
public NBAObserver(string name, ISubject sub)
: base(name, sub)
{
}
public void CloseNBADirectSeeding()
{
Console.WriteLine("{0},{1},closeNBA", sub.SubjectState, name);
}
}
客户端
static void Main(string[] args)
{
Secretary sec = new Secretary();
StockObserver stock = new StockObserver("allen",sec);
NBAObserver nba = new NBAObserver("kobe",sec);
sec.SubjectState = "back";
//将各自的行为添加到事件中
sec.Update += new EventHandler(stock.CloseStockMarket);
sec.Update += new EventHandler(nba.CloseNBADirectSeeding);
sec.Notify();
}
观察者模式的应用场景:
1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
观察者模式的优点:
1、 Subject和Observer之间是松偶合的,分别可以各自独立改变。
2、 Subject在发送广播通知的时候,无须指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
本文根据《大话设计模式》整理得出