观察者(发布 - 订阅者)模式在 Android 中的简单应用

2,294

学过设计模式的少年or少女一定听说过观察者模式了,也就是多个观察者观察一个事物,当事物发生变化的时候,观察者也将进行动作改变。但是,为了让不懂的人儿or复习一下,我啰嗦一下什么是观察者模式:

什么是观察者模式

百度是这样解释:
这里写图片描述
- -说明文字还是如此的深奥
我简单举个例子吧,就像在学校上课的时候,到了中午十二点,闹铃铃铃的响了,这时,有些同学仍然坐在那里屁股一动不动的记录笔记,而有些同学则早已收拾好书包跑去饭堂了。
在这里,闹铃则是被观察的对象,而同学们则是观察者,当被观察的对象发生改变的时候,观察者则进行了相对应的行为动作。
经过上面的例子,估计大家都初步了解了观察者模式的大概流程,但是,其在代码中的实现又该是如何?

观察者模式在Java中的简单实现

首先,我们要确认在观察者模式中,我们需要对两个对象进行研究——观察者被观察者
我们发现更多的是观察者围着被观察者在转,就有点像多对一的的情况(当然一个观察者可以观察多个被观察的对象,因此是多对多,但是我们现在只考虑多对一),因此,在被观察的对象中需要使用一个东西来存储多个观察者(例如ArrayList),而且,为了便于被观察者提醒观察者做出改变,观察者应该拥有一个统一的方法名便于被观察者调用(实现同一个接口)
大致思路便是如此,下面我们一起来实现观察者对象被观察者对象

观察者接口

功能:
- 当被观察者发生改变时,观察者进行的行为动作
- 提供一个统一的方法名便于被调用
代码:

/**
 * 观察者的接口
 * Created by Jinxiong.Xie on 2016/11/17.
 */

public interface Observer {

    /**
     * 当被观察者发生改变时,观察者进行的行为动作
     * @param objects
     */
    void action(Object... objects);

}

被观察者接口

功能:
- 注册观察者
- 注销观察者
- 提醒观察者进行动作更新
代码:

/**
 * Created by Jinxiong.Xie on 2016/11/17 15:05.
 * 邮箱:173500512@qq.com
 */

public abstract class Observable<T> {

    public final ArrayList<T> observerList = new ArrayList<>();

    /**
     * 注册观察者对象
     *
     * @param t
     */
    public void registerObserver(T t) {
        checkNull(t);
        observerList.add(t);
    }

    /**
     * 注销观察者对象
     *
     * @param t
     */
    public void unRegisterObserver(T t) {
        checkNull(t);
        observerList.remove(t);
    }

    /**
     * 判断所传的观察者对象是否为空
     *
     * @param t
     */
    private void checkNull(T t) {
        if (t == null) {
            throw new NullPointerException();
        }
    }

    /**
     * 通知观察者做出行为改变
     *
     * @param objects
     */
    public abstract void notifyObservers(Object... objects);
}

OK,接口书写完毕,是时候展现真正的技术了,呃,手快了,是时候来写写观察者对象被观察者对象
在最开始的时候,我们举的是闹钟与学生的例子,下面的两个对象,我们也是围绕这两个写。

观察者对象

呃,去搜了一下学霸与学渣的英文不是很明确,就暂时用XB和XZ来代码(- -貌似透露了我的英文水平)
XBObserver.Java

/**
 * 学霸观察者
 * Created by Jinxiong.Xie on 2016/11/17 15:43.
 * 邮箱:173500512@qq.com
 */
public class XBObserver implements Observer {
    private static final String TAG = "XBObserver";

    @Override
    public void action(Object... objects) {
        if (objects.length > 0 && objects[0] != null) {
            Log.e(TAG, objects[0].toString() + " 下课了,老师要走了,快记下老师讲的东西");
        }
    }
}

XZObserver.java

/**
 * 学渣观察者
 * Created by Jinxiong.Xie on 2016/11/17 15:49.
 * 邮箱:173500512@qq.com
 */

public class XZObserver implements Observer {
    private static final String TAG = "XZObserver";

