简介
观察者模式是基于事件(event)或者委托(delegate)的。比如winform的控件事件OnXXXX,其实就是一种观察者模式。简单地说,观察者通过观察被观察者的行为而执行对应的动作。下面来进行一个假设吧。
假如你是个员工
我们用代码来模拟一个这样的场景。假如你是一个员工,同事们都跟你一样在偷懒,突然,老板回来了,这个时候,大家都要做回老板在的时候该做的事情(虽然老板不在也应该这样做),比如敲代码啊、写文案啊、做设计之类的咯(这里有程序员Jack、秘书小秘和文案小文)。下面我们来看一下模拟这个场景的极度耦合的代码。
public abstract class Staff
{
// 员工名
protected string name;
public Staff(string name)
{
this.name = name;
}
// 老板回来后做的事情
public abstract void HandleBossComming();
}
// 老板的秘书
public class Secretary : Staff
{
public Secretary(string name) : base(name) { }
public override void HandleBossComming()
{
Console.WriteLine($"{name}正在给老板倒杯卡布奇诺");
}
}
// 程序员
public class Programmer : Staff
{
public Programmer(string name) : base(name) { }
public override void HandleBossComming()
{
Console.WriteLine($"{name}假装在敲代码");
}
}
// 文员
public class Clerk : Staff
{
public Clerk(string name) : base(name) { }
public override void HandleBossComming()
{
Console.WriteLine($"{name}停止玩手机");
}
}
好了,员工都定义好了,也都实现了各自应对老板回来的策略。接下来,就是老板要回来了。
public class Boss
{
public void Comming()
{
Programmer programmer = new Programmer("Jack");
Secretary secretary = new Secretary("小秘");
Clerk clerk = new Clerk("小文");
// 员工们各自的反应
programmer.HandlerBossComming();
secretary.HandlerBossComming();
clerk.HandlerBossComming();
}
}
class Program
{
static void Main(string[] args)
{
Boss boss = new Boss();
boss.Comming();
}
}
调用boss.Comming()方法后,在控制台就可以看到员工各自应对老板回来的策略。
Jack假装在敲代码
小秘正在给老板倒杯卡布奇诺
小文停止玩手机
功能实现得很完美啊,但是,之前也说了,这是极度耦合的一种实现方式,Boss类和Staff类耦合在一起了(纠缠在一起了),Boss类依赖了Staff类了(在Comming方法里面去实例化Staff,并调用他们的HandleBossComming方法),假如,公司又来了一个员工(假设他也会偷懒),那这里的代码就需要添加一个员工了。要解决这个问题,轮到观察者模式出现了。这里的观察者就是员工,被观察者就是老板啦(貌似正常应该是老板观察着员工吧,哈哈哈哈,偷懒的员工才需要观察老板)。
我们先定义对应的事件和委托来修改一下Boss类。
public class Boss
{
// 老板来了对应事件的委托类型
public delegate void EvenHandler();
// 老板来了的事件
public event EvenHandler OnComming;
public void Comming()
{
// 发布老板来了这个事件
OnComming?.Invoke();
}
}
好了,现在老板回来这个事件已经定义好了,员工们(员工类)是不需要修改的,接下来要修改的部分,只是增加员工去观察(订阅)老板回来这个事件了。
class Program
{
static void Main(string[] args)
{
Boss boss = new Boss();
Programmer programmer = new Programmer("Jack");
Secretary secretary = new Secretary("小秘");
Clerk clerk = new Clerk("小文");
// 员工们订阅老板回来这个事件
boss.OnComming += programmer.HandleBossComming;
boss.OnComming += secretary.HandleBossComming;
boss.OnComming += clerk.HandleBossComming;
// 老板回来了!
boss.Comming();
}
}
现在运行的结果和之前的是一样的啦。
Jack假装在敲代码
小秘正在给老板倒杯卡布奇诺
小文停止玩手机
现在的代码就解耦了,Boss类中的Comming方法里面只需要处理Boss自己的事情,不需要再去管Staff类的事情了。现在不管有多少个员工,只有有需要的才会去订阅老板回来这个事件。
现在呢,Jack和小文说,我们好像没有必要三个人都去观察着老板,要不,小秘,老板回来了由你来通知我们(同仇敌忾)?小秘说,好吧,毕竟我是最贴近老板的人。
那现在要对小秘也加个事件,让Jack和小文来订阅。
public class Secretary : Staff
{
public delegate void BossComeHandler();
public event BossComeHandler BossIsComming;
public Secretary(string name) : base(name) { }
public override void HandleBossComming()
{
Console.WriteLine($"{name}正在给老板倒杯卡布奇诺");
BossIsComming();
}
}
现在Jack和小文只要等小秘告诉他们就好了
class Program
{
static void Main(string[] args)
{
Boss boss = new Boss();
Programmer programmer = new Programmer("Jack");
Secretary secretary = new Secretary("小秘");
Clerk clerk = new Clerk("小文");
// 员工们订阅老板回来这个事件
boss.OnComming += secretary.HandleBossComming;
// Jack和小文订阅小秘告知老板来了的事件
secretary.BossIsComming += programmer.HandleBossComming;
secretary.BossIsComming += clerk.HandleBossComming;
// 老板回来了!
boss.Comming();
}
}
通过小秘来告诉Jack和小文的这种形式有什么好处呢?处理老板来了,在这里只是让小秘发布一个BossComeHandler事件,但实际小秘还可以定义和发布多个事件(定义不同的委托事件),通过小秘,可以减少Boss类上面的事件定义,让Boss只负责发布回来了的这个消息。
总结
观察者模式通过委托事件,让类通过事件的发布和订阅的方式来实现方法的调用,这样既降低了类之间的耦合,也可以使代码更容易扩展。