死磕观察者模式(1)

来源:互联网 发布:臻云创投投资人工智能 编辑:程序博客网 时间:2024/06/10 23:36
    公司的代码由于业务需求最近在大范围改动,其中使用了大量的Handler机制,但是我又不想使用这个以及activity.runOnUiThread()这种,仔细考虑了下可行性替代方案那就是可以考虑使用RxJava和RxAndroid。作为在开发界比较流行的26设计模式下,观察者模式是我认为比较有趣的模式,加上这几天在自学RxJava和RxAndroid这两种响应式编程,使用到的就是观察者模式这种设计模式,所以,那得先了解和掌握观察者模式。文章考虑到每个开发人员的水平参差不齐,所以尽量写的详细,篇幅就可能会比较大,请大家见谅。    网上有很多篇幅关于观察者模式,常见的例子就是一个警察(观察者)和小偷(被观察者)两者之间的关系。不重复造轮子的我们还是依旧用这个来引用一下,当小偷(被观察者)想要搞事情(开车)的时候,我们的警察蜀黍(观察者)就会及时知道这种变化进行一系列的后续动作(有没有一种线程切换和消息传递的即视感)。但是网上关于这两类(观察者和被观察者)之间为什么会触发事件的”媒介”解释的不是很清楚,比如,当小偷搞事情,这个搞事情的动作,该如何去理解以及如何在代码里面去写,这个才是我们需要理清的关键。    在描述观察者模式之前,先提提Java中的接口,对于接口的定义,我们可以理解为你要使用这个接口,那就得遵循在接口中提供的这一种规则,但是这个规则怎么用,该怎么写,就是得值得考虑的事情,接下来,我们也尝试用几个接口去描述下观察者模式。    首先,我们定义一个主角,观察者(接口),
