观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会收到通知并自动更新。它常用于事件驱动系统,如 GUI 框架、发布-订阅系统等。
观察者模式的组成
观察者模式通常包含以下几个角色:
- 主题(Subject):被观察的对象,维护一组观察者,提供添加、删除观察者的方法,并在状态变化时通知所有观察者。
- 观察者(Observer):接收主题状态变化通知的对象,通常定义一个更新方法(如
update()
)。 - 具体主题(Concrete Subject):主题的具体实现,存储状态并在状态变化时通知观察者。
- 具体观察者(Concrete Observer):观察者的具体实现,定义状态更新后的具体行为。
工作原理
- 观察者向主题注册,表明自己需要接收状态变化通知。
- 主题状态发生变化时,调用通知方法,遍历所有注册的观察者,调用它们的更新方法。
- 观察者收到通知后,根据主题的状态执行相应的逻辑。
UML 类图
┌────────────────┐ ┌────────────────┐
│ Subject │ │ Observer │
├────────────────┤ ├────────────────┤
│ observers │<----->│ update() │
│ attach() │ └────────────────┘
│ detach() │
│ notify() │
└────────────────┘
↑
┌────────────────┐ ┌────────────────┐
│ConcreteSubject │ │ConcreteObserver│
├────────────────┤ ├────────────────┤
│ state │ │ state │
│ getState() │ │ update() │
└────────────────┘ └────────────────┘
代码示例(以 Java 为例)
以下是一个简单的观察者模式实现,模拟新闻发布系统:
import java.util.ArrayList;
import java.util.List;
// 观察者接口
interface Observer {
void update(String message);
}
// 具体观察者:订阅者
class Subscriber implements Observer {
private String name;
public Subscriber(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " 收到新闻: " + message);
}
}
// 主题接口
interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
// 具体主题:新闻机构
class NewsAgency implements Subject {
private List<Observer> observers = new ArrayList<>();
private String news;
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(news);
}
}
public void setNews(String news) {
this.news = news;
System.out.println("新闻更新: " + news);
notifyObservers();
}
}
// 测试代码
public class Main {
public static void main(String[] args) {
NewsAgency agency = new NewsAgency();
// 创建观察者
Subscriber sub1 = new Subscriber("张三");
Subscriber sub2 = new Subscriber("李四");
// 注册观察者
agency.attach(sub1);
agency.attach(sub2);
// 发布新闻
agency.setNews("今天是晴天!");
// 移除一个观察者
agency.detach(sub1);
// 发布新新闻
agency.setNews("明天有雨!");
}
}
输出:
新闻更新: 今天是晴天!
张三 收到新闻: 今天是晴天!
李四 收到新闻: 今天是晴天!
新闻更新: 明天有雨!
李四 收到新闻: 明天有雨!
观察者模式的特点
- 优点:
- 支持一对多的依赖关系,主题和观察者松耦合。
- 动态添加或移除观察者,灵活性高。
- 符合“开闭原则”,易于扩展新观察者。
- 缺点:
- 如果观察者数量过多,通知过程可能耗时。
- 观察者与主题间可能存在循环依赖,导致系统复杂化。
- 观察者需要自行处理更新逻辑,可能增加维护成本。
使用场景
- 当一个对象的状态变化需要通知其他对象,且这些对象是动态变化的:
- 事件监听系统(如 GUI 按钮点击事件)。
- 发布-订阅系统(如消息队列、新闻订阅)。
- 实时数据监控(如股票价格更新)。
- 需要实现松耦合的事件处理机制。
注意事项
- 通知机制:可以采用同步或异步通知,异步通知可提高性能但需注意线程安全。
- 弱引用:为避免内存泄漏,可使用弱引用存储观察者。
- 状态传递:通知时可以传递主题的完整状态或仅传递变化的部分,视需求而定。
- 与发布-订阅模式的区别:观察者模式中主题和观察者直接关联,而发布-订阅模式通常通过中间件(如消息代理)解耦。
总结
观察者模式通过建立主题与观察者间的松耦合关系,实现了状态变化的动态通知,广泛应用于事件驱动的场景。它在 GUI 框架、消息系统和实时监控中非常实用,但在设计时需注意通知效率和依赖管理,以避免性能或复杂度问题。