敲了一些代码,观察者设计模式我们也会经常会碰到,比如rxjava就使用到了观察者设计模式。其实java也为我们封装了相关的api,方便我们实现观察者。本文就总结下java原装的观察者设计模式来说明下观察者的流程。
一、什么是观察者
1、Observer模式是行为模式之一。
2、一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新。(是不是想到了接口的回调)
二、观察者模式的角色和职责
- Subject(汉语意思订阅。有时也使用Observable)被观察者 被观察的对象。当需要被观察的状态发生变化时,需要通知容器中所有观察者对象。Subject需要维持(添加,删除,通知)一个观察者对象的队列列表。
- ConcreteSubject 被观察者的具体实现类。包含一些基本的属性状态及其他操作。
- Observer(观察者) 接口或抽象类。当Subject的状态发生变化时,Observer对象将通过一个callback函数得到通知。
- ConcreteObserver 观察者的具体实现。得到通知后将完成一些具体的业务逻辑处理。
三、uml图
1、uml类图
2、通信原理图:
通过uml和原理图我们可以猜测:这种模式的通信原理就是接口的回调机制。
接口回调机制参考这里
四、 栗子
1、java原装api 观察者: Observer
2、java原装api 被观察者:Observable
如上:为了方便实现观察者模式,java为我们提供了相关api,方便我们快速实现观察者
3、使用原装api实现观察者Demo
观察者:
package pattern_observer;
import java.util.Observable;
import java.util.Observer;
/**
* Create by SunnyDay on 2019/04/10
*/
public class PersonObsever implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("观察到了更新数据");
}
}
如上 创建类,实现观察者接口即可。
被观察者:
package pattern_observer;
import java.util.Observable;
/**
* Create by SunnyDay on 2019/04/10
*
* 被观察者(Observable/Subject)
* 自定义被观察者类 继承Observable
*/
public class PersonObservable extends Observable {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.setChanged();//让对象改变(这个必须设置:手动通知对象改变了)
this.notifyObservers();// 通知观察者
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.setChanged();
this.notifyObservers();
this.name = name;
}
}
如上Observable java原生设计成了类我们继承他即可。
测试代码:
package pattern_observer;
/**
* Create by SunnyDay on 2019/04/10
*/
public class MainTest {
public static void main(String[] args){
// 1、声明被观察者
PersonObservable observable = new PersonObservable();
// 2、注册观察者
observable .addObserver(new PersonObsever());
//3、被观察者 状态改变
observable.setAge(18);// 代码执行时 观察者会收到消息
observable.setName("kate");// 代码执行时 观察者会收到消息
}
}
// log:
// 观察到了更新数据
// 观察到了更新数据
4、java原生观察者设计模式源码分析:
观察者源码:
package java.util;
public interface Observer {
void update(Observable o, Object arg);
}
可以看到就是一个接口,等被观察者数据更新这里就会收到消息。
被观察者源码:
package java.util;
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
// 构造,添加0个观察者
public Observable() {
obs = new Vector<>();
}
// 注册观察者
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
//删除观察者
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
// 通知更新
public void notifyObservers() {
notifyObservers(null);
}
// 通知更新
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
//清空所有的注册(吧所有注册的观察者都清空)
public synchronized void deleteObservers() {
obs.removeAllElements();
}
//标记此 Observable 对象为已改变的对象;现在 hasChanged 方法将返回 true
protected synchronized void setChanged() {
changed = true;
}
//指示对象不再改变,或者它已对其所有的观察者通知了最近的改变,所以 hasChanged 方法将返回 false
protected synchronized void clearChanged() {
changed = false;
}
// 测试对象是否改变。 对象改变 返回true反之false
public synchronized boolean hasChanged() {
return changed;
}
//返回 Observable 对象的观察者数目。也就是订阅了几个观察者
public synchronized int countObservers() {
return obs.size();
}
}
可见被观察者的设计:主要是一个容器用来保存注册的观察者,注册时添加进集合,清除时,清空集合。还有就是接口的回调(update的调用)
我们从main的测试代码分析下运行流程:
我们注册观察者时走:
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
我们声明被观察者接着注册观察者,这时进入源码我们发现他把观察者加入集合容器,加入前进行了判空处理、判重处理。
main中代码继续走,到 observable.setAge(18);被观察者状态改变时
public void setAge(int age) {
this.setChanged();//让对象改变(这个必须设置:手动通知对象改变了)
this.notifyObservers();// 通知观察者
this.age = age;
}
首先是通知对象改变:setChanged
再是通知观察者数据更新(进入代码继续观看如下)
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
1、先判断对象是否改变,没改变直接结束。
2、吧object对象数组转变为数组
3、清空改变
4、遍历观察者,开始更新。
5、优缺点
1.主要优点
(1)观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息传递机制,并抽象了更新接口,使得可以有各种各样的表示层充当具体的观察者角色。
(2)观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察者对象只需要维持一个抽象观察者的集合,无需了解其具体观察者。
(3)观察者模式支持广播通信,观察目标会向所有已注册的观察者发送通知,降低了一对多系统的设计难度。
(4)观察者模式满足开闭原则的要求,增加新的具体观察者无须修改原有的系统代码。
2.主要缺点
(1)如果一个观察目标对象有很多的直接观察者和间接观察者,那么所有的观察者接收到消息会耗费大量的时间。
(2)如果观察者和被观察者之间存在循环依赖,那么观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
(3)观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道目标观察对象发生了变化。
参考:https://www.jianshu.com/p/186a0041ac5b
四、小结
通过案例吧观察者总结了一遍,只要理解了接口回调,很容易看懂理解观察者。看了java原生的观察者其实我们也可以手动实现思路很简单。这里就把观察者简单的过一下,加深下印象。