19使用推模式和拉模式实现电梯超重报警
当电梯超过最大承重800公斤,报警器报警。把电梯看成被观察者,报警器看成观察者。分别使用推模式和拉模式实现超重报警。
推模式实现超重报警
被观察者在运行本身方法的时候,达到一定条件,就触发通知事件,把自己的状态"推"个观察者。
→把电梯看作是被观察者,当运行其TestWeight()方法时候,如果满足某种weight变量>800,就触发方法把自己的状态推给观察者。
1: //被观察者
2: public class Elevator : SubjectBase
3: {
4: private int weight;
5: private static string factory = "日立";
6: private static string area = "广州";
7:
8:
9: protected virtual void OnMoreThanStandard(MoreThanStandardEventArgs e)
10: {
11: base.Notify(e);
12: }
13:
14: public void TestWeight()
15: {
16: for (int i = 795; i <= 805; i++)
17: {
18: weight = i;
19: if (weight > 800)
20: {
21: MoreThanStandardEventArgs e = new MoreThanStandardEventArgs(weight,factory,area);
22: OnMoreThanStandard(e);
23: }
24: }
25: }
26: }
→Elevator把自己的状态信息封装成MoreThanStandardEventArgs类,推送给观察者。
1: //观察者和被观察者之间传递的参数
2: public class MoreThanStandardEventArgs
3: {
4: private int weight;
5: private string factory;
6: private string area;
7:
8: public MoreThanStandardEventArgs(int weight, string factory, string area)
9: {
10: this.weight = weight;
11: this.factory = factory;
12: this.area = area;
13: }
14:
15: public int Weight {get { return weight; }}
16: public string Factory {get { return factory; }}
17: public string Area {get { return area; }}
18: }
→Elevator本身没有注册、注销、通知观察者的能力,所以需要继承有这些能力的基类SubjectBase。
1: //被观察者基类抽象类
2: public abstract class SubjectBase : IObseervable
3: {
4: private List<IObserver> container = new List<IObserver>();
5: public void Register(IObserver obj)
6: {
7: container.Add(obj);
8: }
9:
10: public void Unregister(IObserver obj)
11: {
12: container.Remove(obj);
13: }
14:
15: protected virtual void Notify(MoreThanStandardEventArgs e)
16: {
17: foreach (IObserver observer in container)
18: {
19: observer.Update(e);
20: }
21: }
22: }
→而IObservalble的接口就注册和取消注册2个行为。
1: //被观察者接口
2: public interface IObseervable
3: {
4: void Register(IObserver obj);
5: void Unregister(IObserver obj);
6: }
→观察者接口拿到被观察者推来的信息,做自己的事。
1: //观察者接口
2: public interface IObserver
3: {
4: void Update(MoreThanStandardEventArgs e);
5: }
→观察者就把状态信息显示出来
1: //观察者
2: public class Alarm : IObserver
3: {
4: public void Update(MoreThanStandardEventArgs e)
5: {
6: Console.WriteLine("嘀嘀嘀:来自{0}{1}的电梯最大承重800kg,目前重量为{2}",e.Area,e.Factory,e.Weight);
7: }
8: }
→主程序
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: Elevator elevator = new Elevator();
6: Alarm alarm = new Alarm();
7:
8: elevator.Register(alarm);
9: elevator.TestWeight();
10: Console.ReadKey();
11: }
12: }
拉模式实现超重报警
观察者把被观察者拉进来作为方法参数,当被观察者在运行本身方法的时候,达到一定条件,就触发通知事件,并把自己传递给观察者方法。
→被观察者Elevator执行方法满足条件就触发通知
1: //被观察者
2: public class Elevator : SubjectBase
3: {
4: private int weight;
5: private static string factory = "日立";
6: private static string area = "广州";
7:
8: public int Weight {get { return weight; }}
9: public string Factory {get { return factory; }}
10: public string Area {get { return area; }}
11:
12: protected virtual void OnMoreThanStandard()
13: {
14: base.Notify(this);
15: }
16:
17: public void TestWeight()
18: {
19: for (int i = 795; i <= 805; i++)
20: {
21: weight = i;
22: if (weight > 800)
23: {
24: OnMoreThanStandard();
25: }
26: }
27: }
28: }
29:
→被观察者Elevator本身没有注册、取消注册、通知观察者的能力,需要继承拥有这些能力的SubjectBase。
1: //被观察者基类
2: public abstract class SubjectBase : IObservable
3: {
4: List<IObserver> container = new List<IObserver>();
5:
6: public void Register(IObserver obj)
7: {
8: container.Add(obj);
9: }
10:
11: public void Unregister(IObserver obj)
12: {
13: container.Remove(obj);
14: }
15:
16: protected virtual void Notify(IObservable obj)
17: {
18: foreach (IObserver observer in container)
19: {
20: observer.Update(obj);
21: }
22: }
23: }
→观察者调用自己拉进来的观察者对象获得其状态信息
1: //观察者接口
2: public interface IObserver
3: {
4: void Update(IObservable sender);
5: }
6:
7: //观察者
8: public class Alarm : IObserver
9: {
10: public void Update(IObservable sender)
11: {
12: Elevator elevator = (Elevator)sender;
13: Console.WriteLine("嘀嘀嘀:来自{0}{1}的电梯最大承重800kg,目前重量为{2}", elevator.Area, elevator.Factory, elevator.Weight);
14: }
15: }
→ 主程序
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: Elevator elevator = new Elevator();
6: Alarm alarm = new Alarm();
7:
8: elevator.Register(alarm);
9: elevator.TestWeight();
10: Console.ReadKey();
11: }
12: }
结果:
推模式和拉模式的区别
● 推模式的好处是按需供给:观察者需要什么状态信息,被观察者就把需要的状态信息封装起来推给观察者。缺点是需要创建封装的状态信息。
● 拉模式的好处是不需要创建推送给观察者的、封装状态的信息。缺点是把被观察者的属性和方法暴露给了被观察者。
而微软定义的委托EventHandler则很好解决了推和拉的问题,不仅可以拿到被观察者本身,同时可以拿到观察者的状态信息:
public delegate void EventHandler(object sender, EventArgs e)
参考资料:
※ 《.NET之美》--张子阳,感谢写了这么好的书!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?