Java设计模式之观察者模式及在Android中的应用

KaelLi 2019年2月20日11:25:49
评论
4,879

观察者模式简介

Observer Pattern,观察者模式,也是设计模式中非常常见的一种,在此模式下,一个对象(被观察者)可以管理一大批观察者对象,并且在自己的状态发生变化时,通过接口通知观察者们,而观察者们接到通知后,可以分别作出相应的动作。

观察者与被观察者之间不会产生直接耦合,但它们是抽象耦合的(通过接口),这算是一个优点,但毕竟还是存在耦合。

观察者模式解读

Java设计模式之观察者模式及在Android中的应用

从网上找来一张观察者模式的简单类图,从这张图里,我们对观察者模式中的各个角色进行解读。

  • Subject(被观察者)

是一个接口(实际上也有人使用抽象类),主要包含了3个方法:

  1. addObserver方法可以添加观察者对象,可以理解为观察者把自己注册到了被观察者这里,只有注册了的观察者,才能接到被观察者的通知。
  2. deleteObserver方法是将观察者移除,被移除的观察者自然就不能再接到通知了。
  3. notifyObserves方法可以把通知发送给所有的已注册的观察者,至于观察者们后续做什么事情,被观察者是完全不关心的。
  • Observer(观察者)

也是一个接口,必须实现的只有一个notify方法(当然在Java里你得把notify改成其他名字),被观察者通过调用这个方法,让观察者了解到事件的发生。

  • ConcreteSubject(被观察者的具体实现)

在这里可以写被观察者的具体业务逻辑,另外还需要一个存储观察者的集合。一般情况下使用ArrayList就行,当然如果考虑到多线程场景,可以使用CopyOnWriteArraySet,因为线程安全嘛。

  • ConcreteObserver(观察者的具体实现)

在这里写的是观察者的具体业务逻辑。毕竟一个观察者如果除了接收消息没有别的能力,那么这个观察者就没有意义了。观察者模式嘛,观察者从被观察者那里接收到了消息之后的逻辑才更加重要。

实现观察者模式的Java代码

先定义观察者接口:

// 这里的接口很简单,就只有一个update方法,被观察者通过此方法通知观察者。
public interface Observer {
    void update(String message);
}

然后是观察者接口:

public interface Subject {
    // 注册观察者
    void addObserver(Observer observer);
    // 删除观察者
    void deleteObserver(Observer observer);
    // 通知观察者
    void notifyObservers();
}

接下来就是具体的实现类了,先来一个MessageCenter,是被观察者,用来给观察者发送消息的:

public class MessageCenter implements Subject {
    private final CopyOnWriteArraySet<Observer> observers = new CopyOnWriteArraySet<>();

    private String mMessage;

    @Override
    public void addObserver(Observer observer) {
        if (!observers.contains(observer)) {
            observers.add(observer);
        }
    }

    @Override
    public void deleteObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(mMessage);
        }
    }

    public void sendMessage(String message) {
        mMessage = message;
        notifyObservers();
    }
}

然后再来一个观察者的具体实现:

public class DotaPlayer implements Observer {
    public String playerName;

    @Override
    public void update(String message) {
        System.out.println("DotaPlayer-" + playerName + "接收到信息:" + message);
    }
}

最后就是实际的调用了:

DotaPlayer playerA = new DotaPlayer();
playerA.playerName = "甲";
DotaPlayer playerB = new DotaPlayer();
playerB.playerName = "乙";
MessageCenter messageCenter = new MessageCenter();
messageCenter.addObserver(playerA);
messageCenter.addObserver(playerB);
messageCenter.sendMessage("圣剑代表了孤注一掷、背水一战的勇气……");
messageCenter.sendMessage("兄弟们,这波我很强,因为我有BKB!");

看一下运行结果:

System.out: DotaPlayer-甲接收到信息:圣剑代表了孤注一掷、背水一战的勇气……

System.out: DotaPlayer-乙接收到信息:圣剑代表了孤注一掷、背水一战的勇气……

System.out: DotaPlayer-甲接收到信息:兄弟们,这波我很强,因为我有BKB!

System.out: DotaPlayer-乙接收到信息:兄弟们,这波我很强,因为我有BKB!

在Android中的应用

观察者模式在Android里的运用十分广泛,Framework层中的观察者模式随处可见,而在很多第三方库中也有使用。

  • BroadcastReceiver

BroadcastReceiver就是一个十分典型的观察者模式,注册广播就是BroadcastReceiver这个观察者把自己添加到了被观察者那里(广播管理中心),然后被观察者发送某个广播时,只有注册了该广播的BroadcastReceiver才能接收到,最终在onReceive回调里收到广播再进行其他处理。

  • 各种各样的Listener

最熟悉的当然是View. setOnClickListener这个流程了,setOnClickListener的参数OnClickListener本身就是一个接口,注册后,当发生点击事件时,View中的performClick()方法被调用,在该方法中通过mOnClickListener.onClick实现了回调。

  • 各类Adapter

ListView、RecyclerView、ViewPager等的Adapter中,都有一个mObservable,它们归根结底都是android.database. Observable类的实现。当数据发生变化时,我们调用notifyDataSetChanged方法,最终就要依赖mObservable.notifyChanged()方法来实现数据刷新。

  • RxJava

这个库的强大相信很多人都有所了解。它可以很方便的实现链式调用,在主线程和其他子线程之间的切换十分便捷,在RxJava里观察者模式可以说是它的根本,才能实现如此方便的线程管理与数据流管理。代码分析就不上了,太多了,日后单独分析吧。

总结

观察者模式的优点还是比较明显的,耦合度较低,且被观察者只负责发出通知,通知都有谁接收、接收后做什么事情,一概不问。但也有可能会造成一个严重的问题,因为在notifyObservers方法里会对所有的观察者进行通知,如果观察者数量较多,甚至在update方法里执行了耗时操作,那么就有可能造成很严重的性能问题。

也有人把观察者模式与发布订阅模式(Publish-Subscribe pattern)划等号,认为这两者是一种设计模式。但实际上二者有一定的区别,从某种意义上说,发布订阅模式是解耦更彻底的一种特殊的观察者模式,但并不能直接判断二者孰好孰坏,还是要根据实际需求来选择。

KaelLi
  • 本文由 发表于 2019年2月20日11:25:49
  • 转载请务必保留本文链接:https://www.kaelli.com/32.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: