观察者模式主要是建立对象之间的一对多关系,当其中一方发生变化时,通知多个对应的对象,而具体什么时候去通知,这个就由具体的子类来实现。
下图当中给出的是观察者模式之间的结构关系图
Subject
被观察的对象的抽象接口,其中包括需要通知观察者的集合,同时也提供了,添加观察者和移除观察者方法,以及通知所有的观察者的方法。
ConcreteSubject
具体的被观察者对象,当具体的被观察者发生某些变化的时候,调用通知所有观察者,同时也对其实现的接口的方法进行了实现
Observer
观察者对象接口,观察者当中提供了一个更新接口,即通知消息接口,当被观察者需要通知所有的观察者对象时,会调用该接口,并将具体的被观察者对象传递进
ConcreteObserver
具体的观察者对象,其中实现了更新状态的接口,将被观察者的状态更新至观察者当中。
/**
* 目标对象,作为被观察者
*/
public class Subject {
/**
* 用来保存注册的观察者对象,这里并未考虑线程安全的问题
*/
private List<Observer> readers = new ArrayList<Observer>();
public void attach(Observer reader) {
readers.add(reader);
}
public void detach(Observer reader) {
readers.remove(reader);
}
protected void notifyObservers() {
for(Observer reader : readers){
reader.update(this);
}
}
}
public class ConcreteSubject extends Subject{
/**
* 变更内容
*/
private String content;
/**
*
* 返回变更的内容
*/
public String getContent() {
return content;
}
/**
* 设置变更内容,同时当变更内容发生变化,就通知所有观察者
* @param content 报纸的具体内容
*/
public void setContent(String content) {
this.content = content;
notifyObservers();
}
}
对于观察者接口
/**
* 观察者
*/
public interface Observer {
/**
* 被通知的方法
* @param subject 具体的目标对象,可以获取被观察者当中的变更内容然后进行通知
*/
public void update(Subject subject);
}
具体的观察者
public class ConcreteObserver implements Observer{
/**
* 观察者的名字
*/
private String name;
public void update(Subject subject) {
//这是采用拉的方式
System.out.println(name+"收到消息啦,内容是==="+((ConcreteSubject)subject).getContent());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试类
public class Client {
public static void main(String[] args) {
//创建被观察者
ConcreteSubject subject = new ConcreteSubject();
//创建阅读者,也就是观察者
ConcreteObserver observer1 = new ConcreteObserver();
observer1.setName("张三");
ConcreteObserver observer2 = new ConcreteObserver();
observer2.setName("李四");
ConcreteObserver observer3 = new ConcreteObserver();
observer3.setName("王五");
//注册阅读者
subject.attach(observer1);
subject.attach(observer2);
subject.attach(observer3);
//要出报纸啦
subject.setContent("本期内容是观察者模式");
}
}
打印结果
张三收到消息了,内容是===本期内容是观察者模式
李四收到消息了,内容是===本期内容是观察者模式
王五收到消息了,内容是===本期内容是观察者模式
在JDK当中为我们提供好了被观察者对象和观察者对象,当我们需要用到观察者模式时,只需要分别实现被观察者对象和观察者对象即可
被观察者对象Observable
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
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) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
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();
}
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
java当中提供的被观察者对象考虑到了并发安全性问题使用了Vector,同时增加了一个状态位,用来表示,当前状态是否发生变更,如果发生没有发生变更,就不会通知观察者,如果发生变更了通知所有的观察者,同时将已经变更的状态进行重置,方便下次继续使用。
接下来看观察者对象
public interface Observer {
void update(Observable o, Object arg);
}
对于观察者对象来说,只有一个接口,这个方法传递两个参数,一个是被观察者对象,还有一个是自己定义的需要通知的消息,可以看到jdk设计的还是比较合理的,因为可能有时候我们的通知消息是被观察者当中获取,也有可能我们的通知消息是自己定义的。