今天我们来讲一下观察者模式。还是老样子,给大家一个案例。

一、案例

在我们怀念的学生时代,我们会有这么一个现象,当在教室里上自习的时候,让一个同学把风,我们在教室里玩,当老师来的时候,让那个同学给我们说一声。

好,下面我们就用简单的控制台应用程序来实现上述的场景(一个把风的同学,两个玩耍的同学)

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,观察者模式就讲完了,下面我们来总结一下:

观察者模式:又叫 发布-订阅模式,他定义了一种一对多的依赖关系,让多个观察者对象共同监听同一个主题对象,当主题对象状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。

观察者模式所做的事情就是解耦,让耦合的双方都依赖于抽象,而不依赖于具体,从而使得自己的变化不会影响到另一边。