观察者模式(Observer Pattern)

该文章的最新版本已迁移至个人博客【比特飞】,单击链接 https://www.byteflying.com/archives/423 访问。

观察者模式属于行为型模式,有时又被称为模型-视图(Model-View)模式、发布-订阅(Publish-Subscribe)模式、源-监听器(Source-Listener)模式或从属者(Dependents)模式。

观察者模式完美的将观察者和被观察的对象分离开,并在目标物件状态改变时主动向观察者发出通知(接口方法、抽象方法、委托、事件)。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。

角色:

1、抽象主题(Subject)

主题需要维持对所有观察者的引用,以便在状态更改时调用观察者接口。每个主题都可以有任何数量的观察者,并可以增加和删除观察者对象;

2、具体主题(Concrete Subject)

将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有订阅过的观察者发送更改通知;

3、抽象观察者(Observer)

为所有的具体观察者定义一个接口,在得到主题通知时更新自己;

4、具体观察者(Concrete Observer)

实现抽象观察者角色所要求的通知(接收)接口,以便使本身的状态与主题状态协调。

示例:

命名空间ObserverPattern中包含抽象出版社基类Publisher(主题)、中国机械工业出版社类Machine、中国农业出版社类Agriculture、读者接口IReader(观察者)、具体观察者Iori类和Jay类、图书类Book。另外为了代码更整洁,引入Extentions扩展类,方便图书和读者信息的处理。这个示例展示读者如何观察出版社发布图书的状态,并在出版社发布图书时,得到通知。

namespace ObserverPattern
public class Book {

    public string Name { get; set; }

    public Book(string name) {
Name = name;
} }

简单的图书类,仅包含一个构造函数和图书的名字属性。

public interface IReader {

    void Receive(Publisher publisher, Book book);

}

读者接口,定义公开的Receive契约,并且得到出版社和图书信息。

public class Iori : IReader {

    public void Receive(Publisher publisher, Book book) {
Console.WriteLine(
$"{this.ReaderName()} received {book.BookName()} from {publisher.Name}.");
} }
public class Jay : IReader {

    public void Receive(Publisher publisher, Book book) {
Console.WriteLine(
$"{this.ReaderName()} received {book.BookName()} from {publisher.Name}.");
} }

具体读者类,Iori和Jay,一个是我的英文名,另一个则是我的偶像。

public abstract class Publisher {

    private List<IReader> _readers = new List<IReader>();

    public string Name { get; set; }
private const string LINE_BREAK =
"----------------------------------------" +
"----------------------------------------";
//文章排版需要,故折成2行 public void AttachReader(IReader reader) {
if (reader == null) throw new ArgumentNullException();
_readers.Add(reader);
} public bool DetachReader(IReader reader) {
if (reader == null) throw new ArgumentNullException();
return _readers.Remove(reader);
} protected virtual void OnPublish(Book book, DateTime publishTime) {
Console.WriteLine(
$"{Name} published {book.BookName()} at {publishTime.ToString("yyyy-MM-dd")}.");
Console.WriteLine(LINE_BREAK);
} public void Publish(Book book, DateTime publishTime) {
OnPublish(book, publishTime);
foreach (var reader in _readers) {
if (reader != null) {
reader.Receive(this, book);
}
}
Console.WriteLine(LINE_BREAK);
} }

抽象出版社类Publisher,即被观察者,这是整个观察者模式的核心基类。首先在内部维持对IReader列表的引用,并且可以对观察者进行增加(AttachReader)或删除(DetachReader)操作。而发布方法Publish则在出版社发布新图书时,通知所有观察者。

public class Machine : Publisher {

    public Machine(string name) {
Name = name;
} protected override void OnPublish(Book book, DateTime publishTime) {
Console.WriteLine(
$"{Name} published {book.BookName()} at {publishTime.ToString("yyyy-MM-dd")}." +
$"->Machine.OnPublish");
Console.WriteLine(LINE_BREAK);
} }
public class Agriculture : Publisher {

    public Agriculture(string name) {
Name = name;
} }

具体出版社类Machine和Agriculture,代表中国机械出版社和中国农业出版社。

public static class Extentions {

    public static string ReaderName(this IReader reader) {
return reader.ToString().Replace(nameof(ObserverPattern) + ".", "");
} public static string BookName(this Book book) {
return "[" + book.Name + "]";
} }

公开的静态的扩展方法类,其中ReaderName扩展处理读者名称前的命名空间,使用nameof关键字是为了支持重构。BookName扩展则用来为书名加上中括号。

public class Program {

    public static void Main(string[] args) {
Publisher publisher = new Machine("China Machine Press"); var iori = new Iori();
var jay = new Jay(); publisher.AttachReader(iori);
publisher.AttachReader(jay); publisher.Publish(new Book("How the Steel Was Tempered"), DateTime.UtcNow); publisher.DetachReader(jay); publisher.Publish(new Book("Jane Eyre"), DateTime.UtcNow); publisher = new Agriculture("China Agriculture Press"); publisher.AttachReader(iori);
publisher.AttachReader(jay); publisher.Publish(new Book("Romance of the Three Kingdoms"), DateTime.UtcNow); Console.ReadKey();
} }

这个是调用方的代码示例,首先创建机械工业出版社实例,再创建2个具体读者实例并订阅,最后出版社发布《钢铁是怎么炼成的》图书,2个读者都能收到发布通知。接下来演示了取消订阅和中国农业出版社的发布情况,请各位看官自行分析。以下是这个案例的输出结果:

China Machine Press published [How the Steel Was Tempered] at 2018-07-19.->Machine.OnPublish
--------------------------------------------------------------------------------
Iori received [How the Steel Was Tempered] from China Machine Press.
Jay received [How the Steel Was Tempered] from China Machine Press.
--------------------------------------------------------------------------------
China Machine Press published [Jane Eyre] at 2018-07-19.->Machine.OnPublish
--------------------------------------------------------------------------------
Iori received [Jane Eyre] from China Machine Press.
--------------------------------------------------------------------------------
China Agriculture Press published [Romance of the Three Kingdoms] at 2018-07-19.
--------------------------------------------------------------------------------
Iori received [Romance of the Three Kingdoms] from China Agriculture Press.
Jay received [Romance of the Three Kingdoms] from China Agriculture Press.
--------------------------------------------------------------------------------

优点:

该文章的最新版本已迁移至个人博客【比特飞】,单击链接 https://www.byteflying.com/archives/423 访问。

1、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不了解每一个具体观察者的内部细节,它只知道它们都有一个共同的接口;

2、由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次,并且符合里氏替换原则和依赖倒置原则。

缺点:

1、如果一个被观察者对象维持了较多的观察者,将所有的观察者都通知到会花费很多时间;

2、如果在被观察者之间有循环依赖的话,被观察者可能会触发它们之间进行循环调用,导致系统崩溃;

3、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。

使用场景:

1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变;

2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的内部细节。

