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

posted @ 2018-10-23 14:06  朱明龙zml  阅读(253)  评论(0编辑  收藏  举报