自学内容网 自学内容网

更灵活的对象之间的联动 - 观察者模式(Observer Pattern)

观察者模式(Observer Pattern)

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象的状态发生变化时,所有依赖于它的观察者对象都会收到通知并自动更新。这种模式特别适用于构建事件处理系统、订阅-发布机制等场景。

还是举个现实中的例子
还记得我们都在使用手机打电话的时候吗? 那个时候没有进入移动互联网,我们需要给谁打电话就需要自己把它们的电话存储到我们的手机上去。这导致了一个什么问题呢? 我们需要的服务很多,但是手机存储很多电话很不方便。 于是有了114查号台,当我们想知道某一些电话的时候,可以直接打电话给114查号台,这个查号台存储了各种各样的电话,它们把自己的信息注册到查号台,我们直接跟查号台沟通就可以得到这些信息。 这种就是我们本次要介绍的观察者模式(Observer Pattern)。

观察者模式(Observer Pattern)概述

观察者模式(Observer Pattern) 结构图

在这里插入图片描述

观察者模式(Observer Pattern)涉及的角色

  1. 主题(Subject):主题接口声明了添加、删除和通知观察者的方法。它可以是接口或抽象类,具体主题实现类会实现这些方法,并维护观察者的列表。
import java.util.ArrayList;
import java.util.List;

public abstract class Subject {
    private final List<Observer> observers = new ArrayList<>();

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}
  1. 具体主题(ConcreteSubject):具体主题实现了Subject接口,并包含有状态信息。当状态发生变化时,它会通知所有注册的观察者。
public class ConcreteSubject extends Subject {
    private String state;

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
        notifyObservers(); // 状态改变后通知所有观察者
    }
}
  1. 观察者(Observer):具体观察者实现了Observer接口,并在update()方法中定义了接收到通知后的具体行为。每个具体观察者都持有一个对具体主题的引用,以便它可以查询主题的状态。
public class ConcreteObserver implements Observer {
    private final ConcreteSubject subject;
    private String name;

    public ConcreteObserver(ConcreteSubject subject, String name) {
        this.subject = subject;
        this.name = name;
    }

    @Override
    public void update() {
        System.out.println(name + " received update: " + subject.getState());
    }
}

talk is cheap, show you my code

按照惯例,我们还是利用本节要介绍的设计模式来实现我们前面说的那个例子。

  1. 定义主题接口
    首先,我们定义一个Subject接口,它声明了添加、删除和通知观察者的方法。
import java.util.ArrayList;
import java.util.List;

public interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers();
}
  1. 实现具体主题
    然后,我们创建一个具体的主题类PhoneDirectory,它实现了Subject接口,并维护了一个电话号码列表。当电话号码发生变化时,它会通知所有注册的观察者。
public class PhoneDirectory implements Subject {
    private final List<Observer> observers = new ArrayList<>();
    private String phoneNumber;

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(phoneNumber);
        }
    }

    // 更新电话号码并通知观察者
    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        System.out.println("Phone number updated to: " + phoneNumber);
        notifyObservers();
    }
}
  1. 定义观察者接口
    接下来,我们定义一个Observer接口,它声明了一个update()方法,所有具体的观察者都需要实现这个方法以响应来自主题的通知。
public interface Observer {
    void update(String phoneNumber);
}
  1. 实现具体观察者
    现在,我们创建两个具体观察者类:DisplayBoard 和 MobileApp,它们实现了Observer接口,并在update()方法中定义了接收到通知后的具体行为。
public class DisplayBoard implements Observer {
    private final String name;

    public DisplayBoard(String name) {
        this.name = name;
    }

    @Override
    public void update(String phoneNumber) {
        System.out.println(name + " received update: Phone Number is " + phoneNumber);
    }
}

public class MobileApp implements Observer {
    private final String name;

    public MobileApp(String name) {
        this.name = name;
    }

    @Override
    public void update(String phoneNumber) {
        System.out.println(name + " received update: Phone Number is " + phoneNumber);
    }
}
  1. 客户端调用
    最后,在客户端代码中,我们将创建一个PhoneDirectory实例作为查号台,并为它添加一些观察者。然后,我们将模拟电话号码的更新过程。
public class Client {
    public static void main(String[] args) {
        // 创建查号台
        PhoneDirectory phoneDirectory = new PhoneDirectory();

        // 创建观察者
        Observer displayBoard = new DisplayBoard("Display Board");
        Observer mobileApp = new MobileApp("Mobile App");

        // 注册观察者到查号台
        phoneDirectory.attach(displayBoard);
        phoneDirectory.attach(mobileApp);

        // 模拟电话号码更新
        phoneDirectory.setPhoneNumber("123-456-7890"); // 第一次更新
        phoneDirectory.setPhoneNumber("098-765-4321"); // 第二次更新

        // 移除一个观察者
        phoneDirectory.detach(mobileApp);

        // 再次更新电话号码,此时只有displayBoard会收到通知
        phoneDirectory.setPhoneNumber("111-222-3333");
    }
}

总结

观察者模式的优点

  • 降低耦合度:观察者与主题之间的依赖是松散的,观察者只需要知道主题提供的接口,而不必了解主题的具体实现。
  • 支持广播通信:一个主题可以通知多个观察者,而且这些观察者之间相互独立,互不影响。
  • 易于扩展:新增加观察者非常容易,只需创建新的观察者实例并将其注册到主题即可,无需修改现有代码。
  • 符合开闭原则:系统可以在不改变原有代码的情况下引入新的功能(如新的观察者),这符合面向对象设计中的开闭原则(Open/Closed Principle)。

观察者模式的缺点:

  • 如果一个观察目标下面有很多观察者,通知所有观察者本身就会花费很多时间
  • 有可能在观察者与观察目标之前存在循环依赖的问题

观察者模式的应用场景

  • 事件驱动系统:如GUI框架中的按钮点击事件、窗口关闭事件等,观察者模式非常适合用来实现事件监听器。
  • 订阅-发布机制:例如新闻网站的用户订阅服务,每当有新文章发布时,所有订阅该频道的用户都会收到通知。
  • 缓存失效通知:在一个分布式系统中,当某个缓存项过期或被更新时,它可以通知所有依赖于该项的组件。
  • 游戏开发:玩家在游戏中完成任务或达到特定条件时,触发一系列事件,如获得奖励、解锁新内容等。

观察者模式提供了一种有效的方式来解耦对象之间的依赖,使得一个对象的状态变化可以自动通知到其他相关对象。它特别适用于那些需要实现事件处理、订阅-发布机制等情况。观察者涉及模式是一种使用频率非常高的设计模式,它主要是为对象之间的联动提供了一套解决方案。


原文地址:https://blog.csdn.net/Zongkunxue/article/details/145146564

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!