设计模式:观察者模式
假如现在有这么一个需求,猫叫了一声,老鼠开始逃跑,狗大叫,主人醒来,宝宝也醒来了并且哭了起来。
实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | public class Mouse { public void Run() { Console.WriteLine( "老鼠开始逃跑" ); } } public class Dog { public void Wang() { Console.WriteLine( "狗大叫" ); } } public class Master { public void Wake() { Console.WriteLine( "主人醒了" ); } } public class Baby { public void Cry() { Console.WriteLine( "宝宝也醒来了并且哭了起来" ); } } public class Cat { public void Miao() { Console.WriteLine( "猫叫了一声!" ); //老鼠开始逃跑,狗大叫,主人醒来,宝宝也醒来了并且哭了起来。 new Mouse().Run(); new Dog().Wang(); new Master().Wake(); new Baby().Cry(); } } internal class Program { /// <summary> /// 调用 /// </summary> static void Main( string [] args) { Cat cat = new Cat(); cat.Miao(); Console.ReadKey(); } } |
上面的代码虽然实现了需求,但是有严重的依赖关系。例如:猫叫后老鼠不跑了,或者增加小偷等等,猫这个类就会被反复修改。那么为了保证猫的稳定,就要把猫叫后的一堆动作剔除了,但是需求又必须触发这一堆动作,这就是矛盾的关键点。导致这个问题的原因是,在Miao方法中即指定了对象又出发了对动作。
而解决矛盾的关键点是对象,那么我们就只触发动作,不指定对象,把对象的指定放到上端去。这样我们就可以解决问题了。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | /// <summary> /// 动作的抽象接口 /// </summary> public interface IObserver { void Action(); } public class Mouse : IObserver { public void Action() { this .Run(); } public void Run() { Console.WriteLine( "老鼠开始逃跑" ); } } public class Dog : IObserver { public void Action() { this .Wang(); } public void Wang() { Console.WriteLine( "狗大叫" ); } } public class Master : IObserver { public void Action() { this .Wake(); } public void Wake() { Console.WriteLine( "主人醒了" ); } } public class Baby:IObserver { public void Action() { this .Cry(); } public void Cry() { Console.WriteLine( "宝宝也醒来了并且哭了起来" ); } } public class Cat { #region 通过把动作抽象成接口来实现,只触发动作,不指定对象 private List<IObserver> _ObserverList = new List<IObserver>(); public void AddObserver(IObserver observer) { this ._ObserverList.Add(observer); } public void CatObserver() { Console.WriteLine( "猫叫了一声!" ); if ( this ._ObserverList != null ) { foreach ( var observer in _ObserverList) { observer.Action(); } } } #endregion } /// <summary> /// 调用 /// </summary> static void Main( string [] args) { Cat cat = new Cat(); cat.AddObserver( new Mouse()); cat.AddObserver( new Dog()); cat.AddObserver( new Master()); cat.AddObserver( new Baby()); cat.CatObserver(); Console.ReadKey(); } |
上面的代码采用的方法是,把动作抽象成接口,然后每个类都继承该接口。这是面向对象语言的实现方式,因为在面向对象设计时,基本上都是类、对象。
在.NET框架中,可以使用委托以及事件来实现,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | public class Cat { #region 通过事件和委托的方式来解决 public event Action MiaoHandler; public void MiaoEvent() { Console.WriteLine( "猫叫了一声!" ); if ( this .MiaoHandler != null ) { //foreach 这块也可以直接写成执行事件 foreach (Action action in this .MiaoHandler.GetInvocationList()) { action.Invoke(); } } } #endregion } { /// <summary> /// 调用 /// </summary> static void Main( string [] args) { cat.MiaoHandler += new Mouse().Run; cat.MiaoHandler += new Dog().Wang; cat.MiaoHandler += new Master().Wake; cat.MiaoHandler += new Baby().Cry; cat.MiaoEvent(); Console.ReadKey(); } } |
从结果来看面向对象的实现方式和事件的实现方式是一样的,把对象的指定放在了上端。而面向对象是进行了抽象(接口),把对象的行为包了一层,然后在传进去。事件则是直接把方法放进去,其实那事件也是委托,通过反编译可以看到委托其实也是类。只是看上去是加方法,本质上是加的是委托的实例。两者都是进行了包装,因为面向对象里面只有类。事件的方式相对来说写发上比较方便,不用在进行抽象一个接口出来。
总结
观察者模式是设计模式中行为模式的一种,它解决了一对多依赖关系的对象的重用问题。此模式的参与者分为两大类,一类是被观察的目标,另一类是观察该目标的观察者们。正因为该模式是基于“一对多”的关系,所以该模式一般是应用于由一个目标对象和N个观察者对象组成。当目标对象的状态发生改变或做出某种行为时,正在观察该目标对象的观察者们将自动地、连锁地作出相应的响应行为。
可以把观察目标理解为主动方、发布方、主体等;把观察者理解为被动方、订阅方、观察器等。目标是整个行为链的源头,其它观察者都依赖于它的变化而作出响应。为了实现低耦合,我们不能使用“直接调用”的方式而需要利用“订阅(清单)-通知”的机制去完成设计。通俗地说就是观察者向目标“订阅”它的改变,而目标发生改变后就“通知”所有已经“订阅”了它的改变的观察者,从而执行“订阅”的内容。这种机制的好处在于降低耦合度,分工明确,目标只负责在自身状态发生改变或做出某种行为时向自身的订阅清单发出“通知”,而不是直接调用观察者的行为(方法);观察者只负责向目标“订阅”它的变化,以及定义自身在收到目标“通知”后所需要做出的具体行为(也就是订阅的内容)。
在.NET框架中,使用委托以及事件,可以很好的实现观察者模式。委托相当于“订阅清单”的角色,当目标中关联了该委托的事件被触发时,则委托将自动按序执行观察者注册于委托中的方法。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)