简介

观察者模式(Observer Pattern)是一种行为型模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式使用三个类Subject、Observer和Client。Subject对象带有绑定观察者到Client对象和从Client对象解绑观察者的方法。我们创建Subject类、Observer抽象类和扩展了抽象类Observer的实体类。

作用

  1. 一个对象状态更新,其他依赖对象收到通知和自动更新的机制。
  2. 实现模块化分离,实现主题与观察者交互对象之间的松耦合。
    1)观察者定义了对象之间一对多的关系。
    2)被观察者(主题)用一个共同的接口来更新观察者。
    3)观察者和被观察者用松耦合方式结合,被观察者不知道观察者的细节,只知道观察者实现了观察者接口。

实现步骤

  1. 创建观察者observer基础接口,包含主题和更新方法
  2. 创建主题subject抽象类,包含observer列表以及添加和删除方法
  3. 创建具体的主题类,实现通知方法,发布通知时轮询通知全部观察者
  4. 创建多个具体观察者,与主题关联,并实现自己的更新方法
  5. 客户调用时先声明主题,再将观察者分别添加到主题,当主题发布通知时,观察者自动更新

UML

 

Java代码

观察者接口

// ObserverAPI.java 观察者接口,Java 9已经默认支持Observer接口
// 这里避免冲突采取ObserverAPI命名
public interface ObserverAPI {
public Subject subject = null;
public void update(String content);
}

具体观察者

// ConcreteObserver.java 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
// 不同的观察者也可以对应多个主题
public class ConcreteObserver implements ObserverAPI { public Subject subject; // 给观察者绑定主题,同时把观察者添加到主题列表
public ConcreteObserver(Subject subject) {
this.subject = subject;
this.subject.register((ObserverAPI) this);
} // 观察者发出更新通知,不用单独告诉订阅者,由订阅者自行监听
public void update(String content) {
System.out.println(String.format("%s::update() [subject.name = %s content = %s]",
this.getClass().getName(),
this.subject.getName(), content));
}
}
// ConcreteObserver2.java 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
// 不同的观察者可以对应不同的主题。
public class ConcreteObserver2 implements ObserverAPI { // 这里没有在构造器就绑定某个主题,而是从客户角度去注册观察者
public ConcreteObserver2() {
} // 观察者发出更新通知,观察者自行监听
public void update(String content) {
System.out.println(String.format("%s::update() [content = %s]",
this.getClass().getName(), content));
}
}

抽象主题类

// Subject.java 定义抽象主题类或者接口,供具体主题类继承
public abstract class Subject {
private String name;
// protected Set<ObserverAPI> observers = new HashSet<>();
protected List<ObserverAPI> observers = new ArrayList<>(); public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public void register(ObserverAPI observer) {
System.out.println(this.getClass().getName() + "::register() [observer = " + observer.getClass().getSimpleName() + "]");
observers.add(observer);
} public void remove(ObserverAPI observer) {
observers.remove(observer);
} // 通知由具体类来实现逻辑
public abstract void notify(String name);
}

具体主题类

// ConcreteSubject.java 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。
public class ConcreteSubject extends Subject { public ConcreteSubject(String name) {
this.setName(name);
} // 不同的主题类有自己的通知方法,批量通知绑定的观察者
@Override
public void notify(String content) {
System.out.println(this.getClass().getName() + "::notify() [content = " + content + "]");
for (Object observer : this.observers) {
((ObserverAPI) observer).update(content);
} } }

测试调用

    /**
* 观察者模式应用非常广泛,主要是观察者提前绑定到发布者
* 当发布者发布消息时,批量广播通知,而无需逐一通知
* 观察者监听到消息后自己决定采取哪一种行为
*/ // 定义一个主题,也就是发布者
Subject concreteSubject = new ConcreteSubject("subject1");
// 再声明观察者,通过构造器注册到主题上
ObserverAPI observer1 = new ConcreteObserver(concreteSubject); // 也可以单独给主题注册一个新的观察者
concreteSubject.register(new ConcreteObserver2());
// 可以移除观察者对象,可以打开注释试下
// concreteSubject.remove(observer1); // 主题开始发布新通知,各观察者自动更新
concreteSubject.notify("hello, this is broadcast.");

Python代码

观察者接口

