前言
本文将分为以下三个部分介绍观察者模式
- 1.观察者模式介绍
- 2.观察者模式示例
- 3.Java中的观察者模式
一.观察者模式介绍
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
image观察者模式的三要素:观察者,被观察者,事件
优点:
1、解耦,被观察者只知道观察者列表「抽象接口」,被观察者不知道具体的观察者
2、被观察者发送通知,所有注册的观察者都会收到信息「可以实现广播机制」
缺点:
1、如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时
2、观察者知道被观察者发送通知了,但是观察者不知道所观察的对象具体是如何发生变化的
3、如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃
二.观察者模式示例
1.警察抓小偷
- 1)首先是被观察者(罪犯)接口的定义
//罪犯
public interface Criminal {
//罪犯被顶上了
void spotted(Police police);
//罪犯的犯罪行为
void crime(String illegalAction);
String getName();
String getIllegalAction();
}
2)接下来是观察者(警察)接口的定义
//警察
public interface Police {
//警察的行为
void action(Criminal criminal);
}
- 3)然后是被观察者(小偷)的实现类
//小偷
public class Thief implements Criminal {
//盯上罪犯的警察集合
private LinkedList<Police> polices = new LinkedList<>();
//罪犯姓名
private String name;
//犯罪行为
private String illegalAction;
//单例模式
public static Thief getInstance(){
return InnerClass.THIEF;
}
//静态内部类单例模式
private static class InnerClass{
private static final Thief THIEF = new Thief("小偷");
}
private Thief(String name) {
this.name = name;
}
@Override
//被盯上了
public void spotted(Police police) {
if (!polices.contains(police)) {
polices.add(police);
}
}
@Override
//犯罪
public void crime(String illegalAction) {
this.illegalAction = illegalAction;
for (Police police : polices) {
police.action(this);
}
}
@Override
//罪犯姓名
public String getName() {
return name;
}
@Override
//犯罪行为
public String getIllegalAction() {
return illegalAction;
}
}
- 4)最后是观察者(警察)的实现类
武警
//武警
public class ArmedPolice implements Police {
public ArmedPolice(Criminal criminal) {
System.out.println(criminal.getName()+"被人民武警盯上了");
//这里调用罪犯的spotted方法,说明罪犯被顶上了
criminal.spotted(this);
}
@Override
public void action(Criminal criminal) {
System.out.println("武警发现了"+criminal.getName()+criminal.getIllegalAction());
}
}
朝阳群众
//朝阳群众
public class MassesPolice implements Police {
public MassesPolice(Criminal criminal) {
System.out.println(criminal.getName()+"被朝阳大妈盯上了");
//这里调用罪犯的spotted方法,说明罪犯被顶上了
criminal.spotted(this);
}
@Override
public void action(Criminal criminal) {
System.out.println("朝阳大妈发现了"+criminal.getName()+criminal.getIllegalAction());
}
}
公安
//公安
public class SecurityPolice implements Police {
public SecurityPolice(Criminal criminal) {
System.out.println(criminal.getName()+"被公安盯上了");
//这里调用罪犯的spotted方法,说明罪犯被顶上了
criminal.spotted(this);
}
@Override
public void action(Criminal criminal) {
System.out.println("公安发现了"+criminal.getName()+criminal.getIllegalAction());
}
}
- 5)测试
public class TestObserver {
public static void main(String[] args) {
Thief thief = Thief.getInstance();
ArmedPolice armedPolice = new ArmedPolice(thief);
SecurityPolice securityPolice = new SecurityPolice(thief);
MassesPolice massesPolice = new MassesPolice(thief);
System.out.println("——————————————————————————");
thief.crime("盗窃");
}
}
运行效果截图.png
2.小明和妈妈
生活中的一个例子,小明妈妈做好了饭,让小明去地里叫他爸回来吃饭,小明说好的我马上去,过了半个小时小明和他爸一起来了,小明给妈妈的说:“妈,爸回来了”,妈妈说:“好的我知道了,让你爸洗洗手吃饭吧”。
在这一过程中,小明是被观察者,小明他妈是观察者。
小明观察到他爸回来了,这个过程就会触发一个事件,然后通知观察者(他妈)。
- 1)定义一个回调接口
- 2)定义妈妈类
- 3)定义小明类
- 4)测试
- 5)运行查看结果
三.Java中的观察者模式
观察者模式在上图的结构,其中Observer作为观察者,Observable则作为被观察者,Observable的状态改变会给注册的Observer进行通知,考虑易用和低耦合,保证高度的协作。
1.代码展示
MyObservable非常简单,只是继承了Observable:
public class MyObservable extends Observable {
public static void main(String[] args) {
//定义被观察者
MyObservable myObservable = new MyObservable();
//建立被观察者和观察者间的关系
myObservable.addObserver(new MyObserver());
//这里需要注意 让其触发事件时及时通知
myObservable.setChanged();
//触发事件,通知观察者
myObservable.notifyObservers("helll");
}
}
此处为了简化结构,直接将main方法写在MyObservable中用于进行效果的测试,main中我们先实例化了MyObservable类,并且将一个MyObserver实例用addObserver方法加入它的观察者列表中,然后通过setChanged改变changed的状态值,随后便可以notifyObservers,里面可以带参数做消息传递,也可以不带参数。关于changed,是因为只有该变量为true的情况下,才会进行通知,否则notifyObservers是无法通知到具体Observer的。
MyObserver的结构也非常简单,由于继承了Observer接口,因此需要实现它的抽象方法update,这个方法也是用于接收Observable发来的通知并执行相应的处理逻辑的:
public class MyObserver implements Observer{
public void update(Observable o, Object arg) {
System.out.println("观察对象为:" + o + ";传递参数为:" + arg);
}
}
运行一下结果为:
JAVA中对观察者模式的支持.png而在注释了setChanged方法之后,会发现没有任何输出,可以理解为,并没有真正通知到Observer。
2.原理解析
原理非常简单,查看Observable源码即可。其实从类图中就可以看出一些端倪,Observable中只有两个变量,一个是changed,一个是obs,从之前的分析可以看出changed只是一个状态指示的变量,那么obs应该就是一个用于存储Observer实例的变量了,它具体是用一个Vector来进行存储的。
private boolean changed = false;
private Vector<Observer> obs;
这样的情况下,我们很容易就可以猜测,obs的初始化应该是在Observable的构造方法中实现的,果不其然,构造方法也仅仅只是完成了obs的初始化:
public Observable() {
obs = new Vector<>();
}
然后我们更为关心的是Observable与Observer之间的关系,那么就是重点关注两个方法,addObserver和notifyObservers。addObserver很显然,里面的主要操作是将作为参数传入的Observer加入到Vector中:
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
notifyObservers则是逐一通知Observer的过程,不过在这个方法中会检查changed的值,以及会在通知完成后对changed归零(其中clearChanged方法就是将changed变量重新置为false
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);
}
到这里基本上整个基于jdk实现的观察者模式的原理已经非常明了了,另外值得注意的一个点是,这个实现中用了一些措施来使它线程安全。比如,使用线程安全的容器Vector,addObserver方法进行了枷锁,在notifyObservers方法中,有一个操作进行了加锁
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
这里主要确保了对changed变量的验证和清零操作以及将obs中的元素赋值给arrLocal的过程是原子操作,这里减少了锁的粒度,提高并发性,也因此大家能明白为何需要arrLocal这个变量了,随后对arrLocal数组进行便利并逐一通知则不需要加锁了,因为arrLocal已经是一个本地变量,并未暴露。
总结
1.使用场景
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
2.注意事项
- JAVA 中已经有了对观察者模式的支持类。
- 避免循环引用。
- 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
参考:
网友评论