设计模式之观察者模式


前言

观察者模式(Observer Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。它是一种行为设计模式,允许对象之间建立一种一对多的依赖关系,这样当一个对象状态改变时,它的所有依赖者(观察者)都会收到通知并自动更新。


一、角色和结构图

观察者模式的主要角色包括:

  1. 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
  2. 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法。当具体主题的内部状态发生改变时,会通知所有注册过的观察者对象。
  3. 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
  4. 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
    示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

二、应用场景

观察者模式是一种广泛应用的软件设计模式,它在多种场景中发挥着重要的作用。以下是观察者模式的一些典型应用场景:

  1. 图形用户界面(GUI)设计:在GUI中,观察者模式常用于处理组件之间的交互。例如,当按钮被点击时,它可以作为观察者通知窗口(被观察者)进行相应的事件处理。这种模式有助于实现组件之间的解耦,使得它们可以独立地进行开发和维护。
  2. 消息订阅与发布系统:观察者模式适用于构建消息发布/订阅系统。在这种系统中,消息发布者充当主题(被观察者),而订阅者则充当观察者。当发布者发布新消息时,所有订阅者都会收到通知并执行相应操作。这种模式常见于新闻订阅、实时数据监控、社交媒体平台等场景。
  3. Spring框架:Spring 中实现的观察者模式包含三部分:Event 事件(相当于消息)、Listener 监听者(相当于观察者)、Publisher 发送者(相当于被观察者)。
  4. 事件驱动系统:观察者模式在事件驱动系统中有着广泛的应用,如游戏引擎。当特定事件发生时,如游戏角色的位置变化,触发相应的回调函数并通知所有注册的观察者,从而更新图形显示。
  5. 实时日志记录系统:观察者模式也可以用于实时日志记录系统,其中日志记录器充当被观察者,而观察者可以是日志分析器、报警系统等。当日志发生变化时,观察者将收到通知并执行相应的操作,如生成报告、发送警报等。

此外,观察者模式还可以应用于团队协作、实时数据更新、硬件状态监控等多种场景。总的来说,当存在对象之间的一对多关系,并且需要实时通知和更新时,观察者模式是一个非常有效的解决方案。

三、观察者模式的简易实现

观察者模式可以通过简单的代码实现。以下是一个使用Java语言实现的简易示例:

首先,我们定义一个Observer接口,它声明了一个update方法,该方法将由被观察者(Subject)在状态改变时调用:

public interface Observer {
    void update(String message);
}

接着,我们定义一个Subject接口,它声明了注册、移除和通知观察者的方法:

import java.util.ArrayList;
import java.util.List;

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers(String message);
}

然后,我们实现一个具体的ConcreteSubject类,它实现了Subject接口,并维护了一个观察者列表:

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

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

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

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

    // 这里可以添加一些改变状态的方法,并在状态改变时调用notifyObservers
}

最后,我们实现一个具体的ConcreteObserver类,它实现了Observer接口:

public class ConcreteObserver implements Observer {
    private String name;

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

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

现在,我们可以创建一个ConcreteSubject实例,并注册一些ConcreteObserver实例,然后通知它们:

public class ObserverPatternDemo {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();

        Observer observer1 = new ConcreteObserver("Observer 1");
        Observer observer2 = new ConcreteObserver("Observer 2");

        subject.registerObserver(observer1);
        subject.registerObserver(observer2);

        // 当ConcreteSubject的状态改变时,它会调用notifyObservers方法
        subject.notifyObservers("State has changed!");

        // 如果需要,可以移除某个观察者
        subject.removeObserver(observer1);

        // 再次通知,这次observer1将不会收到消息
        subject.notifyObservers("Another state change.");
    }
}

运行ObserverPatternDemo类的main方法,你将看到观察者如何接收到来自被观察者的通知,并输出相应的消息。

这个简单的实现展示了观察者模式的基本结构和工作原理。在实际应用中,你可以根据需要添加更多的功能,比如错误处理、线程安全等。

四、异步非阻塞观察者模式的简易实现

异步非阻塞除了能实现代码解耦之外,还能提高代码的执行效率;进程间的观察者模式解耦更加彻底,一般是基于消息队列来实现,用来实现不同进程间的被观察者和观察者之间的交互。 异步非阻塞观察者模式通常涉及到多线程或异步编程技术,使得被观察者(Subject)可以在不阻塞当前线程的情况下通知观察者(Observer)。下面是一个简易的Java实现,使用Java的ExecutorService来异步地通知观察者:

首先,我们定义Observer接口和Subject接口与前面的示例类似:

public interface Observer {
    void update(String message);
}

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObserversAsync(String message);
}

接下来,我们实现一个AsyncConcreteSubject类,它实现了Subject接口,并使用ExecutorService来异步地通知观察者:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AsyncConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private final ExecutorService executorService = Executors.newCachedThreadPool();

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

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

    @Override
    public void notifyObserversAsync(final String message) {
        executorService.submit(() -> {
            for (Observer observer : observers) {
                observer.update(message);
            }
        });
    }

    // 关闭ExecutorService,释放资源
    public void shutdown() {
        executorService.shutdown();
    }
}

现在,我们实现一个ConcreteObserver类,它实现了Observer接口:

public class ConcreteObserver implements Observer {
    private String name;

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

    @Override
    public void update(String message) {
        System.out.println(name + " received message asynchronously: " + message);
    }
}

最后,我们创建一个AsyncObserverPatternDemo类来演示异步非阻塞观察者模式:

public class AsyncObserverPatternDemo {
    public static void main(String[] args) throws InterruptedException {
        AsyncConcreteSubject subject = new AsyncConcreteSubject();

        Observer observer1 = new ConcreteObserver("Observer 1");
        Observer observer2 = new ConcreteObserver("Observer 2");

        subject.registerObserver(observer1);
        subject.registerObserver(observer2);

        // 异步地通知观察者
        subject.notifyObserversAsync("State has changed asynchronously!");

        // 等待一段时间,确保异步任务完成
        Thread.sleep(1000);

        subject.shutdown(); // 关闭ExecutorService
    }
}

在这个例子中,当notifyObserversAsync方法被调用时,它会在一个单独的线程中执行observer.update(message),因此不会阻塞主线程。Thread.sleep(1000)只是为了演示目的,确保异步任务有足够的时间完成。在实际应用中,你可能需要更健壮的机制来处理异步任务的完成,比如使用FutureCompletableFuture

请注意,使用ExecutorService时,应该合理地管理线程池的生命周期,包括关闭它,以避免资源泄漏。在上面的示例中,我们提供了一个shutdown方法来关闭ExecutorService。在应用程序结束时,应该调用此方法。

此外,对于更复杂的场景,比如错误处理、线程池大小的管理、任务的超时处理等,你可能需要使用更高级的并发控制机制。

相关推荐

  1. 【前端设计模式观察模式

    2024-04-02 14:36:03       56 阅读
  2. 设计模式观察模式

    2024-04-02 14:36:03       55 阅读
  3. 设计模式观察模式

    2024-04-02 14:36:03       56 阅读
  4. 设计模式观察模式

    2024-04-02 14:36:03       53 阅读
  5. go 设计模式观察模式

    2024-04-02 14:36:03       53 阅读
  6. 设计模式观察模式

    2024-04-02 14:36:03       54 阅读
  7. C++ 设计模式观察模式

    2024-04-02 14:36:03       51 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-02 14:36:03       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-02 14:36:03       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-02 14:36:03       87 阅读
  4. Python语言-面向对象

    2024-04-02 14:36:03       96 阅读

热门阅读

  1. C语言归并递归与非递归实现

    2024-04-02 14:36:03       36 阅读
  2. 1187: 【二维数组】矩阵加法

    2024-04-02 14:36:03       36 阅读
  3. 开发基础知识之应用程序包基础知识

    2024-04-02 14:36:03       34 阅读
  4. 网络问题排查方案(IP冲突问题)

    2024-04-02 14:36:03       33 阅读
  5. Lua脚本的使用

    2024-04-02 14:36:03       38 阅读
  6. react--常见hook

    2024-04-02 14:36:03       38 阅读
  7. 25.死锁

    25.死锁

    2024-04-02 14:36:03      44 阅读
  8. Git 实战教程

    2024-04-02 14:36:03       45 阅读
  9. 数据流模型——【数据科学与工程算法基础】

    2024-04-02 14:36:03       39 阅读
  10. CPU狂飙900%,该怎么处理

    2024-04-02 14:36:03       37 阅读
  11. 【OpenCV进阶】图像中添加中文字幕

    2024-04-02 14:36:03       38 阅读
  12. 低代码与系统集成:革新企业应用开发的新动力

    2024-04-02 14:36:03       38 阅读