观察者模式(Observer Pattern)
概念
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象间的一对多依赖关系,当被观察的对象(主题)状态发生改变时,所有依赖它的对象(观察者)都会收到通知并自动更新。观察者模式广泛用于事件处理系统中。
应用场景
-
事件驱动的系统:在GUI应用程序中,当用户触发某些事件(如按钮点击、窗口关闭等),界面上多个组件可能需要做出响应。观察者模式在此时尤为合适,可以将事件源(Subject)和处理程序(Observer)解耦。
-
数据变化的广播机制:当数据发生变化时,多个依赖此数据的组件或模块需要更新。例如,在股市行情系统中,当股票价格更新时,所有订阅该股票的观察者都会收到通知。
-
发布/订阅系统:当一个对象(发布者)需要通知多个对象(订阅者)时,观察者模式可以动态注册和通知这些订阅者,从而实现发布/订阅的机制。
注意点
- 性能问题:如果观察者较多或者通知非常频繁,可能会影响性能,因此需要注意观察者模式的使用场景,避免不必要的通知开销。
- 内存泄漏:要避免忘记移除不再需要的观察者,尤其是在对象生命周期较短的情况下,可能会导致内存泄漏问题。
- 通知顺序:多个观察者同时监听同一主题时,可能需要考虑通知的顺序问题。
核心要素
- Subject(主题/被观察者):维护观察者列表,负责添加、移除和通知观察者。
- Observer(观察者):定义一个更新接口,用于接收主题状态的变更通知。
- ConcreteSubject(具体主题):具体的主题对象,状态发生变化时通知所有的观察者。
- ConcreteObserver(具体观察者):具体的观察者对象,实现更新接口并根据通知做出相应动作。
Java代码完整示例
代码示例:简单观察者模式
java
// 观察者接口
interface Observer {
void update(String message);
}
// 具体观察者A
class ConcreteObserverA implements Observer {
private String name;
public ConcreteObserverA(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " 收到通知: " + message);
}
}
// 具体观察者B
class ConcreteObserverB implements Observer {
private String name;
public ConcreteObserverB(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " 收到通知: " + message);
}
}
// 被观察者接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(String message);
}
// 具体主题
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);
}
}
}
// 客户端代码
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserverA("观察者1");
Observer observer2 = new ConcreteObserverB("观察者2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
// 通知所有观察者
subject.notifyObservers("主题状态发生变化");
// 移除观察者1,之后再次通知
subject.removeObserver(observer1);
subject.notifyObservers("第二次状态变化");
}
}
输出结果:
观察者1 收到通知: 主题状态发生变化
观察者2 收到通知: 主题状态发生变化
观察者2 收到通知: 第二次状态变化
各种变形用法完整示例
-
推模式 vs 拉模式
- 推模式(Push):主题对象主动将变更的数据推送给所有观察者,观察者不需要主动请求数据。
- 拉模式(Pull):观察者主动从主题对象拉取所需的数据,主题只通知有更新,但不提供具体数据。
推模式示例(之前的代码已为推模式):
java// notifyObservers()方法中直接将消息推送给观察者 subject.notifyObservers("推送的数据");
拉模式示例:
java// 观察者接口 interface Observer { void update(Subject subject); } // 具体观察者 class ConcreteObserver implements Observer { private String name; public ConcreteObserver(String name) { this.name = name; } @Override public void update(Subject subject) { // 主动拉取数据 if (subject instanceof ConcreteSubject) { ConcreteSubject concreteSubject = (ConcreteSubject) subject; System.out.println(name + " 拉取到数据: " + concreteSubject.getState()); } } } // 具体主题 class ConcreteSubject implements Subject { private List<Observer> observers = new ArrayList<>(); private String state; public String getState() { return state; } public void setState(String state) { this.state = state; notifyObservers(); } @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(this); // 仅通知更新,不推送数据 } } } // 客户端代码 public class ObserverPatternPullDemo { public static void main(String[] args) { ConcreteSubject subject = new ConcreteSubject(); Observer observer = new ConcreteObserver("观察者"); subject.registerObserver(observer); subject.setState("新的状态"); } }
输出结果:
观察者 拉取到数据: 新的状态
-
Java内置的观察者模式实现
Java标准库提供了
java.util.Observer
和java.util.Observable
类,尽管它们现在被认为是过时的,但仍可以用来实现观察者模式。代码示例:
javaimport java.util.Observable; import java.util.Observer; // 具体主题类,继承Observable class ConcreteSubject extends Observable { private String state; public String getState() { return state; } public void setState(String state) { this.state = state; setChanged(); // 标记状态已经改变 notifyObservers(state); // 推送通知给所有观察者 } } // 具体观察者类,继承Observer接口 class ConcreteObserver implements Observer { private String name; public ConcreteObserver(String name) { this.name = name; } @Override public void update(Observable o, Object arg) { System.out.println(name + " 收到更新: " + arg); } } public class ObserverPatternBuiltInDemo { public static void main(String[] args) { ConcreteSubject subject = new ConcreteSubject(); ConcreteObserver observer1 = new ConcreteObserver("观察者1"); ConcreteObserver observer2 = new ConcreteObserver("观察者2"); subject.addObserver(observer1); subject.addObserver(observer2); subject.setState("主题状态已更改"); } }
输出结果:
观察者1 收到更新: 主题状态已更改 观察者2 收到更新: 主题状态已更改
-
异步观察者模式
如果通知的处理较为耗时,可以将通知的操作异步化,防止阻塞主题的状态变化。
代码示例(使用线程实现异步通知):
javaclass AsyncObserver implements Observer { private String name; public AsyncObserver(String name) { this.name = name; } @Override public void update(String message) { // 启动新线程进行异步处理 new Thread(() -> { try { Thread.sleep(1000); // 模拟耗时操作 System.out.println(name + " 异步处理收到的通知: " + message); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } } public class AsyncObserverPatternDemo { public static void main(String[] args) { ConcreteSubject subject = new ConcreteSubject(); Observer asyncObserver1 = new AsyncObserver("异步观察者1"); Observer asyncObserver2 = new AsyncObserver("异步观察者2"); subject.registerObserver(asyncObserver1); subject.registerObserver(asyncObserver2); subject.notifyObservers("异步通知消息"); } }
输出结果:
异步观察者1 异步处理收到的通知: 异步通知消息 异步观察者2 异步处理收到的通知: 异步通知消息
通过观察者模式,系统可以轻松扩展观察者,且实现了低耦合、高可扩展性。不同的变形(推拉模式、异步处理等)还可以帮助优化性能和增强可扩展性。