观察者模式(Observer Pattern)
概念
观察者模式:定义了对象之间的一对多个关系,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
组成
图片来源wiki
观察者模式由 主题 和 观察者 组成。
接口主题类(Subject):主题接口,对象使用该接口成为观察者,主题是有状态的对象。
具体主题类(ConcreteSubject):该类主要实现了 notifyObserver 方法,保证在主题更新时通知所有的观察者。
接口观察者类(Observer):所有观察者必须实现的一个接口,该接口一般仅有 update 方法,当主题改变时被调用。
具体观察类(ConcreteObserver):该类主要实现了 update 方法,并维护一个指向主题类的引用。
例子:
时间主题,假设北京时间为标准时间,每个地方都要根据北京时间来调整本地时间。那么北京时间是主题,地方是观察者。
时间主题接口类
package DesignPattern.Strategy.Observer;
public interface TimeSubject {
public void registerObserver(TimeObserver to);
public void removeObserver(TimeObserver to);
public void notifyObservers();
}
时间观察者接口类
package DesignPattern.Strategy.Observer;
public interface TimeObserver {
public void update(int hour, int minute, int second);
}
展示接口(每个地方展示时间方式不同)
package DesignPattern.Strategy.Observer;
public interface Display {
public void display();
}
具体时间主题类:
package DesignPattern.Strategy.Observer;
import java.util.ArrayList;
public class TimeConcreteSubject implements TimeSubject {
private ArrayList observers;
private int hour;
private int minute;
private int second;
public TimeConcreteSubject() {
observers = new ArrayList();
}
/* 注册观察者 */
public void registerObserver(TimeObserver to) {
observers.add(to);
}
/* 移除观察者 */
public void removeObserver(TimeObserver to) {
observers.remove(to);
}
/* 通知所有观察者 */
public void notifyObservers() {
for (int i = 0; i < observers.size(); ++i) {
TimeObserver to = (TimeObserver) observers.get(i);
to.update(hour, minute, second);
}
}
public void timeChange(int hour, int minute, int second) {
this.hour = hour;
this.minute = minute;
this.second = second;
notifyObservers();
}
}
具体时间观察者类:
package DesignPattern.Strategy.Observer;
public class TimeConcreteObserver implements TimeObserver,Display {
private int hour;
private int minute;
private int second;
private TimeSubject timeSubject;
public TimeConcreteObserver(TimeSubject timeSubject) {
this.timeSubject = timeSubject;
timeSubject.registerObserver(this);
}
/* 观察者接受主题更新 */
public void update(int hour, int minute, int second) {
this.hour = hour;
this.minute = minute;
this.second = second;
}
public void display() {
System.out.println("时:" + hour + " 分:" + minute + " 秒:" + second);
}
}
测试:
package DesignPattern.Strategy.Observer;
public class TimeStation {
public static void main(String []args) {
TimeConcreteSubject timeConcreteSubject = new TimeConcreteSubject();
TimeConcreteObserver timeConcreteObserver = new TimeConcreteObserver(timeConcreteSubject);
timeConcreteSubject.timeChange(10, 50, 29);
timeConcreteObserver.display();
}
}
注意:
观察者模式有两种,上面演示的是 推,还有一种观察者主动 拉。(主题中加入 notifyOneObserver(Observer o) 即可)
Java JDK 中内置了观察者模式,我们可直接使用。
适用场景
- 当其中一个对象的变更会 影响 其他对象,却又不知道 有多少 对象必须被同时变更时。
- 当对象应该有能力通知其他对象,又不应该知道其他对象的实做细节时。
- 当抽象个体有两个互相依赖的层面时。封装这些层面在单独的对象内将可允许程序员单独地去变更与重复使用这些对象,而不会产生两者之间交互的问题。
优缺点
优点:
- 观察者模式可以实现表示层和数据逻辑层的分离。观察者模式通常与 MVC 范式有关系。在 MVC 中,观察者模式被用来降低 model 与 view 的耦合程度。
- 观察者和主题之间是松耦合的方式结合。
缺点:
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
设计原则:
找出程序中会变化的方面,然后将其和固定不变的方面分离。
针对接口编程,不针对实现编程。
多用组合,少用继承。
参考:
wiki百科
Head First 设计模式