Observer --- 观察者模式 :定义了对象之间的一对多依赖, 当一个对象改变状态的时候,他所有的依赖都会收到通知并自动更新。
观察者模式又叫:
发布 - 订阅 (Publish/Subscribe)模式
模型 - 视图 (Model - View)模式
源 - 监听器 (Source - Listener)模式
从属者(Dependents)模式
使用原因:
减少对象之间的耦合,利于系统的复用。低耦合的对象之间能维持行动的协调一致,保证高度的协作。
涉及的角色:
抽象主题(Subject)角色: 把所有的观察者的引用保存在一个集合(例如:ArrayList)中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫抽象被观察者(Observable)角色。
具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable) 角色。
抽象观察者(Observer)角色:为所有具体观察者定义一个接口,在得到主题的通知时更新自己。更新自己的接口叫更新接口。
具体观察者(ConcreteObserver)角色:存储与主题的状态自洽的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者可以保持一个指向具体主题对象的引用。
推模型 和 拉模型
推模型:
主题对象主动向观察者推送主题的详细信息, 不管观察者是否需要, 推送的信息通常是主题对象的全部或部分数据。
拉模型:
主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把 *主题对象自身* 通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
推模型:
观察者模式运用 -- 订阅报纸
/**
* 抽象主题
*/
public interface Subject {
void registerObserver(Observer obserber);
void removeObserver(Observer obserber);
void notifyObservers();
}
/**
* 具体主题
*/
public class Maganize implements Subject {
// 存储登记到观察者
private List<Observer> obserbers;
// 观察的变量
private String flag;
public Maganize() {
obserbers = new ArrayList<Observer>();
}
public void registerObserver(Observer obserber) {
obserbers.add(obserber);
}
public void removeObserver(Observer obserber) {
int i = obserbers.indexOf(obserber);
if (i > 0) {
observers.remove(i);
}
}
public void notifyObservers() {
// 遍历观察者,循环通知
for (int i = 0; i < obserbers.size(); i++ ) {
Observer obserber = (Observer)obserbers.get(i);
// 调用观察者的更新接口
obserber.updte(flag);
}
}
// 发布新杂志,通知观察者更新
public void publishNewMaganize(String flag) {
this.flag = flag;
notifyObservers();
}
}
/**
* 抽象观察者
*/
public interface Observer {
// 更新接口
void update(Stirng flag);
}
/**
* 具体观察者 : Yiyi
*/
public class Yiyi implements Observer {
private String flag;
@Override
public void update (String flag) {
this.flag = flag;
System.out.println("I an Yiyi, now reading maganize: " + flag);
}
}
/**
* 具体观察者 : Erer
*/
public class Erer implements Observer {
private String flag;
@Override
public void update (String flag) {
this.flag = flag;
System.out.println("I an Erer, now reading maganize: " + flag);
}
}
/**
* 具体观察者 : Sansan
*/
public class Sansan implements Observer {
private String flag;
@Override
public void update (String flag) {
this.flag = flag;
System.out.println("I an Sansan, now reading maganize: " + flag);
}
}
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
// 创建主题对象
Maganize maganize = new Maganize();
// 创建观察者
Observer yiyi = new Yiyi();
Observer erer = new Erer();
Observer sansan = new Sansan();
// 将观察者登记到主题对象上
maganize.registerObserver(yiyi);
maganize.registerObserver(erer);
maganize.registerObserver(sansan);
// 改变主题对象的状态
maganize.publishNewMaganize("new Boy");
}
}
拉模型:
/**
* 抽象观察者
*/
public interface Observer {
/**
* update 方法
*
* @param subject 传入主题对象, 方便获取响应的主题对象的状态
*/
void update(Subject subject);
}
/**
* 具体观察者 : Yiyi
*/
public class Yiyi implements Observer {
private String flag;
@Override
public void update (Subject subject) {
this.flag = ((Maganize)subject).getFlag();
System.out.println("I an Yiyi, now reading maganize: " + flag);
}
}
/**
* 具体观察者 : Erer
*/
public class Erer implements Observer {
private String flag;
@Override
public void update (Subject subject) {
this.flag = ((Maganize)subject).getFlag();
System.out.println("I an Erer, now reading maganize: " + flag);
}
}
/**
* 具体观察者 : Sansan
*/
public class Sansan implements Observer {
private String flag;
@Override
public void update (Subject subject) {
this.flag = ((Maganize)subject).getFlag();
System.out.println("I an Sansan, now reading maganize: " + flag);
}
}
/**
* 具体主题
*/
public class Maganize implements Subject {
private List<Observer> obserbers;
private String flag;
public Maganize() {
obserbers = new ArrayList<Observer>();
}
public void registerObserver(Observer obserber) {
obserbers.add(obserber);
}
public void removeObserver(Observer obserber) {
int i = obserbers.indexOf(obserber);
if (i > 0) {
observers.remove(i);
}
}
public void notifyObservers() {
// 遍历观察者,循环通知
for (int i = 0; i < obserbers.size(); i++ ) {
Observer obserber = (Observer)obserbers.get(i);
// 调用观察者的更新接口, 把主题实例传给观察者的更新接口,方便观察者更新自己想要的内容
obserber.updte(this);
}
}
// 发布新杂志,通知观察者更新
public void publishNewMaganize(String flag) {
this.flag = flag;
notifyObservers();
}
public String getFlag() {
return this.flag;
}
}
两种模式的比较:
1. 推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。
2. 推模型可能会使观察者对象难以复用,因为观察者的update()方法是按需要定义的 参数,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update方法,或者干脆重新实现观察者;而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是对题对象能传递的最大数据集合了,基本可以适应各种情况的需要。
优缺点:
优点:
1. 观察者模式可以实现表示层和逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口 ,使的可以有各种各样的不同的表示层作为具体观察者角色。
2. 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
3. 观察者模式支持广播通信。
4. 观察者模式符合"开闭原则"的要求。
缺点:
1. 如果一个观察目标对象有很多直接和简介的观察者的话,将所有的观察者都通知到会话费很多时间。
2. 如果在观察者和观察木变直接有循环依赖的话,观察目标会触发他们之间进行循环调用,可能导致系统崩溃。
3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
模式应用:
凡是涉及到一对一 或 一对多的对象交互场景都可以使用观察者模式。