观察者模式 (Observer Pattern)

ops/2024/11/30 8:05:40/

文章目录

      • 观察者模式 (Observer Pattern)
      • 原理
      • 优点
      • 缺点
      • 示例代码
        • 场景描述
        • 1. 定义观察者接口
        • 2. 定义目标接口
        • 3. 实现具体目标类
        • 4. 实现具体观察者类
        • 5. 客户端代码
        • 输出结果
      • UML 类图
      • 使用场景
      • 优化与扩展
      • 小结

观察者模式 (Observer Pattern)

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


原理

  1. 核心思想
    • 当目标对象(Subject)发生变化时,通知所有观察者(Observer)。
    • 将目标与观察者之间的依赖关系分离,使得两者可以独立变化。
  2. 适用场景
    • 一个对象的变化需要通知其他对象。
    • 不希望目标对象和观察者之间存在紧耦合关系。
  3. 参与角色
    • Subject(目标/被观察者)
      • 保存观察者对象的列表。
      • 提供注册、移除观察者的方法。
      • 当状态发生改变时,通知所有观察者。
    • Observer(观察者)
      • 提供更新接口,供目标调用。
    • ConcreteSubject(具体目标)
      • 实现具体的状态逻辑。
    • ConcreteObserver(具体观察者)
      • 实现更新接口,定义具体更新行为。

优点

  1. 解耦:观察者和目标分离,可以独立扩展。
  2. 动态响应:目标状态变化时,观察者可以动态收到通知。
  3. 复用性强观察者模式非常灵活,适用于各种场景。

缺点

  1. 通知机制复杂性:通知过程可能产生较高的时间开销。
  2. 可能出现循环依赖:当观察者也会触发目标变化时,需防止死循环。
  3. 难以控制通知顺序:观察者的调用顺序由实现决定。

示例代码

场景描述

以新闻发布系统为例,新闻发布者是目标,订阅用户是观察者。当新闻发布时,所有订阅用户都会收到通知。


1. 定义观察者接口
// 观察者接口
public interface Observer {void update(String message); // 接收目标的更新通知
}

2. 定义目标接口
// 目标接口
public interface Subject {void registerObserver(Observer observer); // 注册观察者void removeObserver(Observer observer);   // 移除观察者void notifyObservers();                   // 通知所有观察者
}

3. 实现具体目标类
import java.util.ArrayList;
import java.util.List;// 具体目标类
public class NewsPublisher implements Subject {private List<Observer> observers; // 保存观察者列表private String news;              // 新闻内容public NewsPublisher() {observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(news); // 通知每个观察者}}// 更新新闻并通知观察者public void setNews(String news) {this.news = news;notifyObservers();}
}

4. 实现具体观察者类
// 具体观察者
public class Subscriber implements Observer {private String name;public Subscriber(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + " received news: " + message);}
}

5. 客户端代码
public class ObserverPatternExample {public static void main(String[] args) {NewsPublisher publisher = new NewsPublisher();// 创建观察者Observer alice = new Subscriber("Alice");Observer bob = new Subscriber("Bob");Observer charlie = new Subscriber("Charlie");// 注册观察者publisher.registerObserver(alice);publisher.registerObserver(bob);// 发布新闻publisher.setNews("Breaking News: Observer Pattern Explained!");// 移除观察者publisher.removeObserver(alice);// 再次发布新闻publisher.setNews("Update: Observer Pattern is Easy!");}
}

输出结果
Alice received news: Breaking News: Observer Pattern Explained!
Bob received news: Breaking News: Observer Pattern Explained!
Bob received news: Update: Observer Pattern is Easy!

UML 类图

        +------------------+|    Subject       |<-------------------++------------------+                    || + registerObserver()|                 || + removeObserver()  |                 || + notifyObservers() |                 |+------------------+                    |^                               ||                               |
+-----------------------+       +-----------------------+
|   NewsPublisher       |       |       Observer        |
+-----------------------+       +-----------------------+
| - observers : List    |       | + update(message)     |
| + setNews(news)       |       +-----------------------+
| + notifyObservers()   |                 ^
+-----------------------+                 ||                         |+-----------------------+   +-----------------------+|      Subscriber        |   |      Subscriber        |+-----------------------+   +-----------------------+| - name : String        |   | - name : String        || + update(message)      |   | + update(message)      |+-----------------------+   +-----------------------+

使用场景

