今天我们来讲一下观察者模式。还是老样子,给大家一个案例。
一、案例
在我们怀念的学生时代,我们会有这么一个现象,当在教室里上自习的时候,让一个同学把风,我们在教室里玩,当老师来的时候,让那个同学给我们说一声。
好,下面我们就用简单的控制台应用程序来实现上述的场景(一个把风的同学,两个玩耍的同学)
1 /// <summary>
2 /// 把风同学类
3 /// </summary>
4 class Secretary
5 {
6 //有几个同学请把风的帮忙,于是就给集合增加几个对象
7 private IList<Observer> obs = new List<Observer>();
8 private string _action;
9
10 public void Attach(Observer ob)
11 {
12 obs.Add(ob);
13 }
14
15 /// <summary>
16 /// 老师回来时,给所有登记的同学们发通知。
17 /// </summary>
18 public void Notify()
19 {
20 foreach (var ob in obs)
21 {
22 ob.Update();
23 }
24 }
25 /// <summary>
26 /// 状态
27 /// </summary>
28 public string SecretoryAction
29 {
30 get { return _action; }
31 set { _action = value; }
32 }
33 }
34
35 /// <summary>
36 /// 玩耍的同学类
37 /// </summary>
38 class Observer
39 {
40 private string name;
41 private Secretary sub;
42
43 public Observer(string name, Secretary sub)
44 {
45 this.name = name;
46 this.sub = sub;
47 }
48 /// <summary>
49 /// 得到把风的通知
50 /// </summary>
51 public void Update()
52 {
53 Console.WriteLine($"{sub.SecretoryAction}{name}别玩游戏了");
54 }
55 }
客户端:
1 public static void Main()
2 {
3 //把风的
4 Secretary s = new Secretary();
5 //小王同学
6 Observer ob1 = new Observer("小王", s);
7 //小李同学
8 Observer ob2 = new Observer("小李", s);
9 //把风的记下两位同学
10 s.Attach(ob1);
11 s.Attach(ob2);
12 //发现老师来了
13 s.SecretoryAction = "老师回来了!";
14 //通知两个同学
15 s.Notify();
16 Console.ReadKey();
17 }
嗯,上述代码我们发现了一个问题,就是 把风的这个同学的类 和 玩耍的同学的类,耦合性太强了。把风类需要增加玩耍的同学,同学类需要把风的通知。
这么强的耦合,如果我们需要添加一个同学进来,那两边都需要修改了,如果还要通知唱歌同学老师来了,咋办?这其实违背了设计的两个原则,开放-封闭原则,依赖倒转原则。
我们的程序都应该依赖于抽象,而不是相互依赖。
二、演绎
1、第一步演绎
好,下面我们来修改一下上述的代码。增加一个抽象的观察者类
1 /// <summary>
2 /// 抽象的观察者
3 /// </summary>
4 public abstract class Observer
5 {
6 protected string name;
7 protected Secretary sub;
8
9 public Observer(string name, Secretary sub)
10 {
11 this.name = name;
12 this.sub = sub;
13 }
14
15 public abstract void Update();
16 }
让具体的观察者继承这个抽象类
1 /// <summary>
2 /// 具体的观察者1(玩游戏的)
3 /// </summary>
4 public class Observer1 : Observer
5 {
6 public Observer1(string name, Secretary sub) : base(name, sub)
7 {
8 }
9
10 public override void Update()
11 {
12 Console.WriteLine($"{sub.SecretoryAction}{name},别唱歌了。");
13 }
14 }
15 /// <summary>
16 /// 具体的观察者2(唱歌的)
17 /// </summary>
18 public class Observer2 : Observer
19 {
20 public Observer2(string name, Secretary sub) : base(name, sub)
21 {
22 }
23
24 public override void Update()
25 {
26 Console.WriteLine($"{sub.SecretoryAction}{name},别玩游戏了。");
27 }
28 }
把风同学类的代码基本不变
1 /// <summary>
2 /// 把风同学类
3 /// </summary>
4 public class Secretary
5 {
6 //有几个同学请把风的帮忙,于是就给集合增加几个对象
7 private IList<Observer> obs = new List<Observer>();
8 private string _action;
9
10 public void Attach(Observer ob)
11 {
12 obs.Add(ob);
13 }
14
15 /// <summary>
16 /// 老师回来时,给所有登记的同学们发通知。
17 /// </summary>
18 public void Notify()
19 {
20 foreach (var ob in obs)
21 {
22 ob.Update();
23 }
24 }
25 /// <summary>
26 /// 状态
27 /// </summary>
28 public string SecretoryAction
29 {
30 get { return _action; }
31 set { _action = value; }
32 }
33 }
客户端调用
1 public static void Main()
2 {
3 //把风的
4 Secretary s = new Secretary();
5 //玩耍小王同学
6 Observer ob1 = new Observer1("小王", s);
7 //玩耍小李同学
8 Observer ob2 = new Observer1("小李", s);
9 //玩耍小李同学
10 Observer ob3 = new Observer2("小张", s);
11 //把风的记下三位同学
12 s.Attach(ob1);
13 s.Attach(ob2);
14 s.Attach(ob3);
15 //发现老师来了
16 s.SecretoryAction = "老师回来了!";
17 //通知三位同学
18 s.Notify();
19 Console.ReadKey();
20 }
2、第二步演绎
既然观察者我们抽象出一个抽象类来了,那么我们仔细观察一下,把风的同学这个类是一个具体的类,也可以抽象出来。还有,如果哪个同学跟把风的那个同学闹矛盾了,可以不给他通知了,嘿嘿。所以可以加一个删除方法。好了,看一下代码吧
1 /// <summary>
2 /// 把风类抽象出来的接口
3 /// </summary>
4 interface Subject
5 {
6 void Attach(Observer ob);
7 void Detach(Observer ob);
8
9 string SecretoryAction
10 {
11 get;
12 set;
13 }
14 }
具体的把风同学的类可继承这个接口
1 /// <summary>
2 /// 把风同学类
3 /// </summary>
4 public class Secretary : Subject
5 {
6 private IList<Observer> obs = new List<Observer>();
7 private string action;
8 public void Attach(Observer ob)
9 {
10 obs.Add(ob);
11 }
12
13 public void Detach(Observer ob)
14 {
15 obs.Remove(ob);
16 }
17
18 public void Notify()
19 {
20 foreach (var o in obs)
21 {
22 o.Update();
23 }
24 }
25 public string SecretoryAction
26 {
27 get { return action; }
28 set { action = value; }
29 }
30 }
客户端:
1 public static void Main()
2 {
3 //把风的
4 Secretary s = new Secretary();
5 //玩耍小王同学
6 Observer ob1 = new Observer1("小王", s);
7 //玩耍小李同学
8 Observer ob2 = new Observer1("小李", s);
9 //玩耍小李同学
10 Observer ob3 = new Observer2("小张", s);
11 //把风的记下三位同学
12 s.Attach(ob1);
13 s.Attach(ob2);
14 s.Attach(ob3);
15 //发现老师来了
16 s.SecretoryAction = "老师回来了!";
17 //通知三位同学
18 s.Notify();
19 Console.ReadKey();
20 }
ok,观察者模式就讲完了,下面我们来总结一下:
观察者模式:又叫 发布-订阅模式,他定义了一种一对多的依赖关系,让多个观察者对象共同监听同一个主题对象,当主题对象状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
观察者模式所做的事情就是解耦,让耦合的双方都依赖于抽象,而不依赖于具体,从而使得自己的变化不会影响到另一边。