java的观察者模式是什么

发布时间:2021-06-18 09:15:23 作者:chen
来源:亿速云 阅读:153
# Java的观察者模式是什么

## 目录
1. [观察者模式概述](#观察者模式概述)
2. [模式结构与角色](#模式结构与角色)
3. [Java内置观察者实现](#java内置观察者实现)
4. [自定义观察者模式实现](#自定义观察者模式实现)
5. [推模型与拉模型](#推模型与拉模型)
6. [观察者模式优缺点](#观察者模式优缺点)
7. [典型应用场景](#典型应用场景)
8. [与其他模式的关系](#与其他模式的关系)
9. [JDK中的观察者模式](#jdk中的观察者模式)
10. [Spring框架中的应用](#spring框架中的应用)
11. [实际案例演示](#实际案例演示)
12. [常见问题与解决方案](#常见问题与解决方案)
13. [观察者模式变体](#观察者模式变体)
14. [性能考量](#性能考量)
15. [总结](#总结)

<a id="观察者模式概述"></a>
## 1. 观察者模式概述

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

### 1.1 基本概念

在观察者模式中,有两个主要角色:
- **Subject(主题)**:被观察的对象,它维护一组观察者,提供添加和删除观察者的方法,并在状态改变时通知观察者
- **Observer(观察者)**:定义了一个更新接口,用于在主题状态改变时接收通知

### 1.2 模式价值

观察者模式的核心价值在于:
- 解耦主题和观察者,使它们可以独立变化
- 支持广播通信,主题无需知道具体有哪些观察者
- 遵循开放-封闭原则,可以随时增加新的观察者

<a id="模式结构与角色"></a>
## 2. 模式结构与角色

### 2.1 UML类图

```plantuml
@startuml
class Subject {
    +attach(Observer)
    +detach(Observer)
    +notify()
}

class Observer {
    +update()
}

class ConcreteSubject {
    -subjectState
    +getState()
    +setState()
}

class ConcreteObserver {
    -observerState
    +update()
}

Subject <|-- ConcreteSubject
Observer <|-- ConcreteObserver
Subject o-- Observer
ConcreteObserver --> ConcreteSubject
@enduml

2.2 角色详解

  1. Subject(抽象主题)

    • 知道其观察者,可以有任意数量的观察者
    • 提供注册和删除观察者对象的接口
  2. ConcreteSubject(具体主题)

    • 存储对观察者有意义的状态
    • 当状态改变时,向各个观察者发出通知
  3. Observer(抽象观察者)

    • 为所有具体观察者定义一个更新接口
  4. ConcreteObserver(具体观察者)

    • 维护一个指向ConcreteSubject的引用
    • 存储与主题状态一致的状态
    • 实现Observer的更新接口

3. Java内置观察者实现

Java在java.util包中提供了Observable类和Observer接口的内置实现。

3.1 Observable类

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs = new Vector<>();
    
    public synchronized void addObserver(Observer o) {
        if (o == null) throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
    
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
    
    public void notifyObservers() {
        notifyObservers(null);
    }
    
    public void notifyObservers(Object arg) {
        Object[] arrLocal;
        synchronized (this) {
            if (!changed) return;
            arrLocal = obs.toArray();
            clearChanged();
        }
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
    
    protected synchronized void setChanged() {
        changed = true;
    }
    
    protected synchronized void clearChanged() {
        changed = false;
    }
}

3.2 Observer接口

public interface Observer {
    void update(Observable o, Object arg);
}

3.3 使用示例

// 被观察者
class WeatherData extends Observable {
    private float temperature;
    
    public void measurementsChanged() {
        setChanged();  // 必须调用
        notifyObservers();
    }
    
    public void setMeasurements(float temperature) {
        this.temperature = temperature;
        measurementsChanged();
    }
}

// 观察者
class CurrentConditionsDisplay implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)o;
            // 更新显示
        }
    }
}

// 使用
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay display = new CurrentConditionsDisplay();
weatherData.addObserver(display);
weatherData.setMeasurements(80);

4. 自定义观察者模式实现

4.1 自定义Subject接口

public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

4.2 自定义Observer接口

public interface Observer {
    void update(float temp, float humidity, float pressure);
}

4.3 具体实现

public class WeatherData implements Subject {
    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;
    
    public WeatherData() {
        observers = new ArrayList<>();
    }
    
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }
    
    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }
    
    public void measurementsChanged() {
        notifyObservers();
    }
    
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

5. 推模型与拉模型

5.1 推模型(Push Model)

在推模型中,Subject在通知Observer时,会将详细的数据通过参数传递给Observer。

public interface PushObserver {
    void update(float temp, float humidity, float pressure);
}

public class WeatherData implements Subject {
    // ...
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }
}

5.2 拉模型(Pull Model)

在拉模型中,Subject在通知Observer时,只传递最少的通知,Observer根据需要从Subject中拉取数据。

public interface PullObserver {
    void update(Subject subject);
}

public class WeatherData implements Subject {
    // ...
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(this);
        }
    }
    
    public float getTemperature() { return temperature; }
    public float getHumidity() { return humidity; }
    public float getPressure() { return pressure; }
}

5.3 比较

特性 推模型 拉模型
数据传递方式 Subject主动推送所有数据 Observer根据需要从Subject拉取数据
灵活性 较低,Subject决定传递哪些数据 较高,Observer决定需要哪些数据
耦合度 较高,Observer接口需包含所有参数 较低,只需Subject引用
性能 可能传递不必要的数据 只获取需要的数据

6. 观察者模式优缺点

6.1 优点

  1. 松耦合:Subject和Observer可以独立变化,互不影响
  2. 动态关系:可以在运行时动态建立和删除观察关系
  3. 广播通信:一个Subject可以通知多个Observer
  4. 符合开闭原则:新增Observer无需修改Subject代码

6.2 缺点

  1. 通知顺序不确定:Observer的更新顺序通常无法保证
  2. 性能问题:大量Observer或频繁更新可能导致性能问题
  3. 循环依赖:不当使用可能导致循环调用
  4. 内存泄漏:Observer未正确注销可能导致内存泄漏

7. 典型应用场景

  1. GUI事件处理:如按钮点击事件监听
  2. 发布-订阅系统:如消息队列、事件总线
  3. 模型-视图分离:如MVC架构中的模型和视图
  4. 监控系统:如系统状态监控和报警
  5. 股票行情系统:股票价格变化通知投资者

8. 与其他模式的关系

8.1 与中介者模式

8.2 与责任链模式

8.3 与发布-订阅模式

9. JDK中的观察者模式

9.1 java.util.Observable/Observer

如前所述,Java提供了内置实现,但自Java 9已被标记为@Deprecated。

9.2 JavaBeans属性变更监听

public class BeanExample {
    private PropertyChangeSupport support = new PropertyChangeSupport(this);
    
    public void addPropertyChangeListener(PropertyChangeListener pcl) {
        support.addPropertyChangeListener(pcl);
    }
    
    public void removePropertyChangeListener(PropertyChangeListener pcl) {
        support.removePropertyChangeListener(pcl);
    }
    
    private String value;
    
    public void setValue(String newValue) {
        String oldValue = this.value;
        this.value = newValue;
        support.firePropertyChange("value", oldValue, newValue);
    }
}

9.3 Swing中的观察者模式

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // 处理按钮点击
    }
});

10. Spring框架中的应用

10.1 ApplicationEvent机制

// 自定义事件
public class CustomEvent extends ApplicationEvent {
    public CustomEvent(Object source) {
        super(source);
    }
}

// 事件发布者
@Component
public class EventPublisher {
    @Autowired
    private ApplicationEventPublisher publisher;
    
    public void publish() {
        publisher.publishEvent(new CustomEvent(this));
    }
}

// 事件监听器
@Component
public class EventListener {
    @EventListener
    public void handleCustomEvent(CustomEvent event) {
        // 处理事件
    }
}

10.2 @EventListener注解

Spring 4.2+提供了更灵活的事件监听方式:

@EventListener(condition = "#event.success")
public void handleSuccessfulEvent(CustomEvent event) {
    // 条件监听
}

11. 实际案例演示

11.1 股票价格监控系统

// Subject
public class StockMarket {
    private Map<String, Double> prices = new HashMap<>();
    private List<StockObserver> observers = new ArrayList<>();
    
    public void addObserver(StockObserver observer) {
        observers.add(observer);
    }
    
    public void setPrice(String symbol, double price) {
        prices.put(symbol, price);
        notifyObservers(symbol, price);
    }
    
    private void notifyObservers(String symbol, double price) {
        for (StockObserver observer : observers) {
            observer.update(symbol, price);
        }
    }
}

// Observer
public interface StockObserver {
    void update(String symbol, double price);
}

// Concrete Observer
public class StockTrader implements StockObserver {
    private String name;
    
    public StockTrader(String name) {
        this.name = name;
    }
    
    @Override
    public void update(String symbol, double price) {
        System.out.printf("%s received update: %s is now %.2f\n", name, symbol, price);
    }
}

// 使用
StockMarket market = new StockMarket();
market.addObserver(new StockTrader("Trader1"));
market.addObserver(new StockTrader("Trader2"));
market.setPrice("AAPL", 150.25);

12. 常见问题与解决方案

12.1 线程安全问题

问题:多线程环境下Subject状态变更和通知可能不同步

解决方案: - 使用同步机制保护共享状态 - 考虑使用线程安全的集合类 - 使用CopyOnWriteArrayList存储观察者列表

public class ThreadSafeSubject implements Subject {
    private final List<Observer> observers = new CopyOnWriteArrayList<>();
    
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }
    
    @Override
    public void notifyObservers() {
        for (Observer o : observers) {
            o.update();
        }
    }
}

12.2 内存泄漏

问题:Observer未正确注销导致无法被垃圾回收

解决方案: - 提供明确的注销机制 - 使用WeakReference存储观察者 - 在适当生命周期点自动注销

public class WeakSubject implements Subject {
    private final List<WeakReference<Observer>> observers = new ArrayList<>();
    
    @Override
    public void registerObserver(Observer o) {
        observers.add(new WeakReference<>(o));
    }
    
    @Override
    public void notifyObservers() {
        Iterator<WeakReference<Observer>> it = observers.iterator();
        while (it.hasNext()) {
            Observer o = it.next().get();
            if (o != null) {
                o.update();
            } else {
                it.remove(); // 清理已被GC的观察者
            }
        }
    }
}

13. 观察者模式变体

13.1 事件总线(Event Bus)

public class EventBus {
    private final Map<Class<?>, List<Consumer<?>>> handlers = new ConcurrentHashMap<>();
    
    public <T> void subscribe(Class<T> eventType, Consumer<T> handler) {
        handlers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
    }
    
    public <T> void publish(T event) {
        List<Consumer<?>> eventHandlers = handlers.get(event.getClass());
        if (eventHandlers != null) {
            for (Consumer<?> handler : eventHandlers) {
                ((Consumer<T>) handler).accept(event);
            }
        }
    }
}

13.2 反应式流(Reactive Streams)

public class SimplePublisher implements Publisher<String> {
    private final ExecutorService executor = Executors.newFixedThreadPool(3);
    
    @Override
    public void subscribe(Subscriber<? super String> subscriber) {
        subscriber.onSubscribe(new Subscription() {
            @Override
            public void request(long n) {
                executor.submit(() -> {
                    for (long i = 0; i < n; i++) {
                        subscriber.onNext("Event " + i);
                    }
                });
            }
            
            @Override
            public void cancel() {
                // 处理取消逻辑
            }
        });
    }
}

14. 性能考量

  1. 通知效率:大量Observer时,线性通知可能成为瓶颈

    • 解决方案:考虑使用分层观察、异步通知
  2. 内存占用:每个Observer通常需要存储Subject引用

    • 解决方案:使用弱引用、及时注销
  3. 线程阻塞:同步通知可能导致调用链阻塞

    • 解决方案:异步通知、事件队列
  4. 事件风暴:频繁状态变化导致过多通知

    • 解决方案:合并通知、节流处理

15. 总结

观察者模式是Java中一种极其重要的设计模式,它通过定义对象间的一对多依赖关系,实现了松耦合的交互方式。从早期的AWT/Swing事件模型,到现代的Spring事件机制,观察者模式在Java生态中有着广泛的应用。

关键要点:

  1. 理解推模型和拉模型的区别及适用场景
  2. 注意线程安全和内存泄漏问题
  3. 了解Java内置实现及其局限性
  4. 掌握在Spring等现代框架中的应用方式
  5. 根据实际需求选择合适的变体模式

最佳实践建议:

  1. 对于简单场景,可以使用Java内置的Observer/Observable
  2. 对于复杂系统,考虑实现自定义的观察者模式
  3. 在多线程环境下特别注意同步问题
  4. 考虑使用事件总线等高级变体满足复杂需求
  5. 在性能敏感场景,评估观察者模式的开销

观察者模式虽然简单,但正确使用需要仔细考虑各种边界条件和性能影响。希望本文能帮助您全面理解并有效应用这一经典设计模式。 “`

推荐阅读:
  1. Python中观察者模式的原理是什么
  2. JAVA单例模式,工厂模式,观察者模式是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:GHOSTXP光盘刻录的示例分析

下一篇:python清洗文件中数据的方法

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》