  1. 事件监听:GUI 开发中,按钮点击事件的监听。
  2. 消息订阅:发布/订阅模式,如 RabbitMQ、Kafka。
  3. 实时更新:如股票价格、天气更新。

优化与扩展

  1. 使用 Java 内置类
    • Java 提供了 Observable 类和 Observer 接口。
    • Observable 已被标记为过时,推荐自定义实现。
  2. 多线程支持
    • 使用线程安全的集合 CopyOnWriteArrayList 避免并发问题。
  3. 结合其他模式
    • 中介者模式 结合,简化复杂的观察者关系。

小结

  • 观察者模式在事件驱动系统中非常常用。
  • 它解耦了目标和观察者,但需要注意性能问题。
  • 可结合其他模式(如工厂模式)简化观察者的管理和创建。

http://www.ppmy.cn/ops/137874.html

相关文章

电子应用设计方案-29:智能云炒菜系统方案设计

智能云炒菜系统方案设计 一、系统概述 本智能云炒菜系统旨在为用户提供便捷、高效、个性化的烹饪体验&#xff0c;结合云技术实现远程控制、食谱分享、智能烹饪流程优化等功能。 二、系统组成 1. 炒菜锅主体 - 高品质不粘锅内胆&#xff0c;易于清洁和维护。 - 加热装置&#x…

WinFrom调用webapi接口另一个方法及其应用实例

1.调用接口方法 代码如下&#xff1a; public class WebAPI{#region WebAPI调用 public async Task<string> Call_Webapi(string Url, string Json) //url传入的是接口名称&#xff0c;json传入的是接口参数{string responseBody string.Empty; //responseBod…

DM达梦管理工具拖出空白区块,无法关闭

1. 出现问题&#xff1a;DM达梦管理工具拖出空白区块&#xff0c;无法关闭。 2. 解决方法 新建查询页&#xff0c;把查询页拖到空白区块里&#xff0c;完全覆盖空白区块。之后空白区块会变成查询页&#xff0c;右上角会出现叉号&#xff0c;点击叉号关闭就行。 3. 后记 达梦…

用户密码存储方式的发展史

文章目录 一、裸奔时代——纯文本存储二、远古时代&#xff1a;加密存储三、近代&#xff1a;单向哈希算法四、哈希加盐&#xff1a;穿上迷彩服两种加盐做法1. 随机生成盐并保存2. 固定算法生成盐并且不保存 五、21世纪&#xff1a;自适应单项函数未来趋势 一、裸奔时代——纯文…

Docker for Everyone Plus——Unbreakable!

修改一下telnet的端口配置&#xff0c;访问第二小问&#xff0c;sudo -l命令允许提权执行的命令&#xff1a; 发现多了这两个限制--security-optno-new-privileges&#xff0c;表明docker run命令必须带上--security-optno-new-privileges参数&#xff0c;这可以防止通过suid机…

154. tweenjs相机运动动画

下面给大家讲解如何通过tweenjs实现threejs相机动画&#xff0c;具体说就是使用tweenjs改变相机的位置camera.position和视线方向。 相机飞行动画(从一个点飞到另一个点) 引入tweenjs&#xff0c;并在requestAnimationFrame动画中执行TWEEN.update();更新。 import TWEEN fr…

CSS 属性计算过程

CSS 属性计算过程 你是否了解 CSS 的属性计算过程呢&#xff1f; 有的同学可能会讲&#xff0c;CSS属性我倒是知道&#xff0c;例如&#xff1a; p{color : red; }上面的 CSS 代码中&#xff0c;p 是元素选择器&#xff0c;color 就是其中的一个 CSS 属性。 但是要说 CSS 属…

【Docker】部署nginx

docker部署nginx docker部署nginx镜像加速器1、拉取nginx镜像2、创建nginx容器3、浏览器访问 docker部署nginx 镜像加速器 备注&#xff1a;阿里云镜像加速地址 https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors可用的镜像源&#xff1a; https://https://reg…