C#设计模式(17)——观察者模式
1.观察者模式介绍
观察者模式又叫发布-订阅模式,它定义了对象间的一种一对多关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并被自动更新。观察者模式就四个角色:抽象主题,具体主题,抽象观察者,具体观察者。抽象主题是一个抽象的接口或者抽象类,对主题的功能进行抽象,抽象观察者对具体的观察者进行抽象。观察者模式在软件开发中的应用十分广泛,如微信订阅号、微博订阅等都采用了观察者模式,我们关注了某个明星的微博,当这个明星更新微博状态时我们的微博都会收到通知,明星的微博就是主题角色,我们的微博属于观察者角色。
下边通过大话设计模式中秘书做卧底的例子来理解观察者模式的用法。上班时间有的同事喜欢偷偷看股票行情,有的同事看NBA直播,但是老板会不定时来办公室,如果被老板逮到就不好了,于是大家想出来一个办法:让秘书小妹在外边把风,如果老板来了就通知下大家,这样就不会被逮到了。这个栗子中秘书小妹就是一个主题,同事们属于观察者,秘书小妹如果看到老板过来就会通知 送她零食的同事们。代码比较简单:
//抽象主题类
public interface ISubject
{ //添加观察者 送零食的加进来,老板来了通知你
void Add(Observer observer);
//删除观察者 不送零食的秘书小妹就不通知了
void Remove(Observer observer);
//主题状态
string SubjectState { get; set; }
//通知方法
void Notify();
} //具体主题 ,秘书类
public class Mishu : ISubject
{
//秘书要知道通知哪些同事
private IList<Observer> observers = new List<Observer>(); public void Add(Observer observer)
{
observers.Add(observer);
}
public void Remove(Observer observer)
{
observers.Remove(observer);
}
public string SubjectState { get; set; }
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
}
//抽象观察者
public abstract class Observer
{
//名字
protected string name;
//观察者要知道自己订阅了那个主题
protected ISubject sub;
public Observer(string name, ISubject sub)
{
this.name = name;
this.sub = sub;
}
//接受到通知后的更新方法
public abstract void Update();
}
//看股票的同事
public class StockObserver : Observer
{
public StockObserver(string name, ISubject sub) : base(name, sub) { }
public override void Update()
{
Console.WriteLine($"通知内容:{sub.SubjectState},反应:{name}关闭股票行情,继续工作!");
}
}
//看NBA的同事
public class NBAObserver : Observer
{
public NBAObserver(string name, ISubject sub) : base(name, sub) { }
public override void Update()
{
Console.WriteLine($"通知内容:{sub.SubjectState},反应:{name}关闭NBA直播,继续工作!");
}
} /// <summary>
/// 客户端调用
/// </summary>
class Program
{
static void Main(string[] args)
{
Mishu mishu = new Mishu();
//新建同事 观察者角色
Observer tongshi1 = new StockObserver("巴菲特", mishu);
Observer tongshi2 = new NBAObserver("麦迪", mishu);
//秘书小妹要知道哪些同事要通知(主题要知道所有订阅了自己的观察者)
mishu.Add(tongshi1);
mishu.Add(tongshi2);
//主题状态更改了
mishu.SubjectState = "老板回来了!";
//调用主题的通知方法
mishu.Notify();
Console.ReadKey();
}
}
运行程序,执行结果如下:

