设计模式学习之——观察者模式

news/2024/11/30 7:44:52/

观察者模式是一种行为型设计模式,它用于在对象之间建立一对多的依赖关系。

一、定义与角色

  • 定义
    • 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
  • 主要角色
    • 主题(Subject):也称为被观察者,它维护一个观察者列表,并提供注册、删除和通知观察者的方法。
    • 观察者(Observer):对主题的状态变化感兴趣,并在状态变化时做出响应。它定义了一个更新方法,用于接收和处理主题的通知。
    • 具体主题(ConcreteSubject):实现了主题接口,维护了观察者的集合,并在状态变化时通知所有观察者。
    • 具体观察者(ConcreteObserver):实现了观察者接口,并对主题的状态变化做出具体的反应。

二、工作原理

  1. 注册观察者:观察者通过调用主题的注册方法,将自己添加到主题的观察者列表中。
  2. 状态变化通知:当主题的状态发生变化时,它会遍历观察者列表,并调用每个观察者对象的更新方法,将新的状态传递给观察者。
  3. 观察者响应:观察者对象接收到主题的通知后,执行相应的操作,以便响应状态的变化。

三、优缺点

优点: 

  1. 解耦性:主题和观察者之间是松耦合的,它们之间通过抽象的接口进行通信,使得它们可以独立地进行修改和扩展。
  2. 可扩展性:可以方便地增加新的观察者,而无需修改主题的代码。
  3. 灵活性观察者模式支持同步和异步通知,观察者可以在不同的线程中执行更新操作。
  4. 规范性观察者模式定义了主题和观察者之间的一套规范,使得代码更具可读性和可维护性。

缺点/注意事项:

  1. 循环依赖:避免在观察者和主题之间建立循环依赖关系,否则可能导致系统崩溃。
  2. 性能问题:当观察者数量非常多时,通知操作可能会非常耗时。可以考虑使用异步通知、事件总线等优化方法。
  3. 内存泄漏:如果观察者对象不再需要,确保从主题的观察者列表中删除它们,以避免内存泄漏。
  4. 线程安全:在多线程环境中,确保对观察者列表的访问是线程安全的。可以使用同步机制、并发集合等来实现。

四、应用场景

  1. 数据变化通知:当对象的状态发生变化,需要通知多个对象进行更新时,可以使用观察者模式。例如,股票市场的系统中,投资者需要实时获取股票价格的变化。
  2. 分布式事件系统:需要将事件从一个组件广播到多个组件时,可以使用观察者模式。例如,GUI框架中的事件处理机制,用户的操作需要通知多个界面组件。
  3. 系统状态监控:当系统的状态变化需要被监控时,可以使用观察者模式。例如,应用程序的日志系统、系统监控工具等。

五、示例代码

以下是一个简单的观察者模式示例代码,用于演示其工作原理:

// 主题接口
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}// 具体主题类
public class ConcreteSubject implements Subject {private List<Observer> observers = new LinkedList<>();private int state;public void setState(int state) {this.state = state;notifyObservers();}@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {observers.forEach(observer -> observer.update(state));}
}// 观察者接口
public interface Observer {void update(int state);
}// 具体观察者类
public class ConcreteObserver implements Observer {private String name;public ConcreteObserver(String name) {this.name = name;}@Overridepublic void update(int state) {System.out.println(name + "接收到了更新请求,新的状态:" + state);}
}// 测试类
public class ObserverPatternExample {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer observer1 = new ConcreteObserver("观察者1号");Observer observer2 = new ConcreteObserver("观察者2号");subject.registerObserver(observer1);subject.registerObserver(observer2);subject.setState(10); // 输出:观察者1号接收到了更新请求,新的状态:10;观察者2号接收到了更新请求,新的状态:10}
}

六、与发布-订阅模式的比较

观察者模式和发布-订阅模式都是软件设计模式,用于实现对象间的一对多依赖关系,但它们之间存在一些关键的区别。以下是对这两种模式的详细比较:

定义与特点
  • 观察者模式

    • 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
    • 主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
    • 观察者和主题之间是抽象耦合的,即它们之间通过接口或抽象类进行交互,而不是直接依赖具体实现。
  • 发布-订阅模式

    • 也称为发布/订阅模式或消息传递模式。
    • 在这种模式下,消息的发送者(发布者)和接收者(订阅者)不需要建立直接的联系,也不需要知道对方的存在。
    • 消息的传递通过一个被称为代理(Broker)或调度中心的中间角色来完成。发布者将消息发布到代理上,而订阅者则从代理订阅感兴趣的消息。