C#设计模式之19-观察者模式的更多相关文章

  1. .NET设计模式(19):观察者模式(Observer Pattern)(转)

    概述 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知.如果这样的依赖关系过于紧密,将使软件不能很好地抵御 ...

  2. Java设计模式(19)状态模式(State模式)

    State的定义:不同的状态,不同的行为:或者说,每个状态有着相应的行为. 何时使用状态模式 State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If else ...

  3. 设计模式 ( 十六 ) 观察者模式Observer(对象行为型)

    设计模式 ( 十六 ) 观察者模式Observer(对象行为型) 1.概述 一些面向对象的编程方式,提供了一种构建对象间复杂网络互连的能力.当对象们连接在一起时,它们就可以相互提供服务和信息. 通常来 ...

  4. Java设计模式之《观察者模式》及应用场景

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6513651.html 观察者模式,又可以称之为发布-订阅模式,观察者,顾名思义,就是一个监 ...

  5. Java设计模式百例 - 观察者模式

    观察者(Observer)模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,主体对象的状态变化会通知所有观察者对象.观察者模式又叫做发布-订阅(Publish/Subscribe ...

  6. [head first 设计模式]第二章 观察者模式

    [head first 设计模式]第二章 观察者模式 假如我们有一个开发需求--建造一个气象观测站展示系统.需求方给我们提供了一个WeatherObject对象,能够自动获得最新的测量数据.而我们要建 ...

  7. [设计模式] 19 观察者模式 Observer Pattern

    在GOF的<设计模式:可复用面向对象软件的基础>一书中对观察者模式是这样说的:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.当一个 ...

  8. Java设计模式10:观察者模式

    观察者模式 观察者模式也叫作发布-订阅模式,也就是事件监听机制.观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态上发生变化时,会通知所有观察者对象,使他 ...

  9. 大话设计模式C++版——观察者模式

    观察者模式是一种类似于消息分发的模式,用于一个任务需要被多个对象监听的场景,或者成员对象需要反向通知类对象的情况,是一种很有用的设计模式.    这里以大话设计模式中的例子为例,办公室员工A.B.C在 ...

  10. 【C++深入浅出】设计模式学习之观察者模式

    前言 前两天学习了weak_ptr以后还是不甚明了,一则需要实际应用去锤炼,二来就是不懂观察者模式. 正文 观察者模式又叫发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对 ...

随机推荐

  1. linux中无法使用vim命令

    报:linux中  vim 不是内部指令! 解决: 1. rpm -qa | grep vim // 查看vim命令在什么软件包 出现 vim-minimal-7.4.160-4.el7.x86_64 ...

  2. DEX文件解析--3、dex文件字符串解析

    一.前言    前两篇文章链接:     1.DEX文件头解析     2.DEX文件校验和解析    PS:前几天检查文件夹的时候发现DEX文件解析还只写了开头,正好找点事情来做,就去接着解析DEX ...

  3. react : umi 引入 antd 踩坑

    首先要明确一个问题. 不管是 antd 还是 dva 还是别的什么东西,他们都是 umi 的插件——只要这个项目是使用 umi 脚手架生成的. 所以第一步应该是 .umirc.js (config.j ...

  4. java中实现无限层级的树形结构

    本文展示了两个实现方法的代码.两个代码的实现方法不同,代码2更为简单. 先看一下最后实现的结果: 最后结果-json 代码1: 实现过程: 1.传入一段json字符串 2.将字符串转换成对象存入节点列 ...

  5. Python虚拟环境(virtualenv)

    python虚拟环境 虚拟环境:一个独立的可以运行的python执行环境,可以创建多个,且相互之间互不影响 使用virtualenv库 pip install virtualenv 用法 # 创建虚拟 ...

  6. 动手实现一个简单的 rpc 框架到入门 grpc (下)

    之前手动实现了一次简陋的 rpc 调用,为了简单使用了 json 编码信息,其实这是非常不可靠的,go 中 json 解析会有一些问题,比如整数会变成浮点数,而且 json 字符串比较占空间. gRP ...

  7. 题解 SP1841 【PPATH - Prime Path】

    模拟赛考到了这个题,但我傻傻的用了\(DFS\),于是爆了零 后来才想明白,因为搜索树的分支很多,但答案的深度却又没有那么深,所以在这里\(BFS\),而\(DFS\)一路搜到底的做法则会稳稳地\(T ...

  8. epic游戏平台如何启用认证器应用程序/二次验证码/谷歌身份验证器?

    1.登陆epic游戏平台,找到二次验证绑定界面 登陆https://www.epicgames.com/store/zh-CN/, 点右上角用户头像-[账户]. 之后点-[密码与安全] 在[双重验证] ...

  9. springcloud之简介

    springcloud官方文档翻译网站:https://springcloud.cc/ 一.网站架构的演变过程.(这些架构描述的不是很到位,之后需要从新学习) 传统架构 —> 分布式架构 —&g ...

  10. [C++]类的空指针调用成员函数后,会发生什么事?

    类的实例调用成员函数的原理 其实不管是通过对象实例或指针实例调用,其实底层调用的过程都是一样的,都是把当前对象的指针作为一个参数传递给被调用的成员函数.通过下面的相关实例代码进行检验: 实验的C++代 ...