/** *  * @author Administrator *  Watcher  观察者    *             定位:当被观测者发生变化,产生媒介的时候,及时调用我 *  1)时刻监听被观察者 *  2)为了更好的显示结果,用String来输出 */public interface Watcher {//  观测者通过使用update这个函数来定义:接收被观测者变化的通知    public void updata(String str);}
    当然,这个函数名你可以随便取,关键是函数里面的参数,也就是上文所说的“媒介”,这里暂时用一个字符串去表示,这也就代表观察者被唤醒的前提首先得是一个字符串类型。    接着,轮到我们的下一位主角登场,被观察者(接口),我们需要的就是在被观察者添加些什么东西,当被观察者身上的附带的这些东西发生变化的时候,去唤醒我们的观察者。这个时候我们得想办法,那如何将两个接口关联起来?最常规和最容易理解的套路就是,将观察者接口,以参数的形式传递进我们的被观察者接口。然后,有人就问了,我们的观察者被唤醒的媒介在上面用的是字符串,那么这里的被观察者肯定要有一个字符串来与之对应,没错,你是对的。还有一点就是,如何将这种所谓的变化,也就是被观察者身上媒介的改变用代码去表现,我们可以这样去理解,状态的改变无非就是增删改查这些等等,我们在这里为了更简单的去理解,用增加、删除这两种变化来表示这种状态。将上面三点思路理清之后,于是乎,我们可以有以下的代码,
/** * @author Administrator * Watched 被观察者 *                定位:观察者模式中的主体之一,自身触发的一系列变化,引起后续系列的事件。 *  * 1)当被观察者发生变化的时候,才会即时通知观察者(Watcher) * 2)如何将观察者、被观察者两者“绑定起来”?最常规,最容易理解的方法就是将观察者以参数的形式,传递到被观察者内部函数即可, * 3)当被观察者发生变化的时候,才会触发观察者?这个变化怎么理解? *       变化,我们想简单点可以理解为增删改,对原有的“数据”发生变化,在这里用增加,删除两种变化来表示。 * 4)当被观察者发生变化,观察者和被观察者两者需要结合才会产生我们想要的提示变化的结果,结合? *       结合,我们可以理解为使用到的参数类型需要一致,否则,两者的关系无法映射,也就没有了意义 * 5)理清思路之后,我们可以有以下简化的代码: */public interface Watched {//  描述增加的变化;将观察者、被观察者两者“绑定起来”    public void addWatcher(Watcher watcher);//  描述删除的变化;将观察者、被观察者两者“绑定起来”    public void removeWatcher(Watcher watcher);//  通知观察者,参数类型需要一致,否则,没有意义    public void notifyWatcher(String str);}        然后,我们继续思考,所有的接口,单独写是没有任何意义的。OK,既然被观察者的变化是唤醒观察者的媒介,那么我们就先写一个被观察者的实现类,

public class ImplementsWatched implements Watched{
// 我们用集合来定义,记录有多个观察者同时观察(监听)一个被观察者,
// 只有当被观察者发生变化,多个观察者同时都能收到(变化)消息

    List<Watcher> list = new ArrayList<Watcher>();

// 用集合的(增加)变化去描述被观察者的变化
@Override
public void addWatcher(Watcher watcher) {
// TODO Auto-generated method stub
list.add(watcher);
}
// 用集合的(删除)变化去描述被观察者的变化
@Override
public void removeWatcher(Watcher watcher) {
// TODO Auto-generated method stub
list.remove(watcher);
}

@Overridepublic void notifyWatcher(String str) {    // TODO Auto-generated method stub    for (Watcher watcher : list) {

// 遍历集合
// 当这个集合的元素(对象)发生变化的时候,我们就主动通知观察者发生了变化,及时通知
watcher.updata(str);
}
}

}

    为了更好的表示这种变化,我们用集合来定义。集合自身增删的特点有助于我们描述这种变化,通过设定集合泛型值为一个观察者对象的时候,老司机们就会知道我接下来要干什么了。通过集合自身的增加和移除去描述被观察者的变化,当我增加一个对象,或者移除一个对象,在这之前先去遍历这个集合,我们就可以知道这个对象是否发生了变化,只要发生了变化,我就调用观察者对外提供的方法,去描述被观察者发生了变化。    写到这里,别忘了我们的观察者也等着我们去实现。
//实现观察者接口//为了方便我们看到结果,我们直接打印被观察者传递过来的结果(变化)public class ImplementsWatcher implements Watcher{    @Override    public void updata(String str) {        // TODO Auto-generated method stub        System.out.println(str);    }}
    接下来,我们就写一个主方法,来测试观察者和被观察者之间如何产生关系的吧。

“`
*
* @author Administrator
* 测试类:观察者模式
*/
public class Test {
public static void main(String[] args) {
// 创建3个观察者对象来同时监听一个被观察者
Watcher watcher = new ImplementsWatcher();
Watcher watcher1 = new ImplementsWatcher();
Watcher watcher2 = new ImplementsWatcher();

// 创建一个被观察者对象
Watched watched = new ImplementsWatched();

// 被观察者状态发生变化,如何理解?
// 在被观察者的接口里面,我们写了两个接口方法来定义这种变化,增加和删除的方法,这里用增加的方法,来表示这种变化
watched.addWatcher(watcher);
watched.addWatcher(watcher1);
watched.addWatcher(watcher2);
// removeWatcher 这个函数 大家有兴趣也可以玩下,也会唤醒观察者
// watched.removeWatcher(watcher);

// 当被观察者发生增加或者删除变化的时候,我们就及时通知观察者,
// 那么问题来了,如何通知观察者?这个结果该如何传递?这个结果类型有没有限制?
// 我们需要让被观察者去告知观察者,我发生了变化,以及提示给观察者的信息,也就是调用被观察者对外暴露的方法,以及输入相应的参数类型即可
watched.notifyWatcher(“小偷要开车了—>55555”);

}

}

    在这个测试类里面我们首先创建三个观察者对象来同时监听一个被观察者,然后创建一个被观察者对象来实现被监听,代码如上。接下来就是比较难理解的地方了,那么,我观察者如何知道被观察者发生了变化,在图二中,我们讲到,为了将观察者和被观察者两者结合起来,用到了将观察者以参数的形式传递到被观察者暴露的接口方法里面去的,当发生变化的时候,就调用被观察者中的notifyWatcher()这个函数,然后,我们把程序运行一下。结果如下

这里写图片描述

最后,我们从主方法入手,将这个程序理一遍。
1)创建3个观察者对象
2)创建1个被观察者对象
3)在创建观察者对象的时候,我们是为了更好的显示,就直接讲结果打印在输出台上,输出内容是通过函数传递过来的str
4)在创建被观察者对象的时候,我们在里面创建了一个(观察者)集合,在具体接口暴露的方法中,通过添加观察者对象、移除观察者对象、遍历集合,调用观察者接口暴露的方法去表示这种变化
5)在被观察者,我们调用addWatcher()这个函数,描述这种变化
6)被观察者通知观察者发生了变化(str)——-》观察者调用update(str)函数,将被观察者想要表达的信息返回给观察者,然后在调用——-》ImplementsWatcher这个类里面的update()函数,将传过来的str以System.out的形式输出在控制台上面。整个流程就结束了。

文章的最后,我们在梳理一下思路。当被观察者发生变化的时候及时通知观察者(数据类型)我们就可以通过观察者去获得这个变化(数据类型)。代码逻辑基本上写在注释里面了,然后这是《死磕观察者模式》系列的第一篇,第二篇我会采用JAVA语言自带的观察者模式在写一篇。也希望大家多多支持。

1 0
原创粉丝点击