结构与角色
  • 观察者模式

    • Subject(主题/被观察者):维护一个观察者列表,提供注册、删除和通知观察者的方法。
    • Observer(观察者):定义了一个更新接口,用于接收来自主题的通知。
    • ConcreteSubject(具体主题):实现Subject接口,维护观察者列表,并在状态变化时调用所有观察者的更新方法。
    • ConcreteObserver(具体观察者):实现Observer接口,定义当接收到主题通知时的具体行为。
  • 发布-订阅模式

    • 发布者(Publisher):负责将消息发布到主题上。发布者一次只能向一个主题发送数据,且发布消息时无需关心订阅者是否在线。
    • 订阅者(Subscriber):通过订阅主题接收消息。订阅者可一次订阅多个主题,从而接收感兴趣的消息。
    • 代理(Broker):负责所有消息的路由和分发工作。代理接收到发布者的消息后,根据主题将其分发给相应的订阅者。
    • 主题(Topic):用于标识消息的类别。每个消息都包含一个主题,订阅者通过订阅主题来接收感兴趣的消息。
区别与联系
  • 区别

    • 耦合性观察者模式中,发布者与订阅者直接关联,发布者维护观察者列表,发布者状态变更通知订阅者。而在发布-订阅模式中,订阅者与发布者相互不了解,通过事件中心(代理)进行通信,实现了完全松耦合。
    • 角色数量观察者模式中只有两个主要角色:观察者和被观察者。而发布-订阅模式中则包含四个角色:发布者、订阅者、代理和主题。
    • 应用场景观察者模式多用于单个应用内部,而发布-订阅模式则更多的是一种跨应用的模式(cross-application pattern),如消息中间件等。
  • 联系

    • 两者都实现了对象间的一对多依赖关系,当某个对象的状态发生变化时,都会通知所有依赖的对象。
    • 两者都关注对象之间的通讯,并致力于保持对象间的低耦合和高协作性。

综上所述,观察者模式和发布-订阅模式在定义、结构、耦合性、角色数量和应用场景等方面都存在明显的区别。选择哪种模式取决于具体的应用需求和设计考虑。

最后,观察者模式是一种强大的设计模式,它能够在对象之间建立一对多的依赖关系,并自动通知观察者进行更新。然而,在实际应用中需要注意其潜在的性能问题和循环依赖问题


http://www.ppmy.cn/news/1551138.html

相关文章

记录学习《手动学习深度学习》这本书的笔记(一)

这几天在看同学推荐的这本书&#xff0c;看到了大概百来页&#xff0c;感觉受益匪浅&#xff0c;很多老师上课没讲到的东西在书里都有详细介绍。 平时上课做机器学习的作业实验基本都是调用模型直接套&#xff0c;没有去关注具体的方法&#xff0c;这本书都帮我巩固了很多知识…

深度学习之Mask-R-CNN

1.1 Mask-RCNN 的网络结构示意图 其中黑色部分为原来的Faster-RCNN&#xff0c;红色部分为在Faster网络上的修改&#xff1a;    1&#xff09;将ROI Pooling层替换成了ROIAlign&#xff1b;    2&#xff09;添加并列的FCN层&#xff08;Mask层&#xff09;&#xff1b;  …

彻底理解quadtree四叉树、Octree八叉树 —— 点云的空间划分的标准做法

1.参考文章&#xff1a; &#xff08;1&#xff09;https://www.zhihu.com/question/25111128 这里面的第一个回答&#xff0c;有一幅图&#xff1a; 只要理解的四叉树的构建&#xff0c;对于八叉树的构建原理类比方法完全一样&#xff1a;对于二维平面内的随机分布的这些点&…

百度在下一盘大棋

这两天世界互联网大会在乌镇又召开了。 我看到一条新闻&#xff0c;今年世界互联网大会乌镇峰会发布“2024 年度中国互联网企业创新发展十大典型案例”&#xff0c;百度文心智能体平台入选。 这个智能体平台我最近也有所关注&#xff0c;接下来我就来讲讲它。 百度在下一盘大棋…

【Oracle11g SQL详解】GROUP BY 和 HAVING 子句:分组与过滤

GROUP BY 和 HAVING 子句&#xff1a;分组与过滤 在 Oracle 11g 中&#xff0c;GROUP BY 子句用于根据一个或多个列对查询结果进行分组&#xff0c;而 HAVING 子句用于对分组后的结果进行过滤。这两者常结合聚合函数使用&#xff0c;用以实现复杂的数据统计和分析。本文将系统…

android 新增一个系统服务

android如何新增一个系统服务呢&#xff1f; 两个方案&#xff0c;一个是新增一个systemserver下的binder服务&#xff0c;一个是新增一个native的独立进程服务 这里选的是第一种&#xff0c;但坐下来感觉第二种更简单一些 因为systemserver要遵循的规则特别多 先是在startOthe…

【Python-Open3D学习笔记】004Mesh生成方法

PointCloud的TriangleMesh生成方法 本文使用csv文件作为点云数据&#xff08;csv —> DataFrame —> PointCloud&#xff09; 文章目录 PointCloud的TriangleMesh生成方法0. 数据展示1. Poisson表面重建2. Alpha Shapes3. 检测边界4. 小结 0. 数据展示 数据可视化方法…

heapq模块常用方法

heapq.heapify(x) import heapq# heapq.heapify(x)将列表x转换为堆 # 在堆数据结构中&#xff0c;最小元素总是位于根节点&#xff08;索引为 0&#xff09; # 这个操作是在原列表上进行的&#xff0c;会改变列表的顺序 my_list [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5] heapq.hea…