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)——观察者模式的更多相关文章

  1. C#设计模式(17)——观察者模式(Observer Pattern)

    一.引言 在现实生活中,处处可见观察者模式,例如,微信中的订阅号,订阅博客和QQ微博中关注好友,这些都属于观察者模式的应用.在这一章将分享我对观察者模式的理解,废话不多说了,直接进入今天的主题. 二. ...

  2. 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)

    原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...

  3. 设计模式之观察者模式(Observable与Observer)

    设计模式之观察者模式(Observable与Observer) 好久没有写博客啦,之前看完了<设计模式之禅>也没有总结一下,现在回忆一下设计模式之观察者模式. 1.什么是观察者模式 简单情 ...

  4. 8.5 GOF设计模式四: 观察者模式Observer

    GOF设计模式四: 观察者模式Observer  现实中遇到的问题  当有许多不同的客户都对同一数据源感兴趣,对相同的数据有不同的处理方式,该如 何解决?5.1 定义: 观察者模式  观察者模式 ...

  5. php 设计模式之观察者模式(订阅者模式)

    php 设计模式之观察者模式 实例 没用设计模式的代码,这样的代码要是把最上面那部分也要符合要求加进来,就要修改代码,不符合宁增不改的原则 介绍 观察者模式定义对象的一对多依赖,这样一来,当一个对象改 ...

  6. [JS设计模式]:观察者模式(即发布-订阅者模式)(4)

    简介 观察者模式又叫发布---订阅模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 举一个现实生活中的例子,例如小 ...

  7. 实践GoF的23种设计模式:观察者模式

    摘要:当你需要监听某个状态的变更,且在状态变更时通知到监听者,用观察者模式吧. 本文分享自华为云社区<[Go实现]实践GoF的23种设计模式:观察者模式>,作者: 元闰子 . 简介 现在有 ...

  8. 17.java设计模式之观察者模式

    基本需求: 气象站可以将每天测量到的温度,湿度,气压等等,以公告的形式发布出去(比如发布到自己的网站或第三方) 需要设计开放型API,便于其他第三方也能接入气象站获取数据 提供温度.气压和湿度的接口 ...

  9. java设计模式之观察者模式

    观察者模式 观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式.模型-视图(View)模式.源-收听者(Listener)模式或从属者模式)是软件设计模式的一种.在此种模 ...

随机推荐

  1. php+qrcode类+生成二维码方法

    //生成二维码 public function qrcode() { $data = input(); if(!$data['param']){ return json(['code ' => ...

  2. 【Linux基础】grep命令

    1.简介 grep是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来. 命令格式:grep [option] pattern file 2.常用参数与举例: -e :  使用P ...

  3. ubuntu创建idea桌面快捷方式

    This method can be used to create a launcher for any application, not just IntelliJ IDEA. For any la ...

  4. Hystrix是个什么玩意儿

    1. 什么是Hystrix Hystrix是Netflix的一个开源框架,地址如下:https://github.com/Netflix/Hystrix 中文名为“豪猪”,即平时很温顺,在感受到危险的 ...

  5. 清除被占用的8080端口,否则npm run dev无法正常运行

    解决方案一: 1. 打开git-bash2. 输入:netstat -ano查看所有端口信息,如图,找到端口 8080,以及对应的 PID 3.输入:tskill PID 即可杀死进程 解决方案二: ...

  6. ideal中项目resources下txt文件读取不到的问题。

    这次做项目,原来用到了一个txt文件,在ideal中项目启动后报读取不到txt文件.项目原来是在eclipse中的. 在网上找了些文章,发现ideal中要读取到resources下的文件需要加上下面红 ...

  7. 【转】Android调用Sqlite数据库时自动生成db-journal文件的原因

    数据库为了更好实现数据的安全性,一半都会有一个Log文件方便数据库出现意外时进行恢复操作等.Sqlite虽然是一个单文件数据库,但麻雀虽小五脏俱全,它也会有相应的安全机制存在 这个journal文件便 ...

  8. 等待通知--wait notify

    1.简单理解 在jdk1.5之前用于实现简单的等待通知机制,是线程之间通信的一种最原始的方式.考虑这样一种等待通知的场景:A B线程通过一个共享的非volatile的变量flag来实现通信,每当A线程 ...

  9. mongo分片集群部署

    测试环境192.168.56.101-213 前期准备: openssl rand -base64 756 > /home/software/mongodb/mongodbkey chmod   ...

  10. codeforces#1136 C. Nastya Is Transposing Matrices(找规律)

    题意:给出两个n*m的矩阵,每次操作可以让一个正方形矩阵行列交换.问,在无限次操作下,第一个矩阵能否变成第二个矩阵 分析:先把操作限定在2*2的矩阵中.这样对角线上的元素就可以随意交换.也就是说,如果 ...