观察者模式
定义
观察者模式(又称发布-订阅模式),在这种模式中,一个目标物件管理所有依赖于它的观察者物件,并且在它本身状态改变时主动发出通知,就拿微信和订阅公众号来说,我们订阅了一个公众号,就意味着我们成为了这个公众号的观察者(当然,我们可以订阅许许多多的公众号,也就是说我们可以是很多个目标物件的观察者),这样在每次这个公众号有新的推送的时候(状态改变),这个公众号就会给它的每一个观察者发送新的推送内容。
观察者的一般结构
既然是一般结构,就说明了这种结构可以有其他的变型,不过思想还是这个思想就是了。
- 抽象主题(Subject):一般是一个接口,规定了具体主题需要实现的方法,如添加和删除观察者,以及通知观察者更新数据。
- 具体主题(ConcreteSubject):是抽象主题的一个实现类,一般有一个集合用来存放观察者,以便状态变换时通知每一个具体观察者。
- 观察者(Observer):一个接口,规定了具体观察者更新数据的方法。
- 具体观察者(ConcreteObserver):是观察者的一个实现类,可以持有一个用来引用存放具体主题的抽象主题的变量,这样就可以通过这个变量让自己加入到具体主题的观察者集合中,或者从集合中删除。
类图
例子
为了让自己的例子看起来好看一点加了一些乱七八糟的东西。但是,中心思想还是不变的。
import java.util.*;
interface MySubject {
String getName();
void add(MyObserver o);
void remove(MyObserver o);
void notify(String mes);
}
interface MyObserver {
String getName();
void addSubject(MySubject s);
void removeSubject(MySubject s);
void update(MySubject s, String mes);
}
class ConcreteSubject implements MySubject {
private String name;
private Set<MyObserver> observers;
public ConcreteSubject(String name) {
this.name = name;
observers = new HashSet<>();
}
public void add(MyObserver o) {
System.out.println(name + ": 欢迎" + o.getName() + "关注了本公众号!!!");
observers.add(o);
}
public void remove(MyObserver o) {
System.out.println(name + ": 很遗憾,"+ o.getName() + "不再关注本公众号了...");
observers.remove(o);
}
public void notify(String mes) {
for(MyObserver o : observers) {
o.update(this, mes);
}
}
public String getName() {
return this.name;
}
}
class ConcreteObserver implements MyObserver {
private String name;
private Set<MySubject> subjects; //存放所有自己正在观察的主题
public ConcreteObserver(String name) {
subjects = new HashSet<>();
this.name = name;
}
public void addSubject(MySubject s) {
subjects.add(s); //将新的主题加到自己的主题集合中
s.add(this); //将自己加入到该主题的观察者集合中
}
public void removeSubject(MySubject s) {
s.remove(this); //将自己从该主题的观察者集合中移除
subjects.remove(s); //将该主题从自己的主题集合中移除
}
public void update(MySubject s, String mes) {
System.out.println(s.getName() + "的订阅者 "+ this.name + " 你好,这是新的推送内容:" + mes);
}
public String getName() {
return name;
}
}
public class ObserverTest {
public static void main(String[] args) {
MySubject s1 = new ConcreteSubject("公众号1");
MySubject s2 = new ConcreteSubject("公众号2");
MyObserver o1 = new ConcreteObserver("大娃");
MyObserver o2 = new ConcreteObserver("二娃");
o1.addSubject(s1);
o1.addSubject(s2);
o2.addSubject(s1);
s1.notify("今天会下大暴雨...");
s2.notify("今天是周日,不用上课...");
o1.removeSubject(s1);
s1.notify("很遗憾," + o1.getName() + "没有办法接收到这条新的推送了...");
}
}
Java的内置观察者模式
java用Observable来表示抽象主题,但是这里的Observable是个类而不是接口
package java.util;
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) {
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();
}
}
观察者(Observer)
public interface Observer {
void update(Observable o, Object arg);
}
</br/>
虽然内置的观察者模式为我们提供了方便,但是由于抽象主题是个类而非接口,这样又限制我们继承抽象主题后,便不能再继承其他的类,降低了我们设计的灵活性。