设计模式之观察者模式
什么是观察者模式
- 定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
角色:
- 抽象被观察者角色:把所有对观察者对象的引用保存在一个集合中,每个被观察者角色都可以有任意数量的观察者。被观察者提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。 - 具体被观察者角色:在被观察者内部状态改变时,给所有登记过的观察者发出通知。具体被观察者角色通常用一个子类实现。
具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用
观察者模式的场景 — 触发联动(本质)
- 当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化
- 如果更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变
- 当一个对象必须通知其他的对象,但是你又希望这个对象和其他被通知的对象是松散耦合的
怎么使用观察者模式
观察者模式的两种实现方式:
推模型
- 目标对象主动向推送目标的详细消息
拉模型
目标对象通知观察者,值传递少量的信息。如果观察者需要更具体的消息,由观察者主动到目标对象中获取。
一般在这种模型实现中,会把目标对象通过自身update方法传递给观察者
拉模型code:
import java.util.ArrayList;
import java.util.List;
/**
* 目标对象,它知道它的观察者,并添加和删除观察者的接口。
*/
public class Subject {
// 用来保存观察者对象。
private List<Observer> Observers = new ArrayList<>();
// 将观察者添加集合中
public void attach(Observer observer) {
Observers.add(observer);
}
// 将观察者重集合中删除。
public void detach(Observer observer) {
Observers.remove(observer);
}
// 向所有注册者发送消息
protected void notifyObservers() {
for (Observer observer : Observers) {
observer.update(this);
}
}
}
/**
* 观察者接口,定义一个更新的接口,给那些在目标发生改变时被通知的对象
*/
public interface Observer {
// 传入目标对象,方便获取目标对象的状态。
public void update(Subject subject);
}
public class ConcreteObserver implements Observer {
// 观察者的状态
private String observerstate;
// 获取目标类的状态同步到观察者的状态中。
@Override
public void update(Subject subject) {
observerstate = ((ConcreteSubject) subject).getSubjectstate();
System.out.println("我收到"+observerstate);
}
}
/**
* 具体目标对象,负责把有关的状态存入相应的观察者对象中
*
*/
public class ConcreteSubject extends Subject {
// 目标对象的状态
private String subjectstate;
public String getSubjectstate() {
return subjectstate;
}
public void setSubjectstate(String subjectstate) {
this.subjectstate = subjectstate;
// 在保存目标状态时通知。
this.notifyObservers();
}
}
public class Client {
public static void main(String[] args) {
//创建目标
ConcreteSubject suj = new ConcreteSubject();
//创建观察者
ConcreteObserver obs = new ConcreteObserver();
//注册观察者
suj.attach(obs);
//发布信息
suj.setSubjectstate("有新消息");
}
}
拉模型code:
/**
* 具体的观察者对象,实现更新方法,使自身和目标的状态一致
*/
public class ConcreteObserver implements Observer {
@Override
public void update(String content) {
System.out.println("我收到"+content);
}
}
/**
* 具体目标对象,负责把有关的状态存入相应的观察者对象中
*
*/
public class ConcreteSubject extends Subject {
// 目标对象的状态
private String subjectstate;
public String getSubjectstate() {
return subjectstate;
}
public void setSubjectstate(String subjectstate) {
this.subjectstate = subjectstate;
// 在保存目标状态时通知。
this.notifyObservers(subjectstate);
}
}
/**
* 观察者接口,定义一个更新的接口,给那些在目标发生改变时被通知的对象
*/
public interface Observer {
// 传入目标对象,方便获取目标对象的状态。
public void update(String content);
}
/**
* 目标对象,它知道它的观察者,并添加和删除观察者的接口。
*/
public class Subject {
// 用来保存观察者对象。
private List<Observer> Observers = new ArrayList<>();
// 将观察者添加集合中
public void attach(Observer observer) {
Observers.add(observer);
}
// 将观察者重集合中删除。
public void detach(Observer observer) {
Observers.remove(observer);
}
// 向所有注册者发送消息
protected void notifyObservers(String content) {
for (Observer observer : Observers) {
observer.update(content);
}
}
}
认识观察者模式
目标与之间的关系
- 一对一,多对一的关系,为不同的观察者设置不同的回掉方法。
单项依赖
- 观察者等待目标通知,观察者始终是被动的
命令建议
- 观察者模式又被称为发布订阅模式
- 目标接口的,建议在后面加上subject作为标识
- 观察者接口定义,建议在Observer作为标识
- 观察者接口的更新方法,建议名称为update
触发通知的时间
- 先添加后通知
观察者模式顺序示意图
- 准备阶段
- 运行阶段
- 准备阶段
通知的的顺序
- 通知的的顺序是平行的
比较
- 推模型是假定目标对象知道观察者需要的数据。
- 拉模型是目标对象不知道观察者需要的具体数据,把自身传给观察者,有观察者来取
java观察者模式的应用:
- 不需要再定义观察者和目标接口(JDK已经定义)。
- 具体的目标实现里面不需要再维护观察者的注册信息,Java中的Observable类里面已经实现。
- 触发通知的方式有一点变化,要先调用setChanged方法,这个是Java为了帮助实现更精确的触发控制而提供的功能。
- 具体观察者的实现里面,update方法其实能同时支持推模型和拉模型,这个Java在定义的时候,已经考虑。
import java.util.Observable;
import java.util.Observer;
public class ConcreteObserver implements Observer{
@Override
public void update(Observable o, Object arg) {
// System.out.println("收到了推送过来的"+arg);
System.out.println("收到了消息,拉取目标对象的消息"+((Subject)o).getContent());
}
}
import java.util.Observable;
public class Subject extends Observable{
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
//在通知之前,在java中使用Observer模式时必须调用setChanged();
this.setChanged();
//通知--推模型
//notifyObservers(content);
//通知 --拉模型
notifyObservers();
}
}
public class Client {
public static void main(String[] args) {
//创建目标
Subject suj = new Subject();
//创建观察者
ConcreteObserver obs = new ConcreteObserver();
//注册观察者
suj.addObserver(obs);
//发布信息
suj.setContent("天气不好");
}
}
为什么要用观察者模式
观察者的优点:
- 观察者模式实现了观察者和目标之间的抽象耦合
- 观察者模式实现了动态联动
- 观察者模式支持广播通信
缺点:
- 可能引起无谓的操作
观察者模式的衍生:
区别对待观察者模式:
对不同的消息通知不同的观察者,将subject类的notifyObservers()方法改为abstract具体在子类中进行判断实现。