这是模仿“应用宝”的demo,这里提取出一个功能来讲解:在首页中,选择一个App点进去,进入到详细介绍页面,点击页面最下方下载按钮,进度条变化开始下载。再次回到首页,首页中的进度条 与 详细页面的进度条 同步更新 !相信熟悉设计模式的人已经想到这就是 “观察者模式”。
由于关于这个理论方面的博客实在是太多了,此篇将重点放在应用上,但下面还是简单介绍一下此模式。
一 .“观察者模式”简介
(此模式简介部分来源于书籍《Android源码涉及模式解析与实战》)
1. 定义
观察者模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
2.意义
此模式最重要的作用就是 解耦!将被观察者和观察者解耦,使得它们之间的依赖性更小。
3. UML图
(1)Subject:就是“被观察者”的角色,它将所有观察者对象引用保存在一个集合中,每个“观察者”都可以有任意数量的 “观察者”,而自身提供一个接口,可以增加和删除观察者对象。
(2)Observer:抽象“观察者”,它定义了一个更新接口,使得在“被观察者”状态改变时通知自己,可及时更新。
(3)ConcreteObserver: 具体的”观察者“,该角色实现抽象“观察者”角色所定义的更新接口,以便在“被观察者”状态改变时更新自身状态。
二. “观察者模式”实现
以上的理论大家并不陌生,可是理论部分知晓的再熟悉,未融入到实际应用中也是枉然!还是回到“应用宝软件下载”实际例子中,注意上述简介中的UML图分析其实大致已将需要实现的步骤给出来了,以下:
1. “被观察者” Subject 对象
首先,我们需要一个“下载管理器”DownloadManager,也就是观察者模式中的“被观察者”。(注意:一般这里的“被观察者”数量唯一!)一般像这种全局的管理器,需要将它设置成 “单例模式”,在这里使用 饿汉形式。
/**
* 下载管理器 [观察者模式]
* DownloadManager: 被观察者, 有责任通知所有观察者状态和进度发生变化
*
* Created by gym on 2016/9/20.
*/
public class DownloadManager {
//单例模式---饿汉
private static DownloadManager mDM = new DownloadManager();
private DownloadManager(){};
public static DownloadManager getInstance(){
return mDM;
}
再次回顾逻辑,如上图所示,可得知在具体页面点击的“下载”,进度可同时在详细页面和首页中展示,相反亦是。从设计模式的角度来讲,这种就是“观察者设计模式”。也就是说 下载 的逻辑只在一处去做,但是有多个界面在 监听 其进度。
观察者设计模式有两个元素,一个叫做“被观察者”,另一个是“观察者”。一般而言 被观察者 只有一个,而观察者的数量却不止一个。举个栗子:在大街上,一个老太太摔倒了,路上行人不免开始围观,而些许路人要事在身,匆匆看一眼便走了,有些闲人驻足观望。这时,我们的老太太便成为了 “被观察者”,这数量不定的路人就是 “观察者”。 而 “观察者”中有两个方法: 注册观察者、注销观察者。这就是“观察者”的设计模式。
2. 声明观察者的接口
首先我们需要声明一个 “观察者”,这里可以用一个 接口 来表示,在例子中,路人观察者观察的是老太太伤势如何等等。 回到实际逻辑来,这里的观察者 需要观察什么?也就是说接口中需要定义什么方法? 下载状态的变化 和 下载进度的变化。
/**
* 1.声明观察者的接口
*/
public interface DownloadObserver{
//下载状态发生变化
public void onDownloadStateChanged(DownloadInfo info);
//下载进度发生变化
public void onDownloadProgressChanged(DownloadInfo info);
}
3.注册、注销观察者 方法
这里声明完观察者的接口后,还要额外写两个方法:之前提到的 注册和注销观察者 方法。而所谓注册和注销 其实就是 增加和减少 一个观察者罢了,所以在这里我们维护一个观察者的集合,对集合进行操作 就是 注册和注销观察者方法的本质。
(1). 注册观察者:参数需要将要注册的观察者传进来,并且做判断:该观察者是否为空 且 集合中是否已经包含,再向集合中增加观察者。
(2). 注销观察者:参数也需要将注销的观察者传进来,判断 该观察者不为空 且 集合确实包含它,才向集合中移除该观察者。
//4.观察者集合
private ArrayList<DownloadObserver> mObservers = new ArrayList<DownloadObserver>();
//2.注册观察者
public void registerObserver(DownloadObserver observer){
if(observer!= null && mObservers.contains(observer)){
mObservers.add(observer);
}
}
//3.注销观察者
public void unregisterObserver(DownloadObserver observer){
if(observer!= null && mObservers.contains(observer)){
mObservers.remove(observer);
}
}
4. 通知状态变化方法(回调 观察者 接口) !!!
可是现在这些 观察者 如何收到变化呢?当然就需要 被观察者 通知给大家。这时还是回到我上面举得例子 (我们不要考虑天底下所有的老太太摔跤都是为了讹钱),老太太在地上缓了一会,深知大家都以为她是讹钱的,自己慢慢爬了起来,告诉众人没事没事,都散了吧,这时路人了解了情况,开始散开各行其事去了。而这里就是 被观察者通知给观察者,观察者做出反应 的事例。
所以,一开始定义的 下载管理器DownloadManager类就是 被观察者,谁需要去观察它呢?首页和某个App的详情页面会去观察它!从它这里拿到 下载的进度,对其显示。 此时,该观察者也就有责任去通知大家 状态发生变化了。在DownloadManager类中需要再增加两个方法(观察者接口中是两个方法),来通知该状态的变化。
(1)通知下载状态发生变化:状态改变时,对所有的观察者(mObservers 集合)遍历,调用接口中对应状态改变的方法。(这里是onDownloadStateChanged)
(2)通知下载进度发生变化:如上所示。
// 5.通知下载状态发生变化
public synchronized void notifyDownloadStateChanged(DownloadInfo info) {
for (DownloadObserver observer : mObservers) {
observer.onDownloadStateChanged(info);
}
}
// 6.通知下载进度发生变化
public synchronized void notifyDownloadProgressChanged(DownloadInfo info) {
for (DownloadObserver observer : mObservers) {
observer.onDownloadProgressChanged(info);
}
}
三. 总结
1.逻辑总结
暂且回顾以上4点,也就是DownloadManager类:
(1)“被观察者” Subject 对象
(2) 声明“观察者”的接口
(3)注册、注销”观察者“方法
(4)通知状态变化方法(回调 “观察者” 接口)
以上4点都是在DownloadManager,也就是被观察者中的逻辑,把逻辑整体串一遍:我们的观察者只会有一个,首先在DownloadManager中给 被观察者 限制为单例模式;再声明 观察者 接口(定义好观察者观察的状态),维护一系列的 观察者集合,再加两个注册和注销观察者的方法,算是对 观察者 的管理;之后当观察者接口中 所关注的状态有所改变时,被观察者中有义务立刻通知,再加两个方法,即在新增方法中调用 观察者接口中对应状态改变的 接口。
2. 观察者模式 与 回调
总体就是,一旦 被观察者的状态改变后,观察者中的方法就会被调用。理解之后,你会发现,观察者模式 就是 在 回调!先声明接口,再声明方法,把监听传过来,只不过我们这里的监听不止一个,而是用集合 维护了 一堆监听,然后再适当的时机马上去通知回调。两者有异曲同工之妙,非要说区别的话,”回调“中只有一个监听者,而这里的“观察者设计模式”中有许多个监听者。
以上,这里只是把此功能 的 核心骨架 抽取出来讲解,具体还有 “观察者接口”实现的部分等等,但是核心思想模式的理解是重要的一步!
希望对你们有帮助:)