学习自『观察者模式』来钓鱼
定义
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
类图

总的来说,发布订阅模式中有两个关键字,通知和更新。
namespace Observer.Pattern.SimpleDemo;
///
一个钓鱼工具的抽象类,维护订阅者列表,并负责循环通知订阅者。```csharpnamespace Observer.Pattern.SimpleDemo;/// <summary>/// 钓鱼工具抽象类/// 用来维护订阅者列表,并通知订阅者/// </summary>public abstract class FishingTool{private readonly List<ISubscriber> _subscribers;protected FishingTool(){_subscribers = new List<ISubscriber>();}public void AddSubscriber(ISubscriber subscriber){if (!_subscribers.Contains(subscriber))_subscribers.Add(subscriber);}public void RemoveSubscriber(ISubscriber subscriber){if (_subscribers.Contains(subscriber))_subscribers.Remove(subscriber);}public void Notify(FishType type){foreach (var subscriber in _subscribers)subscriber.Update(type);}}
鱼竿的实现,这里用随机数模拟鱼儿咬钩
namespace Observer.Pattern.SimpleDemo;/// <summary>/// 鱼竿/// </summary>public class FishingRod : FishingTool{/// <summary>/// 鱼儿咬钩/// </summary>public void Fishing(){Console.WriteLine("开始下钩!");//用随机数模拟鱼咬钩,若随机数为偶数,则为鱼咬钩if (new Random().Next() % 2 == 0){var type = (FishType)new Random().Next(0, 5);Console.WriteLine("铃铛:叮叮叮,鱼儿咬钩了");Notify(type);}}}
定义简单的观察者接口
namespace Observer.Pattern.SimpleDemo;/// <summary>/// 订阅者(观察者)接口/// 由具体的订阅者实现Update()方法/// </summary>public interface ISubscriber{void Update(FishType type);}
垂钓者实现观察者接口
namespace Observer.Pattern.SimpleDemo;/// <summary>/// 垂钓者实现观察者接口/// </summary>public class FishingMan : ISubscriber{public FishingMan(string name){Name = name;}public string Name { get; set; }public int FishCount { get; set; }/// <summary>/// 垂钓者收钩/// </summary>/// <param name="type"></param>public void Update(FishType type){FishCount++;Console.WriteLine("{0}:钓到一条[{2}],已经钓到{1}条鱼了!", Name, FishCount, type);}}
场景类实现
//观察者模式(简单的实现方式)--钓鱼:鱼儿咬钩,鱼竿通过铃铛通知垂钓者收钩。using Observer.Pattern.SimpleDemo;SimpleObserverTest();/// <summary>/// 测试简单实现的观察者模式/// </summary>void SimpleObserverTest(){Console.WriteLine("简单实现的观察者模式:");Console.WriteLine("=======================");//1、初始化鱼竿var fishingRod = new FishingRod();//2、声明垂钓者var jeff = new FishingMan("圣杰");//3、将垂钓者观察鱼竿fishingRod.AddSubscriber(jeff);//4、循环钓鱼while (jeff.FishCount < 5){fishingRod.Fishing();Console.WriteLine("-------------------");//睡眠5sThread.Sleep(5000);}}
委托方式实现
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
namespace Observer.Pattern.DelegateDemo;/// <summary>/// 鱼的品类枚举/// </summary>public enum FishType{鲫鱼,鲤鱼,黑鱼,青鱼,草鱼,鲈鱼}
有了委托,我们就不再需要定义专门的抽象被观察者对象了,直接实现鱼竿
namespace Observer.Pattern.DelegateDemo;/// <summary>/// 鱼竿(被观察者)/// </summary>public class FishingRod{public delegate void FishingHandler(FishType type); //声明委托public event FishingHandler FishingEvent; //声明通知事件public void Fishing(){Console.WriteLine("开始下钩!");//用随机数模拟鱼咬钩,若随机数为偶数,则为鱼咬钩if (new Random().Next() % 2 == 0){var a = new Random(10).Next();var type = (FishType)new Random().Next(0, 5);//鱼儿咬钩,鱼竿通过铃铛通知垂钓者收钩Console.WriteLine("铃铛:叮叮叮,鱼儿咬钩了");if (FishingEvent != null)FishingEvent(type);}}}
因为被观察者定义了委托,我们也没必要定义专门的观察者接口,只需要在具体的观察者中实现对应的委托即可。
namespace Observer.Pattern.DelegateDemo;/// <summary>/// 垂钓者(观察者)/// </summary>public class FishingMan{public FishingMan(string name){Name = name;}public string Name { get; set; }public int FishCount { get; set; }/// <summary>/// //鱼儿咬钩,鱼竿通过铃铛通知垂钓者收钩/// </summary>/// <param name="type"></param>public void Update(FishType type){FishCount++;Console.WriteLine("{0}:钓到一条[{2}],已经钓到{1}条鱼了!", Name, FishCount, type);}}
场景类
//观察者模式(委托实现方式)--钓鱼:鱼儿咬钩,鱼竿通过铃铛通知垂钓者收钩。using Observer.Pattern.DelegateDemo;DelegateObserverTest();/// <summary>/// 测试委托实现的观察者模式/// </summary>void DelegateObserverTest(){Console.WriteLine("委托实现的观察者模式:");Console.WriteLine("=======================");//1、初始化鱼竿var fishingRod = new FishingRod();//2、声明垂钓者var jeff = new FishingMan("圣杰");//3、注册观察者(绑定通知事件)fishingRod.FishingEvent += jeff.Update;//4、循环钓鱼while (jeff.FishCount < 5){fishingRod.Fishing();Console.WriteLine("-------------------");//睡眠5sThread.Sleep(5000);}}
总结
观察者模式中有两个关键字,通知和更新。
被观察者状态改变通知观察者做出相应更新。
解决的是当对象改变时需要通知其他对象做出相应改变的问题。
优缺点
优点
观察者和被观察者之间是抽象耦合,易于扩展;
可构造一套触发机制,形成广播链,支持广播通信;
缺点
若一个对象存在较多的观察者,则通知观察者存在效率问题;
观察者和被观察者间可能会导致循环依赖,导致系统奔溃。
应用场景
- 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
- 事件多级触发场景。
- 跨系统的消息交换场景,如消息队列的处理机制。
