网站Logo Ilren 小记

Java设计模式-观察者模式

jack
3
2023-05-06

观察者模式(Observer Pattern):事件通知与解耦的艺术

模式简介
观察者模式是一种行为型设计模式,定义了对象之间的一对多依赖关系,当一个对象(被观察者)状态改变时,所有依赖它的对象(观察者)都会自动收到通知并更新。
就像微信公众号(被观察者)和订阅用户(观察者)的关系——公众号发布新文章时,所有订阅者都会自动收到推送。

📦 应用场景

  • GUI事件处理(按钮点击、键盘输入)

  • 发布-订阅系统(如Kafka、Redis Pub/Sub)

  • 状态监控(服务器健康检测)

  • 数据变更通知(数据库->缓存同步)

  • Spring的事件驱动模型(ApplicationEvent

  • Vue/React的响应式系统

🧠 核心实现思路

  1. Subject(主题):维护观察者列表,提供注册/注销方法

  2. Observer(观察者):定义更新接口

  3. ConcreteSubject(具体主题):状态变化时通知观察者

  4. ConcreteObserver(具体观察者):实现更新逻辑

🧱 观察者模式的多种实现方式

1. 经典实现(手动管理)

// 观察者接口
interface Observer {
    void update(String message);
}

// 主题接口
interface Subject {
    void register(Observer o);
    void remove(Observer o);
    void notifyObservers();
}

// 具体主题
class NewsPublisher implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String latestNews;

    public void setNews(String news) {
        this.latestNews = news;
        notifyObservers();
    }

    @Override
    public void register(Observer o) {
        observers.add(o);
    }

    @Override
    public void notifyObservers() {
        observers.forEach(o -> o.update(latestNews));
    }
}

// 具体观察者
class Subscriber implements Observer {
    private String name;
    
    public Subscriber(String name) {
        this.name = name;
    }
    
    @Override
    public void update(String message) {
        System.out.println(name + "收到新闻:" + message);
    }
}

2. Java内置支持(已过时但需了解)

import java.util.Observable;
import java.util.Observer;

// 继承Observable类
class WeatherStation extends Observable {
    private float temperature;
    
    public void setTemperature(float temp) {
        this.temperature = temp;
        setChanged();  // 标记状态变化
        notifyObservers(temp);  // 推模式通知
    }
}

// 实现Observer接口
class Display implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("温度更新:" + arg + "°C");
    }
}

3. 响应式流(Java9+ Flow API)

import java.util.concurrent.Flow.*;
import java.util.concurrent.SubmissionPublisher;

// 发布者
SubmissionPublisher<String> publisher = new SubmissionPublisher<>();

// 订阅者
class NewsSubscriber implements Subscriber<String> {
    private Subscription subscription;
    
    @Override
    public void onSubscribe(Subscription subscription) {
        this.subscription = subscription;
        subscription.request(1); // 背压控制
    }
    
    @Override
    public void onNext(String item) {
        System.out.println("收到新闻:" + item);
        subscription.request(1);
    }
    
    @Override
    public void onError(Throwable throwable) {
        System.err.println("出错:" + throwable.getMessage());
    }
    
    @Override
    public void onComplete() {
        System.out.println("新闻推送结束");
    }
}

// 使用
publisher.subscribe(new NewsSubscriber());
publisher.submit("Java 17发布啦!");
publisher.close();

4. Spring事件驱动(企业级推荐)

// 自定义事件
class OrderEvent extends ApplicationEvent {
    private String orderId;
    
    public OrderEvent(Object source, String orderId) {
        super(source);
        this.orderId = orderId;
    }
    // getter...
}

// 发布者
@Service
class OrderService {
    @Autowired ApplicationEventPublisher publisher;
    
    public void createOrder() {
        // 业务逻辑...
        publisher.publishEvent(new OrderEvent(this, "ORD-123"));
    }
}

// 监听器(四种方式)
@Component
class NotificationListener {
    // 方式1:注解监听
    @EventListener
    public void handleOrderEvent(OrderEvent event) {
        System.out.println("处理订单事件:" + event.getOrderId());
    }
    
    // 方式2:实现接口
    @Override  // ApplicationListener<OrderEvent>
    public void onApplicationEvent(OrderEvent event) {
        // 处理逻辑
    }
}

💎 最佳实践推荐

线程安全实现

// 使用CopyOnWriteArrayList避免并发修改异常
class SafeSubject implements Subject {
    private List<Observer> observers = new CopyOnWriteArrayList<>();
    
    @Override
    public void register(Observer o) {
        observers.add(o);
    }
    
    @Override
    public void notifyObservers() {
        observers.forEach(Observer::update);
    }
}

异步事件处理

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.initialize();
        return executor;
    }
}

// 异步监听
@EventListener
@Async
public void handleAsyncEvent(OrderEvent event) {
    // 耗时操作...
}

事件过滤与条件监听

// 条件化监听
@EventListener(condition = "#event.orderId.startsWith('VIP')")
public void handleVipOrder(OrderEvent event) {
    System.out.println("处理VIP订单:" + event.getOrderId());
}

💣 常见问题与解决方案

问题1:观察者执行阻塞主线程?

解决方案

  • 使用异步观察者(线程池/消息队列)

  • Spring中配合@Async注解

问题2:观察者之间需要顺序执行?

解决方案

  • 使用@Order注解定义顺序

  • 手动维护观察者优先级列表

问题3:内存泄漏风险?

解决方案

  • 及时调用removeObserver

  • 使用弱引用(WeakReference

📊 模式对比

实现方式

优点

缺点

适用场景

经典手动实现

完全控制

需自行处理线程安全

简单场景

Java Observable

内置支持

已过时,功能有限

遗留系统维护

响应式流

支持背压,异步友好

学习曲线陡峭

高并发流处理

Spring事件

与企业生态集成

依赖Spring框架

Spring应用

📚 实际应用案例

  1. Java Swing事件监听

    button.addActionListener(e -> System.out.println("按钮被点击"));
  2. ZooKeeper Watcher机制

    zk.exists("/path", watchedEvent -> {
        System.out.println("节点变化:" + watchedEvent.getType());
    });
  3. Redis Pub/Sub

    jedis.subscribe(new JedisPubSub() {
        @Override
        public void onMessage(String channel, String message) {
            System.out.println("收到消息:" + message);
        }
    }, "news-channel");

🎯 总结建议

  1. 优先选择:Spring事件驱动(企业应用)或响应式流(高并发)

  2. 性能优化:高频事件考虑批量通知

  3. 线程安全:使用CopyOnWriteArrayList或并发集合

  4. 资源释放:及时注销观察者防止内存泄漏

  5. 设计原则:遵循松耦合原则,主题不应依赖具体观察者


动物装饰