
文章目录
示例代码
完整代码
src/
├── common/ # 共享接口包
│ ├── Observer.java # Observer:观察者接口
│ └── Subject.java # Subject:主题接口
│
└── observer/ # 观察者实现
├── ConcreteSubject.java # Concrete Subject:具体主题
├── UserObserver.java # Concrete Observer:具体观察者
├── NewsAgencyClient.java # Client:客户端
└── ObserverDemo.java # 运行入口
编译运行
bash
# 编译
javac -d out src/common/*.java src/observer/*.java
# 运行
java -cp out observer.ObserverDemo
什么是观察者模式?
概念
观察者模式 是一种行为型设计模式,它的核心作用是定义对象之间的一对多依赖关系,当一个对象(主题)的状态发生改变时,所有依赖它的观察者都会自动收到通知并更新。
简单来说,观察者模式就是在发布-订阅
生活类比
就像报纸订阅:
- 报社(Subject)每天出版报纸
- 订阅者(Observer)收到通知
- 当有新技术发布时,所有订阅者都会收到通知
在代码世界中,观察者模式干的是同样的事:
- 主题维护观察者列表
- 当主题状态改变,通知所有观察者
- 观察者和主题之间是松耦合关系
观察者模式中的三个角色
Subject(主题)
维护观察者列表,并在状态改变时通知观察者。在我们的例子中是 Subject:
java
// src/common/Subject.java
package common;
import java.util.ArrayList;
import java.util.List;
public abstract class Subject {
protected List<Observer> observers = new ArrayList<>();
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void notifyAllObservers() {
for (Observer observer : observers) {
observer.update(getMessage());
}
}
public abstract String getMessage();
}
Observer(观察者)
定义收到通知时的更新接口。在我们的例子中是 Observer:
java
// src/common/Observer.java
package common;
public interface Observer {
void update(String message);
}
ConcreteSubject(具体主题)
存储真实状态,当状态改变时通知观察者。在我们的例子中是 ConcreteSubject:
java
// src/observer/ConcreteSubject.java
package observer;
import common.Subject;
public class ConcreteSubject extends Subject {
private String message;
public void setMessage(String message) {
this.message = message;
System.out.println(" [Subject] 发布新消息: " + message);
notifyAllObservers();
}
@Override
public String getMessage() {
return message;
}
}
ConcreteObserver(具体观察者)
实现 Observer 接口的具体类。在我们的例子中是 UserObserver:
java
// src/observer/UserObserver.java
package observer;
import common.Observer;
public class UserObserver implements Observer {
private String name;
public UserObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(" [" + name + "] 收到更新: " + message);
}
}
观察者模式示例
结构图
┌─────────────┐ ┌──────────────┐
│ Subject │◄──────│ Observer │
│ (主题) │ notify│ (观察者) │
└─────────────┘ └──────────────┘
▲ ▲
│ │
│ │
┌─────────────┐ ┌──────────────┐
│ConcreteSubject│ │UserObserver │
│ (具体主题) │ │ (具体观察者) │
└─────────────┘ └──────────────┘
运行示例
>>> 初始化新闻主题
>>> 用户订阅
[Client] 用户 '张三' 订阅了新闻
[Client] 用户 '李四' 订阅了新闻
[Client] 用户 '王五' 订阅了新闻
>>> 发布第一条新闻:突发!某地发生地震
[Subject] 发布新消息: 突发!某地发生地震
[张三] 收到更新: 突发!某地发生地震
[李四] 收到更新: 突发!某地发生地震
[王五] 收到更新: 突发!某地发生地震
>>> 用户王五退订
>>> 发布第二条新闻:天气预报:明天有雨
[Subject] 发布新消息: 天气预报:明天有雨
[张三] 收到更新: 天气预报:明天有雨
[李四] 收到更新: 天气预报:明天有雨
代码实现
java
// 创建主题
Subject news = new ConcreteSubject();
// 订阅
news.attach(new UserObserver("张三"));
news.attach(new UserObserver("李四"));
news.attach(new UserObserver("王五"));
// 发布消息
((ConcreteSubject) news).setMessage("突发!某地发生地震");
// 退订
news.detach(observers.get("王五"));
// 再次发布
((ConcreteSubject) news).setMessage("天气预报:明天有雨");
观察者模式的优势
- 松耦合:主题和观察者之间不直接依赖
- 动态交互:可以在运行时动态添加/移除观察者
- 开闭原则:对扩展开放,不需要修改主题代码
- 一对多关系:一个主题可以对应多个观察者
观察者模式的注意事项
- 通知顺序:观察者收到通知的顺序是不确定的
- 循环依赖:避免观察者和主题之间的循环依赖
- 通知效率:观察者数量过多时,通知可能影响性能