观察者模式应该算是比较常见的设计模式了,常用的场景有A类发生修改、或者某种操作,从而通知观察者B类进行某种操作
假如需求是 老鼠一开始跑,猫就开始抓
代码如下
/// <summary> /// 老鼠类 /// </summary> public class Mouse { Cat cat = new Cat(); /// <summary> /// 老鼠名字 /// </summary> public string name { set; get; } public void Run () { cat.Catch(this); } } /// <summary> /// 猫类 /// </summary> public class Cat { /// <summary> /// 猫名字 /// </summary> public string name { set; get; } public void Catch (Mouse mouse) { Console.WriteLine($"猫:{name}开始抓老鼠:{mouse.name}"); } }
这样会有一个很严重的问题,Mouse类里面有一个Cat对象,Cat类里面必须引用一下Mouse对象,从而导致如果有一个类进行修改,那么可能引发一连串的连锁反应,同时类之间的耦合性太严重
这个时候就可以通过观察者模式进行修改
/// <summary> /// 老鼠类 /// </summary> public class Mouse { List<ICatch> catches = new List<ICatch>(); /// <summary> /// 老鼠名字 /// </summary> public string name { set; get; } public void Run () { catches.ForEach(t=> { t.Catch(this); }); } } public interface ICatch { void Catch (Mouse mouse); } /// <summary> /// 猫类 /// </summary> public class Cat :ICatch{ /// <summary> /// 猫名字 /// </summary> public string name { set; get; } public void Catch (Mouse mouse) { Console.WriteLine($"猫:{name}开始抓老鼠:{mouse.name}"); } }
添加一个抽象接口,把具体替换成抽象,完成解耦。
在下面的情况下可以考虑使用观察者模式:
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使它们可以各自独立地改变和复用的情况下。从方面的这个词中可以想到,观察者模式肯定在AOP(面向方面编程)中有所体现
- 当对一个对象的改变需要同时改变其他对象,而又不知道具体有多少对象有待改变的情况下。
- 当一个对象必须通知其他对象,而又不能假定其他对象是谁的情况下。
观察者模式有以下几个优点:
- 观察者模式实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层,即观察者。
- 观察者模式在被观察者和观察者之间建立了一个抽象的耦合,被观察者并不知道任何一个具体的观察者,只是保存着抽象观察者的列表,每个具体观察者都符合一个抽象观察者的接口。
- 观察者模式支持广播通信。被观察者会向所有的注册过的观察者发出通知。
观察者也存在以下一些缺点:
- 如果一个被观察者有很多直接和间接的观察者时,将所有的观察者都通知到会花费很多时间。
- 虽然观察者模式可以随时使观察者知道所观察的对象发送了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎样发生变化的。
- 如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃,在使用观察者模式应特别注意这点。