    @Override
    public void action(Object... objects) {
        if (objects.length > 0 && objects[0] != null) {
            Log.e(TAG, objects[0].toString() + " 终于下课了,好饿,快跑,不然没位置了");
        }
    }
}

被观察者对象

AlarmObservable.java

/**
 * 闹铃(被观察者)
 * Created by Jinxiong.Xie on 2016/11/17 15:58.
 * 邮箱:173500512@qq.com
 */

public class AlarmObservable extends Observable<Observer> {

    @Override
    public void notifyObservers(Object... objects) {
        for (Observer observer : observerList) {
            observer.action(objects);
        }
    }
}

main()

        AlarmObservable alarmObservable = new AlarmObservable();
        alarmObservable.registerObserver(new XBObserver());
        alarmObservable.registerObserver(new XZObserver());
        alarmObservable.notifyObservers("闹铃响了~铃~玲~~玲~~~");

运行结果:
这里写图片描述


观察者(发布-订阅者)模式在Android中的简单应用

由上面,我们可以大致理解并简易实现观察者模式在代码上的时候,但是,我们仅仅只是代码实现而已,则还远远不够,我们需要了解它的优点在哪里,并加以使用,使其发挥出最大化。
观察者模式,众多观察者观察其中一个事物,当事情发生改动,则全部观察者发生相应变动,有些像牵一发而动全身,就像QQ里面的头像功能,假如你改了头像,那么全部页面的头像都将进行改变。
好了,我们知道需求了,现在我们就将它搬到Android中去。



相同的,在Java中实现时,观察者是我们实现Observer接口的一些自定义类,而在Android中,这个自定义类便可以换成Activity,而闹铃AlarmObservable被观察者则可以变换成存储Activity的Observable。

观察者对象

MainActivity.java

public class MainActivity extends AppCompatActivity implements Observer {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initReceiver();

    }

    private void initReceiver() {
        ActivityObservable.getInstance().registerObserver(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityObservable.getInstance().unRegisterObserver(this);
    }

    @Override
    public void action(Object... objects) {

        Log.e(this.getClass().getName(), "action: " + objects[0].toString() + "MainActivity已更新");
    }
}

被观察者对象

ActivityObservable.java

/**
 * Created by xiejinxiong on 2016/11/20.
 */
public class ActivityObservable extends Observable<Observer> {

    private static ActivityObservable mActivityObservable = null;

    public static ActivityObservable getInstance() {
        if (mActivityObservable == null) {
            mActivityObservable = new ActivityObservable();
        }

        return mActivityObservable;
    }

    @Override
    public void notifyObservers(Object... objects) {
        for (Observer observer : observerList) {
            if (observer != null) {
                observer.action(objects);
            }
        }
    }
}

提醒观察者更新

Main2Activity.java

public class Main2Activity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

                ActivityObservable.getInstance().notifyObservers("MainActivity,你该更新了");

    }

}

呃,好像就是这样的了,不过,这样提醒看起来有些不符合规范,我们可以在ActivityObservable里面添加一些静态的成员变量,例如

    public static final int UPDATE_MAINACTIVITY = 1;

然后,观察者的接口变成:

    void action(int id,Object... objects);

被观察者的接口变成:

 public abstract void notifyObservers(int id,Object... objects);

都多加id这个参数

推送的时候:

 ActivityObservable.getInstance().notifyObservers(ActivityObservable.UPDATE_MAINACTIVITY);

接收的时候:

    @Override
    public void action(int id,Object... objects) {

        if (id == ActivityObservable.UPDATE_MAINACTIVITY){
            Log.e(this.getClass().getName(), "action: " + objects[0].toString() + "MainActivity已更新");
        }
    }

更多的功能和规范等着你们自己的发现和添加了。
不过,这些都是我们自己写的,难免会有很多遗漏的地方,我们可以大致发挥我们的想象封装,但是,到真正项目的使用的时候,建议使用别人开发的轮子。例如:EventBus和Otto。
再多说一下,其实,观察者模式和广播机制很像,但是,观察者模式消耗比广播低,因此,假如仅仅是App内部的提醒更新可以使用观察者模式,但是App之间的提醒更新仍然使用广播。