1. 概述

本教程将介绍观察者模式,并探讨几种Java实现方案。观察者模式是行为型设计模式中非常实用的一个,掌握它能有效解耦对象间的通信。

2. 什么是观察者模式?

观察者模式定义了对象间的一对多依赖关系:当一个对象(被观察者)状态改变时,所有依赖它的对象(观察者)都会收到通知并自动更新

举个栗子:新闻机构(被观察者)收到新闻时,会通知所有订阅的频道(观察者)。新闻更新改变了新闻机构的状态,进而触发频道更新。

手动实现方案

被观察者实现

public class NewsAgency {
    private String news;
    private List<Channel> channels = new ArrayList<>();

    public void addObserver(Channel channel) {
        this.channels.add(channel);
    }

    public void removeObserver(Channel channel) {
        this.channels.remove(channel);
    }

    public void setNews(String news) {
        this.news = news;
        for (Channel channel : this.channels) {
            channel.update(this.news);
        }
    }
}

关键点:被观察者必须维护观察者列表(channels),状态变化时遍历调用观察者的update()方法。

观察者实现

public interface Channel {
    public void update(Object o);
}

public class NewsChannel implements Channel {
    private String news;

    @Override
    public void update(Object news) {
        this.setNews((String) news);
    } 

    // standard getter and setter
}

使用示例

NewsAgency observable = new NewsAgency();
NewsChannel observer = new NewsChannel();

observable.addObserver(observer);
observable.setNews("突发新闻");
assertEquals(observer.getNews(), "突发新闻");

3. 使用Observer接口实现

Java核心库提供了预定义的Observer接口,能简化实现过程。

观察者实现

public class ONewsChannel implements Observer {
    private String news;

    @Override
    public void update(Observable o, Object news) {
        this.setNews((String) news);
    }

    // standard getter and setter
}

被观察者实现

public class ONewsAgency extends Observable {
    private String news;

    public void setNews(String news) {
        this.news = news;
        setChanged();     // 标记状态已改变
        notifyObservers(news); // 触发通知
    }
}

优势:无需手动维护观察者列表,Observable类已内置addObserver()deleteObserver()方法。

使用示例

ONewsAgency observable = new ONewsAgency();
ONewsChannel observer = new ONewsChannel();

observable.addObserver(observer);
observable.setNews("科技新闻");
assertEquals(observer.getNews(), "科技新闻");

⚠️ 重要提醒Observer接口在Java 9已被废弃!主要缺陷:

  • Observable是类而非接口,无法利用多继承
  • 子类可能覆盖同步方法破坏线程安全

4. 使用PropertyChangeListener实现

这是当前推荐方案,通过JavaBeans的属性变更机制实现。

被观察者实现

public class PCLNewsAgency {
    private String news;
    private PropertyChangeSupport support;

    public PCLNewsAgency() {
        support = new PropertyChangeSupport(this);
    }

    public void addPropertyChangeListener(PropertyChangeListener pcl) {
        support.addPropertyChangeListener(pcl);
    }

    public void removePropertyChangeListener(PropertyChangeListener pcl) {
        support.removePropertyChangeListener(pcl);
    }

    public void setNews(String value) {
        support.firePropertyChange("news", this.news, value);
        this.news = value;
    }
}

核心机制:通过PropertyChangeSupport管理观察者,firePropertyChange()触发通知。

观察者实现

public class PCLNewsChannel implements PropertyChangeListener {
    private String news;

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        this.setNews((String) evt.getNewValue());
    }

    // standard getter and setter
}

使用示例

PCLNewsAgency observable = new PCLNewsAgency();
PCLNewsChannel observer = new PCLNewsChannel();

observable.addPropertyChangeListener(observer);
observable.setNews("财经快讯");
assertEquals(observer.getNews(), "财经快讯");

优势对比

  • 类型安全:通过PropertyChangeEvent传递变更详情
  • 线程安全:PropertyChangeSupport内置同步机制
  • 灵活性:支持属性粒度的监听控制

5. 过期监听器问题

所有上述实现都存在内存泄漏风险:如果观察者未显式注销,即使不再使用仍会被被观察者强引用持有。

解决方案

使用WeakReference弱引用管理观察者:

private List<WeakReference<Channel>> observers = new ArrayList<>();

public void addObserver(Channel channel) {
    observers.add(new WeakReference<>(channel));
}

效果:当观察者对象不再被其他地方引用时,GC会自动回收,避免内存泄漏。

6. 结论

观察者模式在Java中有三种主流实现方式:

实现方案 推荐度 特点
手动实现 ⭐⭐ 完全可控但代码量大
Observer接口 已废弃,存在设计缺陷
PropertyChangeListener 类型安全、线程安全、推荐使用

最佳实践建议

  1. 优先使用PropertyChangeListener
  2. 注意观察者注销,避免内存泄漏
  3. 对于高频通知场景,考虑异步通知机制

完整代码示例可在GitHub仓库查看。


原始标题:The Observer Pattern in Java