java设计模式之观察者模式
一、什么是观察者模式
观察者模式定义了对象之间一对多的关系, 当一个对象(被观察者)的状态改变时, 依赖它的对象都会收到通知。可以应用到发布——订阅, 变化——更新这种业务场景中。观察者和被观察者之间用松耦合的方式, 被观察者不知道观察者的细节, 只知道观察者实现了接口。事件驱动模型更加灵活,但也是付出了系统的复杂性作为代价的,因为我们要为每一个事件源定制一个监听器以及事件,这会增加系统的负担。
观察者模式的核心是先分清角色、定位好观察者和被观察者、他们是多对一的关系。实现的关键是要建立观察者和被观察者之间的联系、比如在被观察者类中有个集合是用于存放观察者的、当被检测的东西发生改变的时候就要通知所有观察者。在观察者的构造方法中将被观察者传入、同时将本身注册到被观察者拥有的观察者名单中、即observers这个list中。
二、观察者模式优点
(1)抽象主题只依赖于抽象观察者
(2)观察者模式支持广播通信
(3)观察者模式使信息产生层和响应层分离
三、观察者模式缺点
(1)如一个主题被大量观察者注册,则通知所有观察者会花费较高代价
(2)如果某些观察者的响应方法被阻塞,整个通知过程即被阻塞,其它观察者不能及时被通知
四、观察者模式实例demo----动态计时器
(1)过程说明
关于动态计时器,这里的功能就是可以轻松添加一个或是移除一个计时器。实例里模拟的是倒计时。
我们可以让其存在一个永远不变的量,那就是真实的时间。这个真实的时间T_S可以是使用System.currentTimeMillis()获得时间戳,也可以是一个相对的时间(时间戳本身就是相对时间)。我们把这个“真实”的,永远存在着的时间看成是一个主题(被观察者),可以被不同的观察者进行订阅。而这里的观察者就是我们所说的计时器T_O。
我们可以让T_S在每隔一个时间单位就发布一个消息,即向其所有的订阅者(观察者)说明此时时间已经改变了。订阅者们就可以做出相应的更新操作。我们可以从图-2中看到这个过程。
(2)代码实现
主题模块
我们的主题对象需要有2个基本操作,注册新的观察者、更新通知。这里我增加了一个新的操作,那就是当我们的计时器倒数结束时,我们就把这个观察者计时器从主题的观察者列表中移除。
接口实现如下(TimerSubject.java):
public interface TimerSubject {
/**
* 为新的观察者实现注册服务
*
* @param o
* 观察者
*/
public void registerObserver(TimerObserver o);
/**
* 移除某一个观察者对象
*
* @param o
* 观察者
*/
public void removeObserver(TimerObserver o);
/**
* 更新通知所有的观察者主题状态已经改变
*/
public void notifyObservers();
}
主题类(NagaTimer.java)代码如下:
public class NagaTimer implements TimerSubject {
private long mCurrentStamp = 0L;
private List<TimerObserver> mObservers = null;
public NagaTimer() {
if (mObservers == null) {
mObservers = new ArrayList<>();
}
}
@Override
public void registerObserver(TimerObserver o) {
if (mObservers != null) {
mObservers.add(o);
}
}
@Override
public void removeObserver(TimerObserver o) {
if (mObservers == null) {
return;
}
mObservers.remove(o);
}
/**
* 更新通知所有的观察者
*/
@Override
public void notifyObservers() {
if (mObservers == null || mObservers.size() == 0) {
return;
}
for (int i = 0; i < mObservers.size(); i++) {
CountDownTimer countDownTimer = (CountDownTimer)mObservers.get(i);
if (countDownTimer.isDone()) {
removeObserver(mObservers.get(i));
} else {
countDownTimer.update(mCurrentStamp);
}
}
}
private void measurementsChanged() {
notifyObservers();
}
public void setMeasurements(long currentStamp) {
mCurrentStamp = currentStamp;
measurementsChanged();
}
}
观察者模块
作为观察者,它可以去根据主题的改变进行一些合理的更新操作。本实例中是时间上的倒数。所以需要有一个更新操作和展示操作。
接口实现(TimerObserver.java):
public interface TimerObserver {
/**
* 主题对象只做一件事情,就是更新当前时间
*
* @param stamp
*/
public void update(long stamp);
}
接口实现(TimerDisplayable.java):
public interface TimerDisplayable {
public void display();
}
观察者(CountDownTimer.java):
public class CountDownTimer implements TimerObserver, TimerDisplayable {
private String mName;
private long mStartStamp;
private long mCountdownStamp;
private long mCurrentStamp = 0L;
public CountDownTimer(String name, long countdown) {
this.mStartStamp = System.currentTimeMillis();
this.mName = name;
this.mCountdownStamp = countdown;
}
@Override
public void display() {
if (mCurrentStamp - mStartStamp <= mCountdownStamp) {
System.out.println(getName() + "还剩" + ((mCountdownStamp - (mCurrentStamp - mStartStamp)) / 1000) + "s");
}
}
@Override
public void update(long stamp) {
mCurrentStamp = stamp;
display();
}
public boolean isDone() {
if (mCurrentStamp - mStartStamp >= mCountdownStamp) {
return true;
}
return false;
}
public String getName() {
return mName;
}
}
运行结果如下:
GitHub源码下载:
https://github.com/William-Hai/DesignPatternCollections