观察者模式是我们经常使用的模式之一,那什么是观察者模式呢?观察者模式(有时又被称为发布-订阅Subscribe>模式是23种模式中的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
我还是用比较通俗的方法来理解!比如:某人去邮局订了一本杂志,那么如果杂志有更新了,邮局就会通知他,因此这里面就会有两个重要的对象,一个就是某人(我们可以称为观察者),那另一个对象就是邮局(我们可以称为被观察对象或者叫主题),那么它的作用是什么?一般而言有两个作用:1.管理观察者,我们去订杂志的时候总会告诉邮局,邮局也一定会有记录的。2.通知观察者,当有新杂志到的时候,主题必须通知观察者有新的杂志到了。
我们可以进一步把上面的概念抽象成对象:
邮局:抽象成主题(Subject),它是管理者一般会有注册方法(regists),当然也有注销方法(unregists),还有一个通知观察者的方法(notify)。
某人:抽象成观察者(Observer),它是被通知的人,那肯定有一个更新的方法(update)。
我们一般都是面向接口编程,所以再一次把上面抽象成接口:
主题(Subject):它把所有观察者对象的引用保存到一个集合或者数组里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
下面我们来简单实现一下:
一. 定义观察者接口:
package java.observer;
public interface Observer {
public void update(Subject subject,Object data);
}
二.主题的实现:这里我用arraylist集合来保存所有观察者对象。因为在项目中我们也是经常只有一个主题,所以这里我使用单例.
package java.observer;
import java.util.ArrayList;
import java.util.List;
public class Subject {
private static Subject mInstance;
private List<Observer> observers = new ArrayList<Observer>();
private Subject() {
}
public static Subject getInstance() {
if(mInstance == null) {
mInstance = new Subject();
}
return mInstance;
}
public void regists(Observer observer) {
if(observer != null) {
observers.add(observer);
}
}
public void unregists(Observer observer) {
if(observer != null) {
observers.remove(observer);
}
}
public void notify(Object object) {
for(Observer observer : observers) {
observer.update(this, object);
}
}
}
三.我们再来具体实现一个观察者:
package java.observer;
public class ObserverImpl implements Observer {
@Override
public void update(Subject subject, Object data) {
System.out.println((String)data);
}
}
这里只是简单的打印,实际项目中肯定不会这么简单。
我还是简单的运行起来:
package javaObserver.myObserver;
public class ObserverClient {
public static void main(String[] args) {
Subject obSubject = Subject.getInstance();// 获取主题
// 实例化一个观察者
ObserverImpl observer = new ObserverImpl();
// 注册观察者
obSubject.regists(observer);
// 如果事件到来时,主题会主动更新
obSubject.notify("update all observer ");
}
}
到这里我们简单的观察者模式,基本已经完成。这只是在单线程的环境下面运行的,如果要想在多线程的环境下面运行,我们必须对上面几个部分进行修改:
第一.单例问题
public static Subject getInstance() {
if(mInstance == null) {
mInstance = new Subject();
}
return mInstance;
}
这是不安全,我们必须改成线程安全的。可以改成下面:
private static class SubjectInner {
public static Subject inner = new Subject();
}
public static Subject getInstance() {
return SubjectInner.inner;
}
第二,给regists,unregists 和 notify方法加上synchronized 关健字。这样相当对每个方法都加了锁,而且锁住的对象本身。
似乎这样已经是很好的,因为我们保证了每个方法都是同步,只有一个线程能操作,但这里就有一个效率上面的问题,如果观察者对象很多的时候,一个线程在遍历,而其他要注册或取消的进程就有可能进行长时间的等待。因此,我们一般只对regists,unregists加同步。
但如果没有对notify加同步的时候却产生了另外一个问题:concurrentmodificationexception ,这个异常是由于遍历集合的时候,同时有人在修改集合里面的数据。
要解决这个问题,我们一般有两种方法:
第一种:把上面的arraylist 改成用CopyOnWriteArrayList 集合,CopyOnWriteArrayList的原理很解决,每次写数据的时候都把原来的数据复制一份,所以也就不会有问题,但这样也会有一个问题,如果数据大的也会比较占空间,是一种用空间换时间的做法。
第二种:每次注册的时候,先去检测有没有在遍历,如果正在遍历则添加到临时集合里面,遍历完成之后再添加到原来的集合里面。本人比较偏向第二种方法。