今天我们来讲一下观察者模式。还是老样子,给大家一个案例。
一、案例
在我们怀念的学生时代,我们会有这么一个现象,当在教室里上自习的时候,让一个同学把风,我们在教室里玩,当老师来的时候,让那个同学给我们说一声。
好,下面我们就用简单的控制台应用程序来实现上述的场景(一个把风的同学,两个玩耍的同学)
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,观察者模式就讲完了,下面我们来总结一下:
观察者模式:又叫 发布-订阅模式,他定义了一种一对多的依赖关系,让多个观察者对象共同监听同一个主题对象,当主题对象状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
观察者模式所做的事情就是解耦,让耦合的双方都依赖于抽象,而不依赖于具体,从而使得自己的变化不会影响到另一边。
好了,观察者模式我么你就说到这里了,下一篇博文,将会讲 抽象工厂模式
本系列将持续更新,喜欢的小伙伴可以点一下关注和推荐,谢谢大家的支持。