【设计模式系列笔记】观察者模式

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: 观察者模式是一种设计模式,它允许一个对象(主题)维护一组依赖于它的对象(观察者)并在状态发生变化时通知它们。这种模式属于行为型模式。在观察者模式中,主题是被观察的对象,而观察者是依赖于主题的对象,当主题的状态发生变化时,所有注册的观察者都会得到通知并自动更新。

1. 观察者模式

观察者模式是一种设计模式,它允许一个对象(主题)维护一组依赖于它的对象(观察者)并在状态发生变化时通知它们。这种模式属于行为型模式。在观察者模式中,主题是被观察的对象,而观察者是依赖于主题的对象,当主题的状态发生变化时,所有注册的观察者都会得到通知并自动更新。

以下是观察者模式的主要角色:

  1. 主题(Subject): 主题是被观察的对象,它维护一组观察者并通知它们状态的变化。主题可以有添加、删除观察者的方法。
  2. 观察者(Observer): 观察者是依赖于主题的对象,当主题的状态发生变化时,观察者得到通知并执行相应的操作。

2. 关键思想

观察者模式的关键思想是定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象。当主题对象的状态发生变化时,所有依赖于它的观察者都会得到通知并被自动更新。这种方式实现了对象之间的松耦合,使得主题和观察者可以独立变化而互不影响。

以下是观察者模式的关键思想:

  1. 主题(Subject)维护观察者列表: 主题对象内部维护了一个观察者列表,该列表存储了所有依赖于该主题的观察者对象。
  2. 观察者(Observer)定义更新接口: 观察者对象实现一个更新接口,该接口通常包含一个参数,用于接收主题对象传递的状态信息。
  3. 注册和通知机制: 观察者通过注册(添加)到主题对象,主题对象在状态变化时通过通知机制来通知所有观察者。
  4. 松耦合: 主题和观察者之间是松耦合的关系,主题并不需要知道观察者的具体类型,而是依赖于观察者接口,这使得系统更容易扩展和维护。
  5. 广播通知: 当主题状态发生变化时,它会向所有注册的观察者广播通知,观察者接收通知后执行相应的更新操作。

观察者模式的应用场景包括但不限于:用户界面事件处理、分布式事件系统、消息队列系统等。这种模式能够有效地实现对象间的解耦,提高系统的灵活性和可维护性。

3. 实现方式

观察者模式的实现方式主要涉及到主题(Subject)和观察者(Observer)的接口定义以及它们的具体实现。以下是一个基本的观察者模式的实现方式:

  1. 主题接口(Subject):
// 主题接口,被观察者
public interface Subject {
    // 添加观察者到观察者列表
    void addObserver(Observer observer);
    // 从观察者列表中移除观察者
    void removeObserver(Observer observer);
    // 通知所有注册的观察者,主题状态发生变化
    void notifyObservers();
}
  1. 观察者接口(Observer):
// 观察者接口,用于定义观察者的通用行为
public interface Observer {
    // 当被观察的主题状态发生变化时,观察者会被通知并调用该方法进行更新
    void update(String state); // 通常包含一个参数,表示主题状态的变化
}
  1. 具体主题类(ConcreteSubject):
import java.util.ArrayList;
import java.util.List;
// 具体主题类,实现了Subject接口
public class ConcreteSubject implements Subject {
    // 观察者列表,存储所有注册的观察者
    private List<Observer> observers = new ArrayList<>();
    // 主题的状态
    private String state;
    // 添加观察者到观察者列表
    public void addObserver(Observer observer) {
        observers.add(observer);
    }
    // 从观察者列表中移除观察者
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
    // 通知所有注册的观察者,主题状态发生变化
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(state);
        }
    }
    // 设置主题的状态,并通知观察者
    public void setState(String state) {
        this.state = state;
        notifyObservers();
    }
}

这个类实现了观察者模式的主题接口,包括添加、移除观察者以及通知观察者的方法。当主题状态发生变化时,通过 notifyObservers 方法通知所有注册的观察者,并调用它们的 update 方法进行状态更新。

  1. 具体观察者类(ConcreteObserver):
// 具体观察者类,实现了Observer接口
public class ConcreteObserver implements Observer {
    // 观察者的状态,与主题的状态保持同步
    private String observerState;
    // 当被观察的主题状态发生变化时,更新观察者的状态
    public void update(String state) {
        observerState = state;
        System.out.println("Observer state updated: " + observerState);
    }
}

这个类实现了观察者模式的观察者接口,包括了 update 方法,用于在被观察的主题状态发生变化时更新观察者的状态。在这个示例中,更新操作是简单地将观察者的状态与主题的状态保持同步,并输出更新信息到控制台。

  1. 示例用法:
