【设计模式】之观察者模式

Java设计模式——观察者模式

概述

  如果此前你已经阅读过《Head First 设计模式》这本书,那么你现在可以跳过本文的阅读。当然,如果你想温故而知新,非常欢迎。因为本文添加了一个新的实例来说明观察者模式。


版权说明

商业转载请联系作者获得授权,非商业转载请注明出处。
本文作者:Q-WHai
发表日期: 2015年11月25日
本文链接:http://blog.csdn.net/lemon_tree12138/article/details/51437883
来源:CSDN
更多内容:分类 >> 设计模式


使用环境

  不知道大家在开发的过程有没有遇到过事物之间的通信问题。之前我在开发Android程序的时候,就有遇到过这样的情况,我的ListView中的Item中的一个按钮的点击事件,这个事件我要想让外层的Fragment来得知这一活动。在开发初期的确是让人困惑,因为我不想去写太多的广播(虽然广播是比较易用且使用方便的方法,但是太多的广播必然会让程序出现意想不到的状况发生)。

  本文会从两个实例(分别是“气象观测站”和“计时器应用”)的角度出发,来做一个详细的说明。

  在实例之前,我们先来看一下,观察者模式的类图(点击查看图片来源):


图-0 观察者模式类图

  从类图中我们可以看到,观察者的内容能够获得较快的更新的原因是因为被观察者在数据有修改的时候去及时通知所有的观察者了。

实例详解

1.气象观测站

(1)过程说明

  这里我们的需求是可以动态添加或是移除一个关于气象的显示布告板。而这个布告板也是可以动态更新显示数据的。

  比方说,目前有三个布告板:天气、森林、统计数据布告板。当我们气象站中的相关数据发生变化时,我们就去更新所有这些布告板的数据。你可能会说这个简单啊,只要在每个布告板上添加一个时间触发器就好了啊。每个布告板又是相互独立的,所以还可以添加上多线程操作。对于这个问题,这样写是可以的。可是,如果现在需求里需要新增一个新的布告板,展示的是不同的内容。于是,新的布告板又要新重编写所有逻辑,气象站里也要新增一些逻辑操作来支持这种新的需求。

  这样是不是有点麻烦?要是我们的气象站对象可以一直不变就好了,因为气象站可能会是一个服务器端的代码。气象站里返回的数据是一些性质不变的数据。这里就是温度、湿度、气压。

  主题每次更新都去更新这三个值,然后去通知所有的观察者说现在数据有变化,你们现在都刷新一下布告板吧。于是,观察者们就开始更新布告板的显示。其过程如下图-1所示:


图-1 气象观察站示意图

(2)代码

主题模块

  主题对象有3个基础操作:注册新的观察者、通知所有观察者更新数据、移除取消观察的观察者对象。

下面是接口实现(Subject.Java):

  1. public interface Subject {  
  2.   
  3.     public void registerObserver(Observer o);  
  4.       
  5.     public void removeObserver(Observer o);  
  6.       
  7.     public void notifyObservers();  
  8. }  
public interface Subject {

public void registerObserver(Observer o);

public void removeObserver(Observer o);

public void notifyObservers();

}

具体主题类实现(WeatherData.java):
  1. public class WeatherData implements Subject {  
  2.   
  3.     private ArrayList observers = null// 观察者列表  
  4.       
  5.     private float temperature; // 温度  
  6.     private float humidity; // 湿度  
  7.     private float pressure; // 气压  
  8.       
  9.     public WeatherData() {  
  10.         observers = new ArrayList();  
  11.     }  
  12.       
  13.     @Override  
  14.     public void registerObserver(Observer o) {  
  15.         observers.add(o);  
  16.     }  
  17.   
  18.     @Override  
  19.     public void removeObserver(Observer o) {  
  20.         int i = observers.indexOf(o);  
  21.         if (i >= 0) {  
  22.             observers.remove(i);  
  23.         }  
  24.     }  
  25.   
  26.     /* 
  27.       更新通知所有的观察者 
  28.      */  
  29.     @Override  
  30.     public void notifyObservers() {  
  31.         for (int i = 0; i < observers.size(); i++) {  
  32.             Observer observer = (Observer) observers.get(i);  
  33.             observer.update(temperature, humidity, pressure);  
  34.         }  
  35.     }  
  36.   
  37.     public void measurementsChanged() {  
  38.         notifyObservers();  
  39.     }  
  40.       
  41.     public void setMeasurements(float temperature, float humidity, float pressure) {  
  42.         this.temperature = temperature;  
  43.         this.humidity = humidity;  
  44.         this.pressure = pressure;  
  45.         measurementsChanged();  
  46.     }  
  47. }  
