学习这个模式的时候,我不由自主的想起了高中数学老师,在办公室看NBA被当场抓获的情形.......
通过例子来学习一下。最近项目计划排的紧,是比较忙的,而最近的股市又特别的火,所以很多人偷偷的通过网页看行情。老板时常会出门办事,于是大家就可以放松一些,看看行情,几个人聊聊买卖股票的心得什么的,但是一不小心,老板就会回来,让老板看到工作当中做这些总是不太好,怎么办呢?
公司的有前台秘书,因为平时同事们买个饮料或零食什么的,都拿一份孝敬她。所以同事就请她帮忙,如果老板出门后回来,就一定要打嗝电话进来,大家也就马上各就各位,这样就不会被老板发现问题了。
我们就用这个例子写成程序。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Observer
{
//通知者接口
interface Subject
{
void Attach(Observer observer);
void Detach(Observer observer);
void Notify();
string SubjectAction
{
get;
set;
}
}
//具体的通知者类可能是前台,可能是老板,它们也许有各自的一些方法,但对于通知者来说,它们是
//一样的。所以它们都去实现这个接口。
class Boss : Subject
{
//同事列表
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 SubjectAction
{
get
{
return action;
}
set
{
action = value;
}
}
}
//抽象的观察者
abstract class Observer
{
protected string name;
protected Subject sub;
public Observer(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
public abstract void Update();
}
//增加了两个具体观察者
//看股票的同事
class StockObserver : Observer
{
public StockObserver(string name, Subject sub)
: base(name, sub)
{
}
public override void Update()
{
Console.WriteLine("{0} {1} 关闭股票行情,继续工作", sub.SubjectAction, name);
}
}
//看NBA的同事
class NBAObserver : Observer
{
public NBAObserver(string name, Subject sub)
: base(name, sub)
{
}
public override void Update()
{
Console.WriteLine("{0} {1}关闭NBA直播,继续工作!", sub.SubjectAction, name);
}
}
class Program
{
static void Main(string[] args)
{
Boss huhansan = new Boss();
StockObserver tongshi1 = new StockObserver("未观察", huhansan);
NBAObserver tongshi2 = new NBAObserver("易冠查", huhansan);
huhansan.Attach(tongshi1);
huhansan.Attach(tongshi2);
//未观察没有被老板通知到,所以减去。
huhansan.Detach(tongshi1);
huhansan.SubjectAction = "我胡汉三回来了!";
huhansan.Notify();
Console.Read();
}
}
}
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有观察者对象,使它们能够自动更新自己。
什么时候用观察者模式呢?将一个系统分割成一系列相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护,扩展和重用都带来不便。
当一个对象的改变需要同时改变其他对象的时候,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。
一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立得改变和复用。
观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于对象,而不是依赖于具体,从而使得各自的变化都不会影响另一边的变化。
但是观察者模式也是有不足的。
现实编程中,具体的观察者完全有可能是风马牛不相及的类,但它们都需要根据通知者的通知来做出Update()的操作,所以必须都实现Observer接口。但是有些是已经封装好的控件,不能实现这个接口,而且不一定是Update,方法名应该是不一样的。
那么怎么做呢?可以用委托。
运行结果:
看股票观察者类和看NBA观察者类,去掉了抽象观察类,所以补上一些代码,并将更新方法名改为各自适合的方法名。
抽象通知者由于不希望依赖抽象观察者,所以增加和减少的方法也就没有必要了,抽象观察者已经不存在了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Observer
{
delegate void EventHandler();
//通知者接口
interface Subject
{
void Notify();
string SubjectAction
{
get;
set;
}
}
//具体的通知者类可能是前台,可能是老板,它们也许有各自的一些方法,但对于通知者来说,它们是
//一样的。所以它们都去实现这个接口。
class Boss : Subject
{
//声明EventHandler的委托事件,名称叫Update(更新)
public event EventHandler Update;
private string action;
//通知
public void Notify()
{
Update();
}
//老板状态
public string SubjectAction
{
get
{
return action;
}
set
{
action = value;
}
}
}
//看股票的同事
class StockObserver
{
string name;
Subject sub;
public StockObserver(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
public void CloseStockMarket()
{
Console.WriteLine("{0} {1} 关闭股票行情,继续工作", sub.SubjectAction, name);
}
}
//看NBA的同事
class NBAObserver
{
string name;
Subject sub;
public NBAObserver(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
public void CloseNBADirectSeeding()
{
Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SubjectAction, name);
}
}
class Program
{
static void Main(string[] args)
{
Boss huhansan = new Boss();
StockObserver tongshi1 = new StockObserver("未观察", huhansan);
NBAObserver tongshi2 = new NBAObserver("易冠查", huhansan);
//看股票者的关闭股票程序方法和看NBA者的关闭NBA直播方法挂钩到老板的更新上,也就是将两不同类
//的不同方法委托给老板类的更新了。
huhansan.Update += new EventHandler(tongshi1.CloseStockMarket);
huhansan.Update += new EventHandler(tongshi2.CloseNBADirectSeeding);
huhansan.SubjectAction = "我胡汉三回来了!";
huhansan.Notify();
Console.Read();
}
}
}
委托就是引用方法的类型,一旦为委托分配了方法,委托将与方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看做对函数的抽象,是函数的类,委托的实例将代表一个具体的函数。
一个委托可以搭载多个方法,所有方法被依次唤起,可以使得委托对象搭载的方法并不需要属于一个类。
委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。