观察者模式的定义:
定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。
观察者模式的类图:
根据类图自定义主题、观察者接口:
package observer;
/**
* 主题接口
* @author 发挥哥
*
*/
public interface Subject {
/**
* 注册观察者
* @param observer 观察者对象
*/
void registerObserver(Observer observer);
/**
* 移除观察者
* @param observer 观察者对象
*/
void removeObserver(Observer observer);
/**
* 通知所有观察者
*/
void notifyObservers();
}
package observer;
/**
* 观察者接口
* @author 发挥哥
*
*/
public interface Observer {
/**
* 主题通知所有观察者,执行该方法
* @param msg
*/
void update(String msg);
}
创建主题类和观察者类:
package observer;
import java.util.ArrayList;
import java.util.List;
import observer.Subject;
/**
* 自定义主题类,实现自定义主题接口
* @author 发挥哥
*
*/
public class TestSubject implements Subject {
/**
* 所有注册到该主题的观察者
*/
private List<Observer> observers=new ArrayList<>();
/**
* 主题的状态
*/
private String msg;
@Override
public void registerObserver(Observer observer) {
// TODO Auto-generated method stub
if(observers!=null&&observer!=null)
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
// TODO Auto-generated method stub
if(observers.contains(observer)) {
observers.remove(observer);
}
}
@Override
public void notifyObservers() {
// TODO Auto-generated method stub
observers.stream().forEach(observer->{
observer.update("主题更新了,大家注意:"+this.msg);
});
}
/**
* 编辑主题的状态后,通知所有观察者
* @param msg
*/
public void editMsg(String msg) {
this.msg = msg;
notifyObservers();
}
}
package observer;
/**
* 观察者1
* @author 发挥哥
*
*/
public class Test1Observer implements Observer {
/**
* 创建观察者对象时,将其注册到指定的主题
* @param subject
*/
public Test1Observer(Subject subject) {
subject.registerObserver(this);
}
/**
* 当主题状态变化时,通过调用该方法通知观察者
*/
@Override
public void update(String msg) {
// TODO Auto-generated method stub
System.out.println(this.getClass().getName()+"收到主题发来的消息:"+msg);
}
}
package observer;
/**
* 观察者2(仅为测试,实现与观察者1一样)
* @author 发挥哥
*
*/
public class Test2Observer implements Observer {
public Test2Observer(Subject subject) {
subject.registerObserver(this);
}
@Override
public void update(String msg) {
// TODO Auto-generated method stub
System.out.println(this.getClass().getName()+"收到主题发来的消息:"+msg);
}
}
创建测试方法:
package observer;
public class TestMain {
public static void main(String[] args) {
//创建主题
TestSubject subject=new TestSubject();
//创建观察者1并注册到主题
Observer observer1=new Test1Observer(subject);
//创建观察者2并注册到主题
Observer observer2=new Test2Observer(subject);
//主题进行2次状态修改
subject.editMsg("第一次调整!");
subject.editMsg("第二次调整!");
}
}
测试结果如下:
observer.Test1Observer收到主题发来的消息:主题更新了,大家注意:第一次调整!
observer.Test2Observer收到主题发来的消息:主题更新了,大家注意:第一次调整!
observer.Test1Observer收到主题发来的消息:主题更新了,大家注意:第二次调整!
observer.Test2Observer收到主题发来的消息:主题更新了,大家注意:第二次调整!
以上是自定义主题和观察者接口及其实现,JDK中已经帮我们实现了观察者模式,借助于java.util.Observable和java.util.Observer:
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();
}
}
package java.util;
public interface Observer {
void update(Observable o, Object arg);
}
类java.util.Observable和接口java.util.Observer比较简单,直接借此创建主题和观察者类:
package observer.jdk;
import java.util.Observable;
/**
* 主题1
* @author 发挥哥
*
*/
public class MySubject extends Observable {
private String msg;
/**
* 编辑主题消息,并将变动标志置true,通知所有观察者,再将标志置false
* @param msg
*/
public void editMsg(String msg) {
if(msg==null) {
this.msg = msg;
setChanged();
notifyObservers("主题将消息置空!");
clearChanged();
return;
}
if(this.msg!=null&&msg!=null&&this.msg.equals(msg)) {
return;
}
this.msg = msg;
setChanged();
notifyObservers("主题更新消息:"+this.msg);
clearChanged();
}
}
package observer.jdk;
import java.util.Observable;
/**
* 主题2(仅为测试,与观察者1实现一样)
* @author 发挥哥
*
*/
public class OtherSubject extends Observable {
private String msg;
public void editMsg(String msg) {
if(msg==null) {
this.msg = msg;
setChanged();
notifyObservers("主题将消息置空!");
clearChanged();
return;
}
if(this.msg!=null&&msg!=null&&this.msg.equals(msg)) {
return;
}
this.msg = msg;
setChanged();
notifyObservers("主题更新消息:"+this.msg);
clearChanged();
}
}
package observer.jdk;
import java.util.Observable;
import java.util.Observer;
/**
* 观察者
* @author 发挥哥
*
*/
public class MyObserver implements Observer {
/**
* 通过该方法将观察者自己注册到主题
* @param observable
*/
public void registerToSubject(Observable observable) {
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
System.out.println(this.getClass().getName()+"收到主题"+o.getClass().getName()+"更新消息:"+arg);
}
}
编写测试方法:
package observer.jdk;
public class MyTest {
public static void main(String[] args) {
//创建主题1、主题2
MySubject subject1=new MySubject();
OtherSubject subject2=new OtherSubject();
//创建观察者
MyObserver observer=new MyObserver();
//将观察者注册到主题1、主题2
observer.registerToSubject(subject1);
observer.registerToSubject(subject2);
//主题1、主题2分别更新状态
subject1.editMsg("主题一更新");
subject2.editMsg("主题二更新");
}
}
测试结果如下:
observer.jdk.MyObserver收到主题observer.jdk.MySubject更新消息:主题更新消息:主题一更新
observer.jdk.MyObserver收到主题observer.jdk.OtherSubject更新消息:主题更新消息:主题二更新
比较好的观察者模式应用是微信公众号,公众号就是我们的主题,粉丝就是观察者。功能如下:
- 1、公众号就是主题,业务就是推送新消息;
- 2、观察者订阅主题,有新的消息自动送来;
- 3、当不想要此主题消息时,取消订阅即可;
总结:
观察者模式的实现方式是在观察者调用主题的addObserver方法将自己添加到主题持有的观察者列表里,主题在需要的时候调用notifyObservers遍历观察者列表,并分别调用观察者的update方法。
打个比方,主题就是婚介所,多个观察者就是多个未婚大龄男青年,男青年注册会员之后将自己的联系方式交给婚介所,婚介所在合适的时候(比如来了适龄女青年),就会挨个给注册的男青年打电话通知。