前言
发布-订阅者模式,可以使代码解耦、易于扩展。这个模式要用委托和事件。
什么时候应该使用 发布-订阅者模式 呢?场景如下:
草原上有 A、B 两只动物,有一只饥肠辘辘的狮子来到了草原上。如果狮子发现了 A,那 A 就要赶紧跑,B 可以继续猫着;如果狮子发现了 B,那 B 就要跑路,A 可以猫着;如果狮子把 A、B 都看到了,那 A、B 都得跑,谁知道狮子中意追哪个呢;如果狮子谁都没看见,那 A、B 都猫着。
你会发现这个简单的例子用个 if…else 语句就搞定了:
if(狮子发现了A) { A跑路; } else if(狮子发现了B) { B跑路; } else if(狮子发现了A、B) { A、B跑路; } else(狮子谁都没看见) { A、B猫着; }
但是,我们再想想,如果还有其他动物,那情况是不是就复杂了,比如狮子可能发现了C,也可能发现了A、C,每多一个动物那这段代码就要重新讨论狮子的眼光,效率低,扩展性可以说是 0;
使用发布-订阅者模式可以很好的解决这个问题。
我们定义三个类,分别表示 狮子、A、B。
public class 狮子 { } public class A { } public class B { }
狮子的类中有 “发现动物” 这个方法,此方法带有一个形参,用于传递发现了哪些动物。
public class 狮子 { public void Find(string animals) { Console.WriteLine("狮子发现了{0}",animals); } }
A、B 类中各有一个跑路方法。
public class A { public void Run() { Console.WriteLine("我是 A,被狮子发现了,我要跑了"); } } public class B { public void Run() { Console.WriteLine("我是 B,被狮子发现了,我要跑了"); } }
好,现在三个独立的类就构造完了,那怎么让 A、B 获取狮子的状态呢?
我们在命名空间下、三个类的外面,也就是公共代码部分,声明一个带形参的委托 FindEventHandler,命名规则是在准备传递的方法名后加上 EventHandler,这是标准命名法,便于代码阅读。
public delegate void FindEventHandler(string animals);
然后在狮子类中,定义一个事件,并且调用这个事件:
// 基于上面的委托定义事件 public event FindEventHandler FindEvent;
public void Find(string animals) { Console.WriteLine("狮子发现了{0}",animals); if(FindEvent != null) { FindEvent(animals); } }
做完上面两个步骤,只要 A、B 订阅了该事件,就能自动执行,就知道狮子到底发现了谁。订阅方法如下,B与之类似:
public class A { public A(狮子 小狮子) { 小狮子.FindEvent += new FindEventHandler(a_FindEvent);//订阅发现事件 } void a_FindEvent(string animals) { if(animals.Contains("A")) { Run(); } } public void Run() { Console.WriteLine("我是 A,被狮子发现了,我要跑了"); } }
完整代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 发布_订阅者模式 { class Program { static void Main(string[] args) { 狮子 狮子 = new 狮子(); A a = new A(狮子); B b = new B(狮子); 狮子.Find("A"); 狮子.Find("B"); 狮子.Find("AB"); Console.ReadLine(); // 由于A和B订阅了狮子的事件,所以无需任何代码,A和B均会按照约定进行动作。 } } public delegate void FindEventHandler(string animals); public class 狮子 { public event FindEventHandler FindEvent; public void Find(string animals) { Console.WriteLine("狮子发现了{0}", animals); if (FindEvent != null) { FindEvent(animals); } } } public class A { public A(狮子 狮子) { 狮子.FindEvent += new FindEventHandler(a_FindEvent);//订阅发现事件 } void a_FindEvent(string animals) { if(animals.Contains("A")) { Run(); } } public void Run() { Console.WriteLine("我是 A,被狮子发现了,我要跑了"); } } public class B { public B(狮子 狮子) { 狮子.FindEvent += new FindEventHandler(b_FindEvent);//订阅发现事件 } void b_FindEvent(string animals) { if (animals.Contains("B")) { Run(); } } public void Run() { Console.WriteLine("我是 B,被狮子发现了,我要跑了"); } } }
上面这个例子是我根据以前看到的例子编的,理解了这个例子,也就理解了委托和事件。关于委托和事件,我在另一篇文章里再细说,传送门:C# 委托和事件