# ObserverAPI.py 观察者抽象父类,定义一些公共方法
class ObserverAPI: def __init__(self, name):
self.name = name # 观察者发出更新通知,观察者自行监听
def update(self, content):
print(self.__class__.__name__ + '::update() [content = ' + content + ']') def set_name(self, name):
self.name = name

具体观察者

# ConcreteObserver.py 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
# 不同的观察者也可以对应多个主题
from src.ObserverAPI import ObserverAPI # 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
# 不同的观察者也可以对应多个主题 class ConcreteObserver(ObserverAPI):
# 给观察者绑定主题,同时把观察者添加到主题列表
def __init__(self, subject, name):
ObserverAPI.__init__(self, name) # python3支持的父类调用
# super(ConcreteObserver, self).__init__(name)
# super().__init__(name) self.subject = subject
subject.register(self) # 观察者发出更新通知,不用单独告诉订阅者,由订阅者自行监听
def update(self, content):
print(self.__class__.__name__ + '::update() [subject.name = ' +
self.subject.name + ' content = ' + content + ']')
# ConcreteObserver2.py 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
# 不同的观察者可以对应不同的主题。
from src.ObserverAPI import ObserverAPI # 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
# 不同的观察者可以对应不同的主题。
class ConcreteObserver2(ObserverAPI):
# 这里没有在构造器就绑定某个主题,而是从客户角度去注册观察者
# 观察者发出更新通知,观察者自行监听 # def update(self, content):
# print(self.__class__.__name__ + '::update() [content = ' + content +']') pass

抽象主题类

# Subject.py 定义抽象主题类或者接口,供具体主题类继承
class Subject: def __init__(self, name):
self.name = name
self.observers = [] def get_name(self):
return self.name def set_name(self, name):
self.name = name def register(self, observer):
print(self.__class__.__name__ + '::register() [observer = ' +
observer.__class__.__name__ + ']') self.observers.append(observer) def remove(self, observer):
self.observers.remove(observer) # 通知由具体类来实现逻辑
def notify(self, name):
pass

具体主题类

// ConcreteSubject.py 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。
from src.Subject import Subject # 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。
class ConcreteSubject(Subject):
# 不同的主题类有自己的通知方法,批量通知绑定的观察者
def notify(self, content):
print(self.__class__.__name__ + '::notify() [content = ' + content +
']')
for observer in self.observers:
observer.update(content)

测试调用

import sys
import os os_path = os.getcwd()
sys.path.append(os_path) from src.ConcreteSubject import ConcreteSubject
from src.ConcreteObserver import ConcreteObserver
from src.ConcreteObserver2 import ConcreteObserver2 def test():
'''
* 观察者模式应用非常广泛,主要是观察者提前绑定到发布者
* 当发布者发布消息时,批量广播通知,而无需逐一通知
* 观察者监听到消息后自己决定采取哪一种行为
''' # 定义一个主题,也就是发布者
concrete_subject = ConcreteSubject('subject1')
# 再声明观察者,通过构造器注册到主题上
observer1 = ConcreteObserver(concrete_subject, 'observer1')
# 也可以单独给主题注册一个新的观察者
observer2 = ConcreteObserver2('observer2')
concrete_subject.register(observer2)
# 可以移除观察者对象
# concrete_subject.remove(observer1) # 主题开始发布新通知,各观察者自动更新
concrete_subject.notify('hello, this is broadcast.') if __name__ == '__main__':
print(__file__)
print("test start:")
test()

更多语言版本

不同语言实现设计模式:https://github.com/microwind/design-pattern