public class WeatherData implements Subject {

private ArrayList observers = null; // 观察者列表

private float temperature; // 温度
private float humidity; // 湿度
private float pressure; // 气压

public WeatherData() {
    observers = new ArrayList();
}

@Override
public void registerObserver(Observer o) {
    observers.add(o);
}

@Override
public void removeObserver(Observer o) {
    int i = observers.indexOf(o);
    if (i &gt;= 0) {
        observers.remove(i);
    }
}

/**
 * 更新通知所有的观察者
 */
@Override
public void notifyObservers() {
    for (int i = 0; i &lt; observers.size(); i++) {
        Observer observer = (Observer) observers.get(i);
        observer.update(temperature, humidity, pressure);
    }
}

public void measurementsChanged() {
    notifyObservers();
}

public void setMeasurements(float temperature, float humidity, float pressure) {
    this.temperature = temperature;
    this.humidity = humidity;
    this.pressure = pressure;
    measurementsChanged();
}

}

观察者模块

更新接口Observer:

  1. public interface Observer {  
  2.   
  3.     public void update(float temperature, float humidity, float pressure);  
  4. }  
public interface Observer {

public void update(float temperature, float humidity, float pressure);

}

展示接口(DisplayElement):
  1. public interface DisplayElement {  
  2.   
  3.     public void display();  
  4. }  
public interface DisplayElement {

public void display();

}

本例中的观察者类型比较多,这里只取其中之一进行介绍。例如:StatisticsDisplay.java

  1. public class StatisticsDisplay implements Observer, DisplayElement {  
  2.   
  3.     private float temperature; // 温度  
  4.     private float humidity; // 湿度  
  5.     private Subject weatherData = null;  
  6.       
  7.     public StatisticsDisplay(Subject weatherData) {  
  8.         this.weatherData = weatherData;  
  9.         weatherData.registerObserver(this);  
  10.     }  
  11.       
  12.     @Override  
  13.     public void display() {  
  14.         System.out.println(”Statistics: ” + temperature + “F degrees and ” + humidity + “% humidity”);  
  15.     }  
  16.   
  17.     @Override  
  18.     public void update(float temperature, float humidity, float pressure) {  
  19.         this.temperature = temperature;  
  20.         this.humidity = humidity;  
  21.         display();  
  22.     }  
  23. }  
public class StatisticsDisplay implements Observer, DisplayElement {

private float temperature; // 温度
private float humidity; // 湿度
private Subject weatherData = null;

public StatisticsDisplay(Subject weatherData) {
    this.weatherData = weatherData;
    weatherData.registerObserver(this);
}

@Override
public void display() {
    System.out.println("Statistics: " + temperature + "F degrees and " + humidity + "% humidity");
}

@Override
public void update(float temperature, float humidity, float pressure) {
    this.temperature = temperature;
    this.humidity = humidity;
    display();
}

}

2.动态计时器