// 示例类,演示观察者模式的使用
public class ObserverPatternExample {
    public static void main(String[] args) {
        // 创建一个具体主题对象
        ConcreteSubject subject = new ConcreteSubject();
        // 创建两个具体观察者对象
        ConcreteObserver observer1 = new ConcreteObserver();
        ConcreteObserver observer2 = new ConcreteObserver();
        // 将观察者注册到主题对象
        subject.addObserver(observer1);
        subject.addObserver(observer2);
        // 设置主题的状态,并触发观察者的更新
        subject.setState("New State 1");
        // Output:
        // Observer state updated: New State 1
        // Observer state updated: New State 1
        // 设置主题的状态,并触发观察者的更新
        subject.setState("New State 2");
        // Output:
        // Observer state updated: New State 2
        // Observer state updated: New State 2
    }
}

这个示例创建了一个具体主题 ConcreteSubject,以及两个具体观察者 ConcreteObserver。观察者被注册到主题上,然后通过设置主题的状态,触发了观察者的更新操作。每个观察者通过 update 方法接收并处理主题的状态变化,输出了相应的更新信息。

示例代码:

考虑一个社交媒体平台的通知系统。在这个例子中,社交媒体平台充当主题,而用户是观察者。当有新的消息、点赞、评论等活动发生时,社交媒体平台会通知所有关注者。

import java.util.ArrayList;
import java.util.List;
// 主题接口 - 社交媒体平台
interface SocialMediaPlatform {
    // 添加观察者到关注列表
    void addObserver(UserObserver observer);
    // 从关注列表中移除观察者
    void removeObserver(UserObserver observer);
    // 通知所有关注者,传递最新的社交媒体活动
    void notifyFollowers(String activity);
}
// 观察者接口 - 用户
interface UserObserver {
    // 当社交媒体平台发布新的活动时,更新用户的通知
    void update(String activity);
}
// 具体主题类 - 具体的社交媒体平台
class ConcreteSocialMediaPlatform implements SocialMediaPlatform {
    // 关注社交媒体平台的用户列表
    private List<UserObserver> followers = new ArrayList<>();
    // 最新的社交媒体活动
    private String latestActivity;
    // 添加用户到关注列表
    public void addObserver(UserObserver observer) {
        followers.add(observer);
    }
    // 从关注列表中移除用户
    public void removeObserver(UserObserver observer) {
        followers.remove(observer);
    }
    // 通知所有关注者,传递最新的社交媒体活动
    public void notifyFollowers(String activity) {
        latestActivity = activity;
        for (UserObserver follower : followers) {
            follower.update(latestActivity);
        }
    }
    // 模拟有新的社交媒体活动发生,并通知所有关注者
    public void postActivity(String activity) {
        notifyFollowers(activity);
    }
}
// 具体观察者类 - 用户
class User implements UserObserver {
    // 用户名
    private String username;
    // 构造函数,初始化用户对象时需要指定用户名
    public User(String username) {
        this.username = username;
    }
    // 当社交媒体平台发布新的活动时,更新用户的通知
    public void update(String activity) {
        System.out.println(username + " 收到通知: " + activity);
    }
}
// 示例
public class SocialMediaPlatformExample {
    public static void main(String[] args) {
        // 创建具体社交媒体平台对象
        ConcreteSocialMediaPlatform socialMediaPlatform = new ConcreteSocialMediaPlatform();
        // 创建两个用户
        User user1 = new User("Alice");
        User user2 = new User("Bob");
        // 用户关注社交媒体平台
        socialMediaPlatform.addObserver(user1);
        socialMediaPlatform.addObserver(user2);
        // 模拟社交媒体平台发布新的活动
        socialMediaPlatform.postActivity("管理员发布了新的动态");
        // 输出:
        // Alice 收到通知: 管理员发布了新的动态
        // Bob 收到通知: 管理员发布了新的动态
    }
}

在这个例子中,ConcreteSocialMediaPlatform 充当主题,而 User 充当观察者。当社交媒体平台发布新的活动时,所有关注者都会收到通知并更新。这个例子展示了观察者模式在社交媒体通知系统中的应用。

要点:

  1. 定义对象间一对多的依赖关系:观察者模式建立了一个一对多的依赖关系,使得一个主题对象的状态发生变化时,所有依赖于它的观察者都会得到通知并自动更新。
  2. 松耦合:主题和观察者之间的关系是松耦合的。主题不需要知道观察者的具体类,只需要知道它们实现了相同的接口。
  3. 适用于多个对象关心一个对象状态的情况:当一个对象的状态发生变化需要通知其他多个对象时,观察者模式是一个合适的选择。
  4. 支持广播通信:主题对象可以轻松地通知所有注册的观察者,实现了广播通信的能力。

