生活中,当某件事发生时,应该通知所有的相关者。例如,上课地点有变,要通知所有学习这门课的同学。
在软件设计中,当一个对象的状态发生变化是,如何通知与它相关的所有对象,就是我们今天要谈到的观察者模式。
观察者模式
概述
定义了一种一对多的依赖关系。让多个观察者对象同事监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有的观察者对象,使它们能够自动更新自己。
实际上,观察者模式所做的工作就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
结构图
示例
例如《大话设计模式》中谈到的前台得知看到老板回来,把消息发给 看股票行情的员工,员工更新自己的状态。
弊端:首先,只有前台发消息。员工对前台的依赖太强。因为员工需要前台的状态。如果正好前台不在,那么员工就不能得到最新消息。其次,前台对员工的依赖太强。 如果又有新的员工看电视剧,那么前台就要更改。
解耦实践一:增加抽象观察者抽象类。让两个观察者去继承”抽象观察者”,对于 Update的方法做重写操作。前台类就可以把与‘具体观察者’耦合的地方改成“抽象观察者”。
解耦实践二:增加抽象通知者接口)。让通知者去实现“抽象通知者”,这个接口。具体观察者类就可以把与‘前台’耦合的地方改成针对抽象通知者。
注: 解耦的过程中,增加抽象类或抽象接口都是可以的。他们的区别在于,抽象类可以共用一些代码,而用接口只是方法上的实现。
来看代码:
class Program
{
//客户端
static void Main(string[] args)
{
Boss huhansan = new Boss();
//看股票的同事
StockObserver tongshi1=new StockObserver("委管",huhansan );
huhansan.Attach(tongshi1);
huhansan.Attach(tongshi2);
huhansan.Detach(tongshi1);
//老板回来
huhansan.SubjectState = "我胡汉三回来了";
huhansan.Notify();
}
//通知者接口
interface Subject
{
void Attach(Observer observer);
void Detach(Observer observer);
void Notify();
string SubjectState
{
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 SubjectState
{
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.SubjectState, name);
}
}
}
}
适用情况
当一个对象的改变需要同时改变其他对象的时候,而且它不知道有多少有待改变时,应该考虑适用观察者模式。
优势
将一个系统分割成一系列相互协作的类在维护的时候,既能维持一致性,又能解除耦合,便于维护扩展和重用。
不足
一方面虽然解除了具体类之间的耦合,但是对抽象类或接口还是耦合。也就是如果没有抽像的类,相关功能就无法完成。另一方面,当得到某消息,其他相关类可能会存在不同的变化。因此调用的方法不一。
针对以上两个不足,又进行改进。
实践三
委托事件
方法是,去掉父类。声明一个委托。无参数无返回值。并在老班和前台类中 声明一事件Update,类型为委托。然后在访问通知方法时,调用‘更新’ 最后,更改客户端,将不同类的不同方法委托给‘老板’的‘更新’。
来看几个关键代码
Delegate void EventHandler();
Class Boss:Subject
{
Public event EventHandler Update;//声明一“EventHandler(事件处理程序)”的委托事件,名称叫“Update(更新)”
Public void Notify()
{
Update();//在访问通知方法时调用更新
}
再有 客户端
客户端/*将“看股票者”的“关闭股票程序 ”和看NBA的关闭直播方法挂钩到“;老板更新”上,也就是将两不同类的方法委托给“老板”类的“更新”了*/
Huhansan.Update+=new EventHandLer(tongshi1.CloseStockMarket);
Huhansan.Update+=new EventHandLer(tongshi2.CloseNBADirectSeeding);
委托模式
概述
委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。
适用
委托对象所搭载的所有方法必须具有相同的原型和形式,也就是拥有相同的参数列表和返回值类型。
优点
一个委托可以搭载多个方法,所有方法被依次唤起。可以使得委托对象所搭载的方法并不需要属于同一个了。
总结
观察者模式就是在解除耦合。事件委托可以实现对一个事件调用多个方法。