【面向对象】设计模式之观察者模式的学习


介绍

观察者模式是一种使用频率非常高的设计模式,最常用的地方就是订阅-发布系统。 这个模式的重要作用就是将观察者和被观察者解耦,使他们之间的依赖更小甚至没有。

观察者模式又被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

使用场景

  • 关联行为场景,这个关联是可拆分的。将观察者和被观察者封装在不同的对象中,可以各自独立的变化。
  • 当一个对象改变时,有其他对象要进行相应的变化,但是他并不知道有多少个对象需要变化。

角色介绍

img

  • 抽象主题(Subject):它把所有观察者对象的引用保存到一个集合,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,一般用抽象类与接口来实现。
  • 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。一般实现了抽象接口。
  • 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己,通常以接口方式实现。
  • 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调,一般实现抽象观察者实现。

一般Subject与Oberver是一对多的,也就是一个Subject可以注册很多个Observer。只需要Observer将自己注册到Subject中即可。

Java API中已经内置了观察者模式类,Observable类与Obeserver接口。分别对应了Subject以及Observer。在使用时需要注意的是,在调用notifyObeservers()方法之前一定要先调用setChanged方法,否则观察者无法接收到通知。

Java API中的观察者模式有一个很明显的缺点。由于Obervable是一个类,而Java又只允许单继承。导致你使用继承者模式又想要获取另一个父类的特性时,只能选择适配器模式或者内部类。这违背了设计模式的原则:多用组合,少用继承。

示例

这里,MyPerson类是一个被观察者,继承自Observable类:

而MyObserver是一个观察者,实现了Obeserver接口及其update方法:

这样,在观察者接收到通知后,便会执行update方法进行更新操作

Android中的观察者模式

我们要更新ListView的数据时,会使用到notifyDataSetChanged()方法,之后ListView的数据便会更新。这就比较类似这里的观察者模式了,ListView观察着Adapter中的内容,内容变化则ListView就会刷新

我们可以查看BaserAdapter的方法,会发现下面的几个方法。显然这里就是使用了观察者模式。当调用了notifyDataSetChanged后,被观察者就会发出通知。

Android用了一种很巧妙的方法解决了前面提到的单继承的问题,通过了对象适配器模式的方式。让BaseAdapter间接成为了被观察者。

其中这些注册和注销观察者等等方法是通过DataSetObservable这个类调用的。这个类继承自Observable。

Observable中有一个protected final ArrayList<T> mObservers = new ArrayList<T>();,  用来保存注册的观察者。mDataSetObservable.registerObserver(observer)mDataSetObservable.unregisterObserver(observer)分别就是增加和删除。

notifyChanged方法中,遍历这个集合,调用每一个观察者的onChanged()方法。

而这些观察者则是在ListView的setAdapter()调用的过程中注册的。

这里AdapterDataSetObserver是ListView的父类AbsListView的内部类 。

AdapterDataSetObserver是AdapterView.AdapterDataSetObserver的子类,因此查看super.onChanged()

当ListView数据变化时,调用Adapter的notifyDataSetChange方法,这个方法调用DataSetObservable的notifyChanged方法,这个方法又会调用所有观察者的onChanged方法,onChanged再调用重新布局View的方法,完成刷新数据的功能。

优缺点分析

  • 优点
    • 解除了观察者和被观察者的耦合,而且依赖的都是抽象,容易应对业务变化,各自的变化不会影响另一个。
    • 增强系统灵活性、可拓展性。
  • 缺点
    • Java中的消息默认是顺序执行,如果其中一个观察者卡顿,会造成整个系统效率变低,可以考虑异步。
    • 可能会引起无用的操作甚至错误的操作。

Android Developer in GDUT