c#设计模式之观察者模式(Observer Pattern)
场景出发
一个月高风黑的晚上,突然传来了尖锐的猫叫,宁静被彻底打破,狗开始吠了,大人醒了,婴儿哭了,小偷跑了
这个过程,如果用面向对象语言来描述,简单莫过于下:
1 public class Cat 2 { 3 public void Miao() 4 { 5 Console.WriteLine("猫叫了.........."); 6 7 new Dog().Wang(); 8 new Parents().Wake(); 9 new Baby().Cry(); 10 new Thief().Run(); 11 } 12 } 13 public class Dog 14 { 15 public void Wang() 16 { 17 Console.WriteLine("狗汪了.........."); 18 } 19 } 20 21 public class Parents 22 { 23 public void Wake() 24 { 25 Console.WriteLine("大人醒了.........."); 26 } 27 } 28 29 public class Baby 30 { 31 public void Cry() 32 { 33 Console.WriteLine("婴儿哭了.........."); 34 } 35 } 36 37 public class Thief 38 { 39 public void Run() 40 { 41 Console.WriteLine("小偷跑了.........."); 42 } 43 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Cat cat = new Cat(); 6 cat.Miao(); 7 } 8 }
代码分析
分析上述代码,显然违背了面向对象语言几大原则中的:
1单一职责原则:猫这个对象的内部还依赖其他对象细节,致使其本来的职责与其他对象的职责耦合在一起
2开闭原则(Open-Closed Principle,OCP):即对扩展开放,对修改关闭,很显然,如果我们要扩展,例如改变上述动作的顺序,那么我们就不得不去修改猫内部代码
上述代码问题的核心:猫这个对象很不稳定,在我们整个过程中,猫应该是独立存在,且不能够修改的
观察者模式
针对上面的问题场景,在面向对象语言中有一种适用的设计模式——观察者模式
观察者模式将上述对象分为2类:
主题(Subject):被关注的对象,当它的状态发生变化时,会触发观察者的反应,上述的猫就是一个主题,它发出猫叫这个状态变化的信号
观察者(或订阅者,Observer):即关注主题的对象,当主题的状态发生变化,它们会做出反应,上述的狗,大人,婴儿,小偷都是观察者
观察者模式采用的方式是将主题对观察者的直接依赖,转为对它们的抽象的依赖,具体代码如下:
1 /// <summary> 2 /// 观察者的抽象 3 /// </summary> 4 public interface IObserver 5 { 6 void Action(); 7 }
1 /// <summary> 2 /// 主题 3 /// </summary> 4 public class Cat 5 { 6 /// <summary> 7 /// 观察者集合 8 /// </summary> 9 private List<IObserver> _observerList = new List<IObserver>(); 10 /// <summary> 11 /// 添加观察者 12 /// </summary> 13 /// <param name="observer"></param> 14 public void AddObserver(IObserver observer) 15 { 16 _observerList.Add(observer); 17 } 18 /// <summary> 19 /// 状态变化 20 /// </summary> 21 public void Miao() 22 { 23 Console.WriteLine("猫叫了.........."); 24 25 ///后续动作 26 foreach (var item in _observerList) 27 { 28 item.Action(); 29 } 30 } 31 }
1 public class Dog : IObserver 2 { 3 public void Wang() 4 { 5 Console.WriteLine("狗汪了.........."); 6 } 7 public void Action() 8 { 9 Wang(); 10 } 11 } 12 13 public class Parents : IObserver 14 { 15 public void Wake() 16 { 17 Console.WriteLine("大人醒了.........."); 18 } 19 public void Action() 20 { 21 Wake(); 22 } 23 } 24 25 public class Baby : IObserver 26 { 27 public void Cry() 28 { 29 Console.WriteLine("婴儿哭了.........."); 30 } 31 public void Action() 32 { 33 Cry(); 34 } 35 } 36 37 public class Thief : IObserver 38 { 39 public void Run() 40 { 41 Console.WriteLine("小偷跑了.........."); 42 } 43 public void Action() 44 { 45 Run(); 46 } 47 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Cat cat = new Cat(); 6 cat.AddObserver(new Dog()); 7 cat.AddObserver(new Parents()); 8 cat.AddObserver(new Baby()); 9 cat.AddObserver(new Thief()); 10 cat.Miao(); 11 Console.ReadKey(); 12 } 13 }
通过观察者模式,主题与观察者之间的依赖被转移到了接口之上,面对新的扩展,完全不用再去修改主题,当我们需要添加新的观察者,只需要实现IObserver这个接口就可以,当我们需要修改顺序的时候,只需要在上端改变添加的顺序就可以
观察者模式的类图:
观察者模式存在的3种角色:
1主题角色(Cat):被关注的对象
2抽象观察者角色(IObserver):具体观察者角色的抽象
3具体观察者角色(Dog,Parents,Baby,Thief):关注主题的对象,在主题状态改变后,作出响应
Csharp下更优选择
相比于上述使用观察者模式解决问题,在.Net里有更加优秀的解决方案,那就是委托事件
同样是上述这个问题,使用委托事件来解决,代码如下
1 public class Cat 2 { 3 public event Action MiaoEvent; 4 public void Miao() 5 { 6 Console.WriteLine("猫叫了.........."); 7 8 MiaoEvent.Invoke(); 9 } 10 }
1 public class Dog 2 { 3 public void Wang() 4 { 5 Console.WriteLine("狗汪了.........."); 6 } 7 } 8 9 public class Parents 10 { 11 public void Wake() 12 { 13 Console.WriteLine("大人醒了.........."); 14 } 15 } 16 17 public class Baby 18 { 19 public void Cry() 20 { 21 Console.WriteLine("婴儿哭了.........."); 22 } 23 } 24 25 public class Thief 26 { 27 public void Run() 28 { 29 Console.WriteLine("小偷跑了.........."); 30 } 31 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Cat cat = new Cat(); 6 cat.MiaoEvent += new Dog().Wang; 7 cat.MiaoEvent += new Parents().Wake; 8 cat.MiaoEvent += new Baby().Cry; 9 cat.MiaoEvent += new Thief().Run; 10 cat.Miao(); 11 Console.ReadKey(); 12 } 13 }
使用观察者模式,主题内部维护的是一堆观察者的抽象对象,使用事件的方式,主题内部维护的是一张方法列表
适用场景和优劣势
当存在一对多的依赖关系,且需要监听状态变化的时候,观察者模式是一个很好的解决方案,例如:服务订阅通知,警报监控等等
优势:
1隔离了对象之间的直接依赖,降低了程序的耦合度
2增加了程序的可读性,方便了维护和扩展
3不需要熟悉观察者的细节详情,只用实现接口就可以
劣势:
1增加了程序的复杂度,设计模式的通病
2观察者模式的效率是个问题,一个后续动作出现问题,会堵塞整个过程
出自:博客园-半路独行
原文地址:https://www.cnblogs.com/banluduxing/p/9279571.html
本文出自于http://www.cnblogs.com/banluduxing 转载请注明出处。