介绍
观察者模式(Observer Pattern)又称为发布-订阅模式,是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,其相关依赖对象都能够自动收到通知并进行相应的更新。
观察者模式的核心思想是将观察者对象注册到被观察者对象上,当被观察者对象的状态发生变化时,它会通知所有已注册的观察者对象进行更新。这种通知机制是基于松散耦合的,被观察者对象并不知道具体的观察者对象是谁,而观察者对象也不需要知道被观察者对象的细节,它们之间只通过定义好的接口进行通信。
模式角色
(1) 抽象主题(Subject)
定义了被观察者对象的接口,包含了添加、删除和通知观察者的方法。
(2) 具体主题(Concrete Subject)
实现了抽象主题接口,维护观察者对象的列表,并在状态发生变化时通知观察者。
(3) 抽象观察者(Observer)
定义了观察者对象的接口,包含了接收通知和进行更新的方法。
(4) 具体观察者(Concrete Observer)
实现了抽象观察者接口,具体实现观察者的更新方法,以便在接收到通知时进行相应的操作。
代码示例
package com.behavioral;
import java.util.ArrayList;
import java.util.List;
/**
* 观察者(Observer)模式
* 又称为发布-订阅模式
*/
public class ObserverPattern {
public static void main(String[] args) {
// 创建具体主题对象
Subject subject = new ConcreteSubject();
// 创建具体观察者对象
Observer observer1 = new ConcreteObserver("observer1");
Observer observer2 = new ConcreteObserver("observer2");
// 注册观察者
subject.registerObserver(observer1);
subject.registerObserver(observer2);
// 被观察者改变状态,并向已注册的观察者发布通知
subject.setState(100);
subject.notifyObservers();
}
}
/**
* 抽象主题(Subject)接口,用于注册、移除和通知观察者
*/
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
void setState(int state);
int getState();
}
/**
* 具体主题(ConcreteSubject)类,实现抽象主题接口,并维护观察者列表
*/
class ConcreteSubject implements Subject {
private final List<Observer> observers = new ArrayList<>();
private int state;
@Override
public void setState(int state) {
this.state = state;
}
@Override
public int getState() {
return state;
}
@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);
}
}
}
/**
* 抽象观察者(Observer)接口,用于接收通知和更新
*/
interface Observer {
void update(ConcreteSubject subject);
}
/**
* 具体观察者(ConcreteObserver)类,实现抽象观察者接口
*/
class ConcreteObserver implements Observer {
private final String observerName;
ConcreteObserver(String observerName) {
this.observerName = observerName;
}
@Override
public void update(ConcreteSubject subject) {
int state = subject.getState();
System.out.println(observerName + " get notification and subject state changed to: " + state);
}
}
应用场景
(1) 通知多个对象
当一个对象的状态变化需要通知其他多个对象,并且这些对象的具体数量和身份可能在运行时发生变化时,可以使用观察者模式。例如,一个新闻发布者需要通知多个订阅者,或者一个股票交易系统需要通知多个观察者。
(2) 影响多个对象
当一个对象的改变需要影响其他对象,但是被影响的对象不需要知道是哪个对象对其进行了改变时,可以使用观察者模式。例如,一个温度传感器检测到温度变化后,通知给显示器和日志记录器,但这些对象不需要知道具体是哪个传感器发出的通知。
(3) 触发一系列操作
当一个对象的改变需要触发一系列的操作,而且这些操作可能位于不同的对象中,可以使用观察者模式。例如,一个订单状态发生变化后,需要更新库存、发送邮件通知和生成报表等操作。
(4) 多个依赖
当需要在对象间建立一种一对多的依赖关系,使得被观察者对象的改变能够通知到所有观察者对象时,可以使用观察者模式。例如,一个用户界面中的控件需要根据数据模型的变化进行更新。
优缺点
(1) 观察者模式的优点
- 松散耦合:被观察者对象和观察者对象之间的耦合度低,它们可以独立变化,互不影响。
- 可扩展性:可以方便地添加新的观察者对象和被观察者对象,扩展系统的功能。
- 规范了对象间的关系:观察者模式明确了被观察者对象和观察者对象之间的关系,使系统更加清晰。
- 支持广播通信:被观察者对象可以同时通知多个观察者对象,实现了一对多的通信方式。
(2) 观察者模式的缺点
- 如果观察者对象较多或者观察者对象之间的处理逻辑复杂,可能会导致性能问题。
- 观察者模式可能导致对象间的循环依赖,需要注意解决循环引用的问题。
Android中的应用-ContentObserver
ContentObserver即内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它,并执行相应的操作(如刷新页面)。
ContentObserver实现就是基于观察者模式的。使用到的对象如下:
- ContentService:具体主题(Concrete Subject),当发现数据源变化时,会通知所有注册该数据源的ContentObserver;
- ContentObserver:抽象观察者;
- MyContentObserver:用户继承自ContentObserver的具体观察者,创建时需要指明被观察数据的uri,并且将自身注册到ContentService。
数据源的变化会由ContentProvider调用getContentResolver().notifyChange()通知到ContentService,之后再由ContentService调用到MyContentObserver。