(1)过程说明

  关于动态计时器,这里的功能就是可以轻松添加一个或是移除一个计时器。实例里模拟的是倒计时。

  我们可以让其存在一个永远不变的量,那就是真实的时间。这个真实的时间T_S可以是使用System.currentTimeMillis()获得时间戳,也可以是一个相对的时间(时间戳本身就是相对时间)。我们把这个“真实”的,永远存在着的时间看成是一个主题(被观察者),可以被不同的观察者进行订阅。而这里的观察者就是我们所说的计时器T_O

  我们可以让T_S在每隔一个时间单位就发布一个消息,即向其所有的订阅者(观察者)说明此时时间已经改变了。订阅者们就可以做出相应的更新操作。我们可以从图-2中看到这个过程。


图-2 动态计时器控制过程示意图

(2)代码

主题模块

  我们的主题对象需要有2个基本操作,注册新的观察者、更新通知。这里我增加了一个新的操作,那就是当我们的计时器倒数结束时,我们就把这个观察者计时器从主题的观察者列表中移除。

接口实现如下(TimerSubject.java):

  1. public interface TimerSubject {  
  2.   
  3.     / 
  4.       为新的观察者实现注册服务 
  5.       
  6.       @param o 
  7.                观察者 
  8.      /  
  9.     public void registerObserver(TimerObserver o);  
  10.   
  11.     / 
  12.       移除某一个观察者对象 
  13.       
  14.       @param o 
  15.                观察者 
  16.      /  
  17.     public void removeObserver(TimerObserver o);  
  18.   
  19.     /* 
  20.       更新通知所有的观察者主题状态已经改变 
  21.      */  
  22.     public void notifyObservers();  
  23. }  
public interface TimerSubject {

/**
 * 为新的观察者实现注册服务
 *
 * @param o
 *          观察者
 */
public void registerObserver(TimerObserver o);

/**
 * 移除某一个观察者对象
 *
 * @param o
 *          观察者
 */
public void removeObserver(TimerObserver o);

/**
 * 更新通知所有的观察者主题状态已经改变
 */
public void notifyObservers();

}

主题类(NagaTimer.java)代码如下:
  1. public class NagaTimer implements TimerSubject {  
  2.   
  3.     private long mCurrentStamp = 0L;  
  4.   
  5.     private List<TimerObserver> mObservers = null;  
  6.   
  7.     public NagaTimer() {  
  8.         if (mObservers == null) {  
  9.             mObservers = new ArrayList<>();  
  10.         }  
  11.     }  
  12.   
  13.     @Override  
  14.     public void registerObserver(TimerObserver o) {  
  15.         if (mObservers != null) {  
  16.             mObservers.add(o);  
  17.         }  
  18.     }  
  19.   
  20.     @Override  
  21.     public void removeObserver(TimerObserver o) {  
  22.         if (mObservers == null) {  
  23.             return;  
  24.         }  
  25.   
  26.         mObservers.remove(o);  
  27.     }  
  28.   
  29.     /* 
  30.       更新通知所有的观察者 
  31.      */  
  32.     @Override  
  33.     public void notifyObservers() {  
  34.         if (mObservers == null || mObservers.size() == 0) {  
  35.             return;  
  36.         }  
  37.   
  38.         for (int i = 0; i < mObservers.size(); i++) {  
  39.             CountDownTimer countDownTimer = (CountDownTimer)mObservers.get(i);  
  40.             if (countDownTimer.isDone()) {  
  41.                 removeObserver(mObservers.get(i));  
  42.             } else {  
  43.                 countDownTimer.update(mCurrentStamp);  
  44.             }  
  45.         }  
  46.     }  
  47.   
  48.     private void measurementsChanged() {  
  49.         notifyObservers();  
  50.     }  
  51.   
  52.     public void setMeasurements(long currentStamp) {  
  53.         mCurrentStamp = currentStamp;  
  54.         measurementsChanged();  
  55.     }  
  56. }  
public class NagaTimer implements TimerSubject {

private long mCurrentStamp = 0L;

private List&lt;TimerObserver&gt; mObservers = null;

public NagaTimer() {
    if (mObservers == null) {
        mObservers = new ArrayList&lt;&gt;();
    }
}

@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 &lt; 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):

  1. public interface TimerObserver {  
  2.   
  3.     /* 
  4.       主题对象只做一件事情,就是更新当前时间 
  5.       
  6.       @param stamp 
  7.      */  
  8.     public void update(long stamp);  
  9. }  
