设计模式之观察者模式
Posted wei57960
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之观察者模式相关的知识,希望对你有一定的参考价值。
前言
生活中我们从牛奶厂家订阅了牛奶后,会有快递员在每天早晨给所有订阅牛奶的家庭送牛奶来。如果我们退订了之后,我们之后也不会收到牛奶。观察者模式就类似这样的一个场景,可以把牛奶场景定义为主题,客户理解为观察者。
除了主题主动的"推送"数据给观察者,观察者能否从主题中主动的 "拉取" 数据呢,事实上也是可以做到的。
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
观察者模式的UML
代码实现
// 主题
public interface Subject {
// 观察者注册
void register(Observer observer);
// 观察者退订
void remove(Observer observer);
// 通知所有观察者
void notifyObservers();
}
// 观察者
public interface Observer {
// 观察者收到数据后进行业务逻辑处理的统一接口
void update(Object data);
}
// 具体主题
public class ConcreteSubject implements Subject {
// 维护一个观察者数组
private List<Observer> observers = new ArrayList<>();
private Object data;
@Override
public void register(Observer observer) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}
@Override
public void remove(Observer observer) {
if (observers.contains(observer)) {
observers.remove(observer);
}
}
// 具体逻辑实现,逐个调用观察者update() 方法
@Override
public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) {
observers.get(i).update(data);
}
}
public void setData(Object data) {
this.data = data;
// 数据改变时通知观察者
notifyObservers();
}
}
// 具体观察者
public class ConcreteObserver implements Observer {
// 维护一个主题 便于退订
private Subject subject;
public ConcreteObserver(Subject subject) {
this.subject = subject;
subject.register(this);
}
// 退订
public void remove() {
subject.remove(this);
}
@Override
public void update(Object data) {
System.out.println("i am concrete observer update [ " + data + " ] now");
}
}
public class Test {
public static void main(String[] args) {
ConcreteSubject concreteSubject = new ConcreteSubject();
ConcreteObserver concreteObserver = new ConcreteObserver(concreteSubject);
concreteSubject.setData("1");
concreteSubject.setData("2");
// 退订后不再收到通知
concreteObserver.remove();
concreteSubject.setData("11");
}
}
// 输出:
i am concrete observer update [ 1 ] now
i am concrete observer update [ 2 ] now
JDK 中的观察者模式
JDK 中对于观察者模式的实现让观察者可以主动拉取一些数据,不仅是主题的推送。主要通过Observable (可被观察的)类和 Observer 接口实现。
使用方式: 我们对上面写的代码用 JDK 里面的方式进行改造 然后在代码注释中进行注释讲解使用。
import java.util.Observable;
// 实际主题通过继承 Observable 的方式
public class JDKConcreteSubject extends Observable {
// 不再管理 Observer 们,超类Observable已经帮我们做了
// 要传输的数据
private Object data;
public void setData(Object data) {
this.data = data;
// 想要通知所有的观察者们前需要调用 setChanged() 方法。
// setChanged() 方法可以让我们更灵活的通知观察者们,观察者们不用那么敏感的感受到所有的数据。
// 比如我们数据减少了 5% 不想通知观察者,在减少 20% 时再通知观察者们
setChanged();
// 通知所有观察者们
notifyObservers(data);
}
// 用于观察者们拉取数据 当然 数据粒度可以控制
public Object getData(){
return this.data;
}
}
import java.util.Observable;
import java.util.Observer;
// 具体观察者实现 Observer 接口
public class JDKConcreteObserver implements Observer {
// 维护 JDK 方式的主题
Observable observable;
public JDKConcreteObserver(Observable observable) {
this.observable = observable;
// 订阅
observable.addObserver(this);
}
/**
* @param o 主题 可用于让观察者知道是哪一个主题来通知的
* @param arg notifyObservers(Object arg) 中的数据对象
*/
@Override
public void update(Observable o, Object arg) {
// 判断是哪一个主题通知
if (o instanceof JDKConcreteSubject) {
JDKConcreteSubject jdkConcreteSubject = (JDKConcreteSubject) o;
Object data = jdkConcreteSubject.getData();
System.out.println("get data from subject " + data);
}
System.out.println("get data from arg " + arg);
}
}
JDK 中的观察者模式需要注意的点:
- 通过继承的方式,限制了复用的潜力
- 不能依赖于观察者被通知的次序
(通知顺序不依赖于注册的顺序),注册顺序不能影响通知顺序
References
- 《Head First 设计模式》
以上是关于设计模式之观察者模式的主要内容,如果未能解决你的问题,请参考以下文章