上边的例子中秘书小妹充当主题角色,上班偷懒的同事充当观察者角色,通过观察者模式实现了功能。但是这里还有一个小问题:上边例子能够成功的前提是所有观察者的角色都有一个Update()方法来执行更新。但是有时候各个观察者并不都是相同的类型,如观察者1收到通知执行Update1()方法,而观察者2收到通知执行的是Update2()方法,这时候采用上边的模式就不能满足需求了。怎么改进呢?①各个观察者不属于同一类,所以不需要抽象观察者类了 ②因为各个观察者的反应不是同一的Update(),所以我们不能foreach遍历观察者集合来统一调用Update()方法了,这时可以考虑通过事件委托在客户端确定所有观察者的反应。改进后的代码如下:
//抽象主题角色
public interface ISubject
{
//添加观察者
void Add(Observer observer);
//删除观察者
void Remove(Observer observer);
//主题状态
string SubjectState { get; set; }
//通知方法
void Notify();
}
//***********************1.定义一个委托
public delegate void EventHandler();
//具体主题角色 秘书类
public class Mishu : ISubject
{
public event EventHandler Update;
//存储要通知的同事
public IList<Observer> observers = new List<Observer>();
public string SubjectState { get; set; } public void Add(Observer observer)
{
observers.Add(observer);
}
public void Remove(Observer observer)
{
observers.Remove(observer);
}
//**********2.通知方法中不能通过遍历来统一调用每个观察者的Update()方法了,改成执行一个委托
public void Notify()
{
Update();
}
}
//抽象观察者角色
public abstract class Observer {
protected ISubject sub;
protected string name;
protected Observer(string name,ISubject sub)
{
this.name = name;
this.sub = sub;
} }
//具体观察者角色 看股票的同事
public class StockObserver
{
public string name;
public ISubject sub;
public StockObserver(string name,ISubject sub)
{
this.name = name;
this.sub = sub;
}
public void CloseStockMarket()
{
Console.WriteLine($"通知内容:{sub.SubjectState},反应:{name}关闭股票行情,继续工作!");
}
}
//具体观察者角色 看NBA的同事
public class NBAObserver
{
public string name;
public ISubject sub;
public NBAObserver(string name, ISubject sub)
{
this.name = name;
this.sub = sub;
}
public void CloseNBA()
{
Console.WriteLine($"通知内容:{sub.SubjectState},反应:{name}关闭NBA直播,继续工作!");
}
}
class Program
{
static void Main(string[] args)
{
Mishu mishu = new Mishu();
//观察者订阅了主题mishu
StockObserver tongshi1 = new StockObserver("巴菲特", mishu);
NBAObserver tongshi2 = new NBAObserver("麦迪", mishu);
//*******************3.将遍历观察者并调用观察者的Update(),改成了事件委托形式
mishu.Update += tongshi1.CloseStockMarket;
mishu.Update += tongshi2.CloseNBA;
//主题状态更改,并通知
mishu.SubjectState = "老板回来了!";
mishu.Notify();
Console.ReadKey();
}
}
运行结果:

我们看到通过事件委托我们可以实现让不同的观察者调用不同的方法,当我们遇到类似这样的情况:点击一个按钮,有的页面元素显示Show(),有的隐藏Hide(),有的关闭Close()就可以采用这种模式。但是这种模式没有对具体的观察者进行抽象,如果观察者太多也会造成事件委托过于复杂。两种观察者模式的实现各有利弊,我们可以根据实际的情况来选择。
2.小结
上边栗子的类图:

观察者模式的使用场景:当一个对象的状态发生变化时,需要让其它对象知道并作出反应可以考虑观察者模式。
观察者模式的优点:想一下如果不使用观察者模式,订阅者怎么获取订阅号的更新?最直接的方法应该就是轮询了,这种方式十分浪费资源,而且获取更新也不及时。观察者模式的主要功能就是解决了这一问题。这种模式符合依赖倒置原则,同时降低了观察者和主题间的耦合,建立了一套触发机制。
C#设计模式(17)——观察者模式的更多相关文章
- C#设计模式(17)——观察者模式(Observer Pattern)
一.引言 在现实生活中,处处可见观察者模式,例如,微信中的订阅号,订阅博客和QQ微博中关注好友,这些都属于观察者模式的应用.在这一章将分享我对观察者模式的理解,废话不多说了,直接进入今天的主题. 二. ...
- 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)
原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...
- 设计模式之观察者模式(Observable与Observer)
设计模式之观察者模式(Observable与Observer) 好久没有写博客啦,之前看完了<设计模式之禅>也没有总结一下,现在回忆一下设计模式之观察者模式. 1.什么是观察者模式 简单情 ...
- 8.5 GOF设计模式四: 观察者模式Observer
GOF设计模式四: 观察者模式Observer 现实中遇到的问题 当有许多不同的客户都对同一数据源感兴趣,对相同的数据有不同的处理方式,该如 何解决?5.1 定义: 观察者模式 观察者模式 ...
- php 设计模式之观察者模式(订阅者模式)
php 设计模式之观察者模式 实例 没用设计模式的代码,这样的代码要是把最上面那部分也要符合要求加进来,就要修改代码,不符合宁增不改的原则 介绍 观察者模式定义对象的一对多依赖,这样一来,当一个对象改 ...
- [JS设计模式]:观察者模式(即发布-订阅者模式)(4)
简介 观察者模式又叫发布---订阅模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 举一个现实生活中的例子,例如小 ...
- 实践GoF的23种设计模式:观察者模式
摘要:当你需要监听某个状态的变更,且在状态变更时通知到监听者,用观察者模式吧. 本文分享自华为云社区<[Go实现]实践GoF的23种设计模式:观察者模式>,作者: 元闰子 . 简介 现在有 ...
- 17.java设计模式之观察者模式
基本需求: 气象站可以将每天测量到的温度,湿度,气压等等,以公告的形式发布出去(比如发布到自己的网站或第三方) 需要设计开放型API,便于其他第三方也能接入气象站获取数据 提供温度.气压和湿度的接口 ...
- java设计模式之观察者模式
观察者模式 观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式.模型-视图(View)模式.源-收听者(Listener)模式或从属者模式)是软件设计模式的一种.在此种模 ...
随机推荐
- A Deep Learning-Based System for Vulnerability Detection(一)
接着上一篇,讨论讨论具体步骤实现方法.步骤1-3分别在下面进行阐述,步骤4,6都是标准的,步骤5类似于步骤1-3. 结合这个图进行讨论详细步骤: 步骤1:提取库/API函数调用和程序片段 1.1将库/ ...
- LivePhoto开发,你要知道的知识点
前言 Apple从iPhone6s开始支持Live Photo.Live Photo 会录下拍照前后 1.5 秒所发生的一切,因此用户获得的不仅仅是一张精美照片,还有拍照前后时刻的动作和声音.具体的操 ...
- 【Python 22】52周存钱挑战2.0(列表list和math函数)
1.案例描述 按照52周存钱法,存钱人必须在一年52周内,每周递存10元.例如,第一周存10元,第二周存20元,第三周存30元,直到第52周存520元. 记录52周后能存多少钱?即10+20+30+. ...
- IDEA+Maven+各个分支代码进行合并
各个模块的分支代码进行合并到一起:https://blog.csdn.net/xsj_blog/article/details/79198502
- 《jmeter:菜鸟入门到进阶》系列
jmeter是我从事软件测试工作以来接触的第一个性能测试工具,也是耗费时间精力最多的一个工具,当然,学习jmeter过程中,由于知识储备不够,也顺带学习了很多其他相关的一些知识. 一直有个想法,就是把 ...
- Filebeat命令参考
Filebeat命令参考: Filebeat提供了一个命令行界面,用于启动Filebeat并执行常见任务,例如测试配置文件和加载仪表板.命令行还支持用于控制全局行为的全局标志. 命令: export ...
- Cesium如何通过addImageryProvider方法加载SkylineGlobe Server发布的WMS服务
某某某单位用SkylineGlobeServer7版本发布了好些服务,然后让我们在Cesium里都加载进来展示. 其实只要符合OGC标准的,加进来还是很容易的. 示例代码如下: function te ...
- 【翻译】WhatsApp 加密概述(技术白皮书)
目录 简介 术语 客户端注册 会话初始化设置 接收会话设置 交换信息 传输媒体和附件 群组消息 通话设置 ...
- easyui-tab标签
一. 加载方式 //class 加载方式<div id="box" class="easyui-tabs" style="width:500px ...
- 最速下降方法和Newton方法
目录 最速下降方法 Euclid范数和二次范数 采用\(\ell_1\)-范数的最速下降方向 Newton 方法 Newton 步径 二阶近似的最优解 线性化最优性条件的解 Newton 步径的仿射不 ...