设计模式|观察者模式

  • 观察者模式是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,它的所有观察者都会收到通知并更新。观察者模式常用于实现事件处理系统、发布-订阅模式等。
  • 在项目中,我们使用RabbitMQ、Kafka、ActiveMQ等消息中间件来进行解耦、异步、广播、削峰 ,在设计模式中我们可以使用观察者模式也能达到解耦、异步的特点。下面我将介绍观察者模式。

观察者模式的主要角色

  • Subject(主题):被观察的对象,包含对观察者的引用列表,并提供注册、移除和通知观察者的方法。
  • Observer(观察者):定义一个更新接口,用于接收主题的通知。
  • ConcreteSubject(具体主题):具体的主题,实现了主题接口,状态发生改变时通知所有注册的观察者。
  • ConcreteObserver(具体观察者):具体的观察者,实现了观察者接口,响应主题的变化。

Java 实现观察者模式

下面是一个简单的 Java 观察者模式示例:

1. 定义观察者接口
java 复制代码
public interface  Observer {
    void update(String message);// 处理业务逻辑
}
2. 定义主题接口
java 复制代码
public interface  Subject {
    void registerObserver(Observer observer);// 添加观察者
    void removeObserver(Observer observer);// 移除观察者
    void notifyObservers();// 通知所有观察者事件
}
3. 实现具体主题
java 复制代码
public class ConcreteSubject implements Subject{
    private List<Observer> observers;
    private String message;

    public ConcreteSubject() {
        this.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() {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }

    public void setMessage(String message) {
        this.message = message;
        notifyObservers();
    }
}
4. 实现具体观察者
java 复制代码
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);
    }
}
5.测试
java 复制代码
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");
        Observer observer3 = new ConcreteObserver("Observer 3");

        subject.registerObserver(observer1);
        subject.registerObserver(observer2);
        subject.registerObserver(observer3);
        System.out.println("----------------------向三个订阅者发送消息 First Message----------------------");
        subject.setMessage("First Message");
        System.out.println("----------------------向三个订阅者发送消息 Second Message----------------------");
        subject.setMessage("Second Message");
        System.out.println("----------------------依次一个订阅者并发送消息 Third Message-------------------------");
        subject.removeObserver(observer2);
        subject.setMessage("Third Message");
    }
}
6.结果
7.分析
  • 在这里我们可以看到,我们已经可以做到可以向多个订阅者发送消息,而只需要创建一个新的订阅者放入observers容器里面,从而达到解耦的目的。

  • 而要达到异步 的功能,可以利用java的并发工具,比如线程、"ExecutorService"等。下面给出实现思路:

1.使用 ExecutorService:

使用 Executors.newCachedThreadPool() 创建一个可缓存的线程池。可以使用其他类型的线程池,如 FixedThreadPool 或 SingleThreadExecutor,根据具体需求选择。

2.异步通知:

在 notifyObservers 方法中,通过 executorService.submit() 提交任务,使得每个观察者的 update 方法在单独的线程中执行,达到了异步通知的效果。

3.资源管理:

在合适的时候调用 executorService.shutdown() 来关闭线程池,确保资源得到释放。通过这种方式,实现了观察者模式的异步通知,使得观察者的 update 方法可以并发执行,提高了效率,避免了通知过程中阻塞的问题。

观察者模式在框架中的应用

1. Java Swing

Java Swing 中的事件处理模型大量使用了观察者模式。按钮点击、窗口关闭等事件的处理都是通过事件监听器实现的。

2. Spring Framework

Spring 框架中也广泛使用了观察者模式,特别是在事件驱动的编程模型中。

  • ApplicationEvent 和 ApplicationListener : Spring 提供了一个事件机制,通过 ApplicationEventApplicationListener 实现观察者模式。
java 复制代码
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("Context Refreshed Event received!");
    }
}
java 复制代码
public class MyCustomEvent extends ApplicationEvent {
    public MyCustomEvent(Object source) {
        super(source);
    }
}

@Component
public class MyCustomEventListener implements ApplicationListener<MyCustomEvent> {
    @Override
    public void onApplicationEvent(MyCustomEvent event) {
        System.out.println("Custom Event received!");
    }
}

// 发布事件
@Component
public class MyBean {
    private final ApplicationEventPublisher publisher;

    public MyBean(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void publishEvent() {
        MyCustomEvent event = new MyCustomEvent(this);
        publisher.publishEvent(event);
    }
}
3. Google Guava

Google Guava 提供的 EventBus 是一种实现发布-订阅(Pub/Sub)模式的工具,它的实现也是采用设计模式中的观察者设计模式。,它广泛应用于解耦事件生产者和消费者。在 EventBus 中,事件生产者和事件消费者可以彼此独立地工作,彼此无需直接依赖。

观察者模式的优缺点

优点
  1. 解耦:观察者和主题之间是抽象耦合的,可以独立扩展观察者和主题。
  2. 动态订阅:观察者可以在运行时动态添加或移除。
  3. 灵活性:可以在不修改主题类的情况下增加新的观察者,实现了开闭原则。
缺点
  1. 可能引起性能问题:如果观察者较多,通知所有观察者可能会比较耗时。
  2. 可能导致内存泄漏:如果观察者没有及时被移除,可能导致内存泄漏。

使用场景

  1. 事件处理系统:GUI 事件监听,按钮点击事件。
  2. 发布-订阅系统:消息队列,订阅推送服务。
  3. 数据变化通知:数据模型变化通知视图更新,类似于 MVC 模式中的观察者角色
相关推荐
小白不太白9505 小时前
设计模式之建造者模式
java·设计模式·建造者模式
菜菜-plus7 小时前
java 设计模式 模板方法模式
java·设计模式·模板方法模式
萨达大7 小时前
23种设计模式-模板方法(Template Method)设计模式
java·c++·设计模式·软考·模板方法模式·软件设计师·行为型设计模式
机器视觉知识推荐、就业指导8 小时前
C++设计模式:原型模式(Prototype)
c++·设计模式·原型模式
阳光开朗_大男孩儿9 小时前
组合模式和适配器模式的区别
设计模式·组合模式·适配器模式
MinBadGuy9 小时前
【GeekBand】C++设计模式笔记13_Flyweight_享元模式
c++·设计模式
Clang's Blog10 小时前
23种设计模式详解(以Java为例)
java·开发语言·设计模式
程序员奇奥10 小时前
设计模式——简单工厂模型、工厂模式、抽象工厂模式、单例模式、代理模式、模板模式
单例模式·设计模式·抽象工厂模式
hxj..11 小时前
【设计模式】代理模式
java·设计模式·代理模式·动态代理
十五年专注C++开发11 小时前
C++不完整类型(Incomplete Type)的检测与避免
开发语言·c++·算法·设计模式