注意事项:

  1. 避免循环依赖:注意在设计中避免循环依赖,以免引起不必要的复杂性。
  2. 避免观察者影响主题:观察者的更新操作应该尽量简洁,不要对主题对象产生太大的影响。
  3. 性能问题:如果观察者的数量很大,或者观察者的更新操作比较耗时,可能会影响整体性能。可以考虑异步方式来处理观察者的更新。
  4. Java内置支持:在Java中,观察者模式已经内置在java.util包中的ObserverObservable类中,但注意Observable类在Java 9及更高版本中被标记为过时(deprecated),建议使用其他方式实现观察者模式,比如使用接口和列表。

优点:

  1. 松耦合:主题和观察者之间是松耦合的关系,使得它们可以独立演化。主题对象不需要知道观察者的具体实现,只需要知道观察者实现了相应的接口。
  2. 扩展性好:可以在系统中方便地增加新的观察者,无需修改主题的代码。
  3. 可重用性:可以重用已有的观察者和主题类,使得系统更加灵活。
  4. 广播通信:主题对象可以方便地通知所有注册的观察者,实现广播通信。

缺点:

  1. 通知顺序不确定:观察者收到通知的顺序是不确定的,可能会导致一些潜在的问题。
  2. 可能导致性能问题:如果观察者的数量很大,或者观察者的更新操作比较耗时,可能会影响整体性能。
  3. 观察者感知主题状态的方式有限:观察者模式只能让观察者知道主题状态发生了变化,但不能获取变化的具体内容,除非主题对象在通知时主动传递。

应用场景:

  1. 事件处理系统:当一个事件发生时,多个对象需要对该事件进行响应,例如GUI界面的按钮、菜单、窗口等。
  2. 分布式系统中的消息推送:在分布式系统中,观察者模式可用于实现消息的推送,当一个节点的状态发生变化时,通知其他节点。
  3. 报警系统:报警系统中,监控器可以是观察者,被监测的设备是主题,当设备发生异常时,监控器收到通知并进行处理。
  4. 多层架构中的事件处理:在多层架构中,观察者模式可以用于处理不同层之间的事件通知,如业务逻辑层通知数据访问层进行数据更新。
  5. 发布-订阅模型:观察者模式可以用于实现发布-订阅模型,其中订阅者(观察者)订阅某个主题,当主题发生变化时,通知所有订阅者。
目录
相关文章
|
13天前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
28 1
|
1月前
|
设计模式 消息中间件 存储
跟着GPT学设计模式之观察者模式
观察者模式是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,其依赖对象都能够收到通知并自动更新。一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。
18 1
|
26天前
|
设计模式 网络协议 Java
技术笔记:Reactor设计模式
技术笔记:Reactor设计模式
|
26天前
|
设计模式 存储 安全
技术好文共享:设计模式笔记:单件模式(Singleton)
技术好文共享:设计模式笔记:单件模式(Singleton)
12 0
|
27天前
|
设计模式 存储 Java
Java设计模式之观察者模式详解
Java设计模式之观察者模式详解
|
28天前
|
设计模式
观察者模式-大话设计模式
观察者模式-大话设计模式
9 0
|
1月前
|
设计模式 存储
行为型设计模式之观察者模式
行为型设计模式之观察者模式
|
2月前
|
设计模式 Java
Java一分钟之-设计模式:观察者模式与事件驱动
【5月更文挑战第17天】本文探讨了Java中实现组件间通信的观察者模式和事件驱动编程。观察者模式提供订阅机制,当对象状态改变时通知所有依赖对象。然而,它可能引发性能问题、循环依赖和内存泄漏。代码示例展示了如何实现和避免这些问题。事件驱动编程则响应用户输入和系统事件,但回调地狱和同步/异步混淆可能造成困扰。JavaFX事件驱动示例解释了如何处理事件。理解这两种模式有助于编写健壮的程序。
37 1
|
2月前
|
设计模式 JavaScript 前端开发
js设计模式-观察者模式与发布/订阅模式
观察者模式和发布/订阅模式是JavaScript中的两种设计模式,用于处理对象间的通信和事件处理。观察者模式中,一个主题对象状态改变会通知所有观察者。实现包括定义主题和观察者对象,以及在主题中添加、删除和通知观察者的功能。发布/订阅模式则引入事件管理器,允许发布者发布事件,订阅者通过订阅接收通知。
|
1月前
|
设计模式 前端开发 NoSQL
设计模式第八讲:观察者模式和中介者模式详解
 定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
179 0