定义
观察者模式(Observer Pattern)也称发布订阅模式。
观察者模式的英文定义如下:
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
意思是:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
以生活中的例子来说,就像我们订阅报纸一样,每天有多少人订阅,当有新报纸发布的时候,就会有多少人收到新发布的报纸,这种模式就是订阅 - 发布模式,而报社和订阅者就满足定义中说是的,一对多的依赖关系。
“观察者模式” 这个词可能不太好理解,但如果用 “发布 — 订阅模式” 来替代的话,就相对好理解一些。
小贴士:本文会采用“观察者模式”来编写内容,但读者可以用“发布 - 订阅模式”来理解本文的内容,两者所说的是同一种模式。
组成角色
观察者模式包含如下角色:
- 抽象主题(Subject)角色:该角色又称为 “发布者” 或” 被观察者 “,可以增加和删除观察者对象;
- 具体主题(Concrete Subject)角色:该角色又称为 “具体发布者” 或 “具体被观察者”,它将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过(关联了观察关系)的观察者发出通知;
- 抽象观察者(Observer)角色:该角色又称为 “订阅者”,定义一个接收通知的接口,在得到主题的通知时更新自己;
- 具体观察者(Concrete Observer)角色:该角色又称为 “具体订阅者”,它会实现一个接收通知的方法,用来使自身的状态与主题的状态相协调。
角色之间的 UML 关系图如下:
观察者模式代码实现
抽象主题(发布者接口)
/**
* 抽象主题(发布者接口)
*/
interface Subject {
// 添加观察者(订阅者)
public void attach(Observer o);
// 删除观察者(订阅者)
public void detach(Observer o);
// 通知所有观察者(订阅者)
public void notifyObservers();
}
具体主题(发布者)
/**
* 具体主题(发布者)
*/
class ConcreteSubject implements Subject {
// 存放观察者(订阅者)
private List<Observer> list = new ArrayList<Observer>();
@Override
public void attach(Observer o) {
// 添加观察者(订阅者)
list.add(o);
}
@Override
public void detach(Observer o) {
// 删除观察者(订阅者)
list.remove(o);
}
@Override
public void notifyObservers() {
// 通知所有观察者(订阅者)
for (Observer o : list) {
o.update();
}
}
/**
* 通知方法
*/
public void change() {
this.notifyObservers();
}
}
抽象观察者(订阅者接口)
/**
* 抽象观察者(订阅者接口)
*/
interface Observer {
public void update();
}
具体观察者(订阅者)
/**
* 具体观察者(订阅者)
*/
class ConcreteObserver implements Observer {
@Override
public void update() {
// 主题有更新之后,执行的具体订阅(通知)方法
System.out.println("我收到了通知~");
}
}
客户端(调用)
/**
* 观察者模式
*/
public class Client {
public static void main(String[] args) {
// 创建主题(发布者)
ConcreteSubject subject = new ConcreteSubject();
// 创建观察者(订阅者)
Observer observer = new ConcreteObserver();
// 关联订阅
subject.attach(observer);
// 改变主题(发布者)状态,发送通知
subject.change();
}
}
程序执行结果如下:
我收到了通知~
从以上代码可以看出,当主题(ConcreteSubject)的状态发生变化时,就会触发通知方法,通知方法会通知所有的观察者对象(ConcreteObserver),这样就完成了整个发布 — 订阅的过程。
优缺点
观察者模式的优点:
- 观察者和被观察者之间,实现了抽象耦合。被观察者角色所知道的只是一个具体观察者集合,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体的观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密的耦合在一起,因此它们可以属于不同的抽象化层次,且都非常容易扩展;
- 此模式为广播模式,所有的观察者只需要订阅相应的主题,就能收到此主题下的所有广播。
观察者模式的缺点:
- 观察者只知道被观察者会发生变化,但不知道何时会发生变化;
- 如果主题之间有循环依赖,会导致系统崩溃,所以在使用时要特别注意此种情况;
- 如果有很多个观察者,则每个通知会比较耗时。
应用场景
使用观察模式的典型应用场景如下:
- 关联行为的场景,例如,在一个系统中,如果用户完善了个人资料,就会增加积分、添加日志、开放一些功能权限等,就比较适合用观察者模式;
- 消息队列,例如,需要隔离发布者和订阅者,需要处理一对多关系的时候。
使用实例
以生活中的读者订阅为例,假设,读者 A 和 读者 B 订阅了某平台的图书,当有新的图书发布时就会给两位读者发送图书,实现代码如下。
读者接口和实现类
/**
* 读者接口(订阅接口)
*/
interface IReader {
public void update(String bookName);
}
/**
* 读者类(订阅者)
*/
class Reader implements IReader {
private String name;
public Reader(String name) {
this.name = name;
}
@Override
public void update(String bookName) {
System.out.println(name + "-收到了图书:" + bookName);
}
}
平台接口和实现类
/**
* 平台接口(发布方接口)
*/
interface IPlatform {
public void attach(IReader reader);
public void detach(IReader reader);
public void notifyObservers(String bookName);
}
/**
* 具体平台类(发布方)
*/
class Platform implements IPlatform {
// 存放读者(订阅者)
private List<IReader> list = new ArrayList();
@Override
public void attach(IReader reader) {
// 添加读者(订阅者)
list.add(reader);
}
@Override
public void detach(IReader reader) {
// 删除读者(订阅者)
list.remove(reader);
}
@Override
public void notifyObservers(String bookName) {
// 通知所有读者(订阅者)
for (IReader reader : list) {
reader.update(bookName);
}
}
/**
* 通知方法
*/
public void change(String bookName) {
this.notifyObservers(bookName);
}
}
客户端(调用)
public class Client {
public static void main(String[] args) {
// 创建图书平台(发布者)
Platform platform = new Platform();
// 创建读者 A(订阅者)
Reader reader = new Reader("A");
// 读者 A 订阅图书通知
platform.attach(reader);
// 创建读者 (订阅者)
Reader reader2 = new Reader("B");
// 读者 B 订阅图书通知
platform.attach(reader2);
platform.change("《Java面试全解析》");
}
}
程序执行结果如下:
A - 收到了图书:《Java 面试全解析》
B - 收到了图书:《Java 面试全解析》
总结
观察者模式就是一个发布者对应多个订阅者的模式,发布者对应的角色就是主题(Subject),而订阅者对应的角色就是观察者(Observer),只要订阅者订阅了发布者(对象),当发布者的状态发生变化时,就会通知所有的订阅者。