【观察者设计模式详解】C/Java/JS/Go/Python/TS不同语言实现的更多相关文章

  1. [ 转载 ] Java开发中的23种设计模式详解(转)

    Java开发中的23种设计模式详解(转)   设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类 ...

  2. Java温故而知新(5)设计模式详解(23种)

    一.设计模式的理解 刚开始“不懂”为什么要把很简单的东西搞得那么复杂.后来随着软件开发经验的增加才开始明白我所看到的“复杂”恰恰就是设计模式的精髓所在,我所理解的“简单”就是一把钥匙开一把锁的模式,目 ...

  3. android java 设计模式详解 Demo

    android java 设计模式详解 最近看了一篇设计模式的文章,深得体会,在此基础我将每种设计模式的案例都写成Demo的形式,方便读者研究学习, 首先先将文章分享给大家: 设计模式(Design ...

  4. JAVA设计模式简介及六种常见设计模式详解

    一.什么是设计模式                                                                                           ...

  5. Javascript设计模式详解

    Javascript常用的设计模式详解 阅读目录 一:理解工厂模式 二:理解单体模式 三:理解模块模式 四:理解代理模式 五:理解职责链模式 六:命令模式的理解: 七:模板方法模式 八:理解javas ...

  6. Javascript常用的设计模式详解

    Javascript常用的设计模式详解 阅读目录 一:理解工厂模式 二:理解单体模式 三:理解模块模式 四:理解代理模式 五:理解职责链模式 六:命令模式的理解: 七:模板方法模式 八:理解javas ...

  7. iOS中MVC等设计模式详解

    iOS中MVC等设计模式详解 在iOS编程,利用设计模式可以大大提高你的开发效率,虽然在编写代码之初你需要花费较大时间把各种业务逻辑封装起来.(事实证明这是值得的!) 模型-视图-控制器(MVC)设计 ...

  8. javascript设计模式详解之策略模式

    接上篇命令模式来继续看下js设计模式中另一种常用的模式,策略模式.策略模式也是js开发中常用的一种实例,不要被这么略显深邃的名字给迷惑了.接下来我们慢慢看一下. 一.基本概念与使用场景: 基本概念:定 ...

  9. PHP常用设计模式,PHP常用设计模式详解,PHP详解设计模式,PHP设计模式

    PHP常用设计模式详解 单例模式: php交流群:159789818 特性:单例类只能有一个实例 类内__construct构造函数私有化,防止new实例 类内__clone私有化,防止复制对象 设置 ...

  10. javascript设计模式详解之命令模式

    每种设计模式的出现都是为了弥补语言在某方面的不足,解决特定环境下的问题.思想是相通的.只不过不同的设计语言有其特定的实现.对javascript这种动态语言来说,弱类型的特性,与生俱来的多态性,导致某 ...

随机推荐

  1. drf认证与权限

    1 认证 # 登录认证--->某个接口必须登录后才能房屋# 登录接口--->登录成功返回随机字符串--->携带随机字符串[认证] 通过,再继续访问某个接口# 读APIView源码时, ...

  2. Post 大小超出允许的限制

    原因大体找到了: 除了项目最外层有web.config, 在 Pages页面下还有一个web.config配置文件, 遍历顺序为 :aspx 页面同级目录-->逐级父级目录-->根目录-- ...

  3. hihocoder 1066 并查集

    /**/ #include <cstdio> #include <cstring> #include <cmath> #include <cctype> ...

  4. 用requests库的get( )函数访问搜狗网站20次

    代码: import requests from bs4 import BeautifulSoup def getHTMLText(url): try: r=requests.get(url,time ...

  5. 使用npm包API Promise化

  6. Unity_飞机大战记录总结

    记录步骤:win+R→PSR.exe 一.竖屏设置 分辨率设为9:16 二.主控脚本 添加一个空节点,命名"游戏主控" 新建游戏的主控脚本,命名为MyGame.cs,方便管理(即, ...

  7. 2020.11.24 javaScript匿名函数的使用

    参考链接:http://www.voidcn.com/article/p-ngxxuegm-bmv.html 匿名函数: 函数表达式中创建的函数叫做匿名函数,也就是没有函数名的函数. 自执行函数: 创 ...

  8. Linux挂载SMB共享文件夹

    mount -t cifs -o username=xxxx,password=xxxx //PATH/TO/Shared/Folder /mount/point

  9. MySQL Mock大量数据做查询响应测试

    上个迭代版本发布后,生产环境业务同事反馈仓配订单查询的页面加载时间过长. 因为页面原来是有的,这次开发是在原来基础上改的,因此没有额外做性能.测试环境只调用接口请求了少量数据去验证功能.在对比该迭代添 ...

  10. 【Visual Leak Detector】在 QT 中使用 VLD(方式二)

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记. 目录 说明 1. 使用前的准备 2. 在 QT 中使用 VLD 2.1 在项目 .pro 文件中指明路径 2.2 在 main.cpp ...