public interface TimerObserver {

/**
 * 主题对象只做一件事情,就是更新当前时间
 *
 * @param stamp
 */
public void update(long stamp);

}

接口实现(TimerDisplayable.java):
  1. public interface TimerDisplayable {  
  2.   
  3.     public void display();  
  4. }  
public interface TimerDisplayable {

public void display();

}

观察者(CountDownTimer.java):
  1. public class CountDownTimer implements TimerObserver, TimerDisplayable {  
  2.   
  3.     private String mName;  
  4.     private long mStartStamp;  
  5.     private long mCountdownStamp;  
  6.     private long mCurrentStamp = 0L;  
  7.   
  8.     public CountDownTimer(String name, long countdown) {  
  9.         this.mStartStamp = System.currentTimeMillis();  
  10.         this.mName = name;  
  11.         this.mCountdownStamp = countdown;  
  12.     }  
  13.   
  14.     @Override  
  15.     public void display() {  
  16.         if (mCurrentStamp - mStartStamp <= mCountdownStamp) {  
  17.             System.out.println(getName() + ”还剩” + ((mCountdownStamp - (mCurrentStamp - mStartStamp)) / 1000) + “s”);  
  18.         }  
  19.     }  
  20.   
  21.     @Override  
  22.     public void update(long stamp) {  
  23.         mCurrentStamp = stamp;  
  24.         display();  
  25.     }  
  26.   
  27.     public boolean isDone() {  
  28.         if (mCurrentStamp - mStartStamp >= mCountdownStamp) {  
  29.             return true;  
  30.         }  
  31.   
  32.         return false;  
  33.     }  
  34.   
  35.     public String getName() {  
  36.         return mName;  
  37.     }  
  38. }  
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 &lt;= 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 &gt;= mCountdownStamp) {
        return true;
    }

    return false;
}

public String getName() {
    return mName;
}

}

运行图例

图-3 气象观测站运行示意图


图-4 计时器运行示意图


GitHub源码下载:

https://github.com/William-Hai/DesignPatternCollections


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1) 优秀的程序应该是这样的:阅读时,感觉很优雅;新增功能时,感觉很轻松;运行时,感觉很快速,这就需要设计模式支撑。2) 设计模式包含了大量的编程思想,讲授和真正掌握并不容易,网上的设计模式课程不少,大多讲解的比较晦涩,没有真实的应用场景和框架源码支撑,学习后,只知其形,不知其神。就会造成这样结果: 知道各种设计模式,但是不知道怎么使用到真实项目。本课程针对上述问题,有针对性的进行了升级 (1) 授课方式采用 图解+框架源码分析的方式,让课程生动有趣好理解 (2) 系统全面的讲解了设计模式,包括 设计模式七大原则、UML类图-类的六大关系、23种设计模式及其分类,比如 单例模式的8种实现方式、工厂模式的3种实现方式、适配器模式的3种实现、代理模式的3种方式、深拷贝等3) 如果你想写出规范、漂亮的程序,就花时间来学习下设计模式吧课程内容和目标本课程是使用Java来讲解设计模式,考虑到设计模式比较抽象,授课采用 图解+框架源码分析的方式1) 内容包括: 设计模式七大原则(单一职责、接口隔离、依赖倒转、里氏替换、开闭原则、迪米特法则、合成复用)、UML类图(类的依赖、泛化和实现、类的关联、聚合和组合) 23种设计模式包括:创建型模式:单例模式(8种实现)、抽象工厂模式、原型模式、建造者模式、工厂模式。结构型模式:适配器模式(3种实现)、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式(3种实现)。行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)2) 学习目标:通过学习,学员能掌握主流设计模式,规范编程风格,提高优化程序结构和效率的能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值