定义
观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象的状态发生变化时,它会自动通知所有观察者,使它们能够及时更新自己。
核心组成
- Subject(主题) :维护观察者列表,提供注册、移除和通知的方法。
- Observer(观察者) :定义更新接口,所有具体观察者实现该接口。
- ConcreteSubject(具体主题) :存储状态,状态改变时通知所有观察者。
- ConcreteObserver(具体观察者) :实现更新接口,保持自身状态与主题同步。
工作原理
- 观察者向主题注册,主题内部维护一个观察者集合。
- 当主题状态变化时,它会遍历集合,调用每个观察者的更新方法。
- 观察者通过更新方法接收变化信息,并执行相应的业务逻辑。
优点
- 降低了主题与观察者之间的耦合度,两者都可以独立扩展。
- 支持广播通信,所有注册的观察者都能收到通知。
- 符合开闭原则,可以动态添加或删除观察者。
缺点
- 如果观察者过多,通知过程可能耗时。
- 若观察者之间存在循环依赖,可能导致系统崩溃。
- 观察者无法直接了解主题的具体变化细节,只能依赖传递的参数。
适用场景
- 一个对象的状态变化需要影响其他多个对象,且不知道具体有多少对象需要响应。
- 系统中存在事件触发机制,如 GUI 监听器、消息订阅、数据同步等。
- 希望将对象之间的依赖关系最小化,实现松耦合设计。
Java 案例:股票价格监控系统
下面使用 Java 实现一个股票价格监控系统。当某只股票价格变动时,所有订阅该股票的投资者(观察者)会收到通知。
代码实现
java
import java.util.ArrayList;
import java.util.List;
// 观察者接口
interface Investor {
void update(String stockName, double price);
}
// 主题接口
interface Stock {
void registerInvestor(Investor investor);
void removeInvestor(Investor investor);
void notifyInvestors();
}
// 具体主题:某只股票
class ConcreteStock implements Stock {
private String name;
private double price;
private List<Investor> investors = new ArrayList<>();
public ConcreteStock(String name, double initialPrice) {
this.name = name;
this.price = initialPrice;
}
@Override
public void registerInvestor(Investor investor) {
investors.add(investor);
System.out.println(investor + " 订阅了股票 " + name);
}
@Override
public void removeInvestor(Investor investor) {
investors.remove(investor);
System.out.println(investor + " 取消了订阅 " + name);
}
@Override
public void notifyInvestors() {
for (Investor investor : investors) {
investor.update(name, price);
}
}
// 当股票价格变动时调用此方法
public void setPrice(double newPrice) {
if (this.price != newPrice) {
this.price = newPrice;
System.out.println("\n股票 " + name + " 价格变更为: " + price);
notifyInvestors();
}
}
}
// 具体观察者:个人投资者
class IndividualInvestor implements Investor {
private String name;
public IndividualInvestor(String name) {
this.name = name;
}
@Override
public void update(String stockName, double price) {
System.out.println("个人投资者 " + name + " 收到通知: " + stockName + " 当前价格 = " + price);
}
@Override
public String toString() {
return "个人投资者[" + name + "]";
}
}
// 具体观察者:机构投资者
class InstitutionalInvestor implements Investor {
private String institutionName;
public InstitutionalInvestor(String institutionName) {
this.institutionName = institutionName;
}
@Override
public void update(String stockName, double price) {
System.out.println("机构 " + institutionName + " 收到通知: " + stockName + " 当前价格 = " + price);
}
@Override
public String toString() {
return "机构[" + institutionName + "]";
}
}
// 测试类
public class ObserverPatternDemo {
public static void main(String[] args) {
// 创建主题:阿里巴巴股票
ConcreteStock alibaba = new ConcreteStock("阿里巴巴", 188.0);
// 创建观察者
IndividualInvestor alice = new IndividualInvestor("Alice");
IndividualInvestor bob = new IndividualInvestor("Bob");
InstitutionalInvestor goldman = new InstitutionalInvestor("高盛");
// 注册观察者
alibaba.registerInvestor(alice);
alibaba.registerInvestor(bob);
alibaba.registerInvestor(goldman);
// 股票价格变动,自动通知
alibaba.setPrice(192.5);
alibaba.setPrice(185.0);
// 取消一个观察者
alibaba.removeInvestor(bob);
// 再次变动
alibaba.setPrice(190.0);
}
}
运行结果
text
个人投资者[Alice] 订阅了股票 阿里巴巴
个人投资者[Bob] 订阅了股票 阿里巴巴
机构[高盛] 订阅了股票 阿里巴巴
股票 阿里巴巴 价格变更为: 192.5
个人投资者 Alice 收到通知: 阿里巴巴 当前价格 = 192.5
个人投资者 Bob 收到通知: 阿里巴巴 当前价格 = 192.5
机构 高盛 收到通知: 阿里巴巴 当前价格 = 192.5
股票 阿里巴巴 价格变更为: 185.0
个人投资者 Alice 收到通知: 阿里巴巴 当前价格 = 185.0
个人投资者 Bob 收到通知: 阿里巴巴 当前价格 = 185.0
机构 高盛 收到通知: 阿里巴巴 当前价格 = 185.0
个人投资者[Bob] 取消了订阅 阿里巴巴
股票 阿里巴巴 价格变更为: 190.0
个人投资者 Alice 收到通知: 阿里巴巴 当前价格 = 190.0
机构 高盛 收到通知: 阿里巴巴 当前价格 = 190.0
代码说明
- Investor 接口:定义了观察者的更新方法
update。 - Stock 接口:定义了主题的注册、移除、通知方法。
- ConcreteStock:具体主题,保存股票名称和价格。当价格改变时,调用
notifyInvestors通知所有注册的观察者。 - IndividualInvestor / InstitutionalInvestor:具体观察者,实现
update方法以响应价格变化。 - ObserverPatternDemo:演示了创建主题、注册观察者、价格变动触发通知以及动态取消订阅的过程。
这个案例展示了观察者模式的典型应用:主题(股票)与观察者(投资者)松耦合,投资者可以动态订阅/取消,价格变动时所有订阅者自动收到通知。