观察者模式:定义一种一对多的依赖关系,让多个观察者对象同时监控某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
这个模式比较绕。我尽量吧上面具体展示在代码位置进行注释,希望对你能有帮助。我也是饶了好久好了若干次关系图才理清。希望我的注解能对你理解这种模式有所帮助。
首先我们把主题与观察者接口定义:
public interface ISubject {
public void registObserver(IObserve o);//注册
public void logOutObserver(IObserve o);//注销
public void notfiyObserver();//通知
}
public interface IObserve {
public void update(String address,String weather, double humidity, double pm2_5);
}
观察者实现:
public class Person implements IObserve {
private ISubject notice;
private String name;
public Person(String name) {
this.name = name;
}
public void registObserver(ISubject notice){
this.notice = notice;
notice.registObserver(this);
}
public void removeObserver(ISubject notice){
this.notice = notice;
notice.logOutObserver(this);
}
@Override
public void update(String address,String weather
, double humidity, double pm2_5) {//获得通知后进行自动更新展示信息
System.out.println("通知"+name+" "+address+"天气:"+weather+", 湿度:"
+humidity+"%, pm2_5:"+pm2_5);
}
}
public class Phone implements IObserve {
private ISubject notice;
private String name;
public Phone(String name) {
this.name = name;
}
public void registObserver(ISubject notice){
this.notice = notice;
notice.registObserver(this);
}
public void removeObserver(ISubject notice){
this.notice = notice;
notice.logOutObserver(this);
}
@Override
public void update(String address,String weather,
double humidity, double pm2_5) {//获得通知后进行自动更新展示信息
System.out.println("通知"+name+" "+address+"天气:"+weather+
", 湿度:"+humidity+"%, pm2_5:"+pm2_5);
}
}
主题实现:
public class Notice implements ISubject {//通知继承主题
private String weather;
private double humidity;
private double pm2_5;
private String address;
//用来实现一对多依赖关系,也就是说一个主题对应多个通知观察者对象。
Set<IObserve> io = new HashSet<IObserve>();
public Notice(String address) {
this.address = address;
}
@Override
public void registObserver(IObserve o) {
if(!io.contains(o))
io.add(o);
}
@Override
public void logOutObserver(IObserve o) {
io.remove(o);
}
@Override
public void notfiyObserver() {//通知订阅者
for (IObserve iObserve : io) {
iObserve.update( address,weather, humidity, pm2_5);
}
}
public void notice( String weather,double humidity,double pm2_5){
this.weather = weather;
this.humidity = humidity;
this.pm2_5 = pm2_5;
notfiyObserver();//监控作用,当主题天气进行更新时进行通知观察者对象
}
}
测试Client
public static void main(String[] args) {
Notice is = new Notice("上海");
Person PersonA = new Person("张三");
Person PersonB = new Person("李四");
Person PersonC = new Person("王五");
Phone phone = new Phone("老大手机");
PersonA.registObserver(is);
PersonB.registObserver(is);
PersonC.registObserver(is);
phone.registObserver(is);
is.notice("晴", 50, 35);
Notice is1 = new Notice("北京");
PersonA.registObserver(is1);
PersonB.registObserver(is1);
PersonB.removeObserver(is1);
is1.notice("晴", 40, 35);
}
输出结果:
通知老大手机 上海天气:晴, 湿度:50.0%, pm2_5:35.0
通知张三 上海天气:晴, 湿度:50.0%, pm2_5:35.0
通知李四 上海天气:晴, 湿度:50.0%, pm2_5:35.0
通知王五 上海天气:晴, 湿度:50.0%, pm2_5:35.0
通知张三 北京天气:晴, 湿度:40.0%, pm2_5:35.0
突然发现上面的 IObserve接口写的好烂,为什么会去规定通知内容格式,不符合设计模式的开闭原则,因此就临时进行了修改,为了记录就没有直接更改上面的代码就展示在下面了。主要还是参考了jdk提供的观察者模式文档。代码如下
接口:
public interface IObserve {
public void update(ISubject notice);
}
实现:
public class Person implements IObserve {
private ISubject notice;
private String name;
public Person(String name) {
this.name = name;
}
public void registObserver(ISubject notice){
this.notice = notice;
notice.registObserver(this);
}
public void removeObserver(ISubject notice){
this.notice = notice;
notice.logOutObserver(this);
}
@Override
public void update(ISubject o) {//获得通知后进行自动更新展示信息
if(o instanceof Notice){
Notice notice = (Notice) o;
System.out.println("通知"+name+" "+notice.getAddress()+"天气:"+notice.getWeather()+
", 湿度:"+notice.getHumidity()+"%, pm2_5:"+notice.getPm2_5());
}
}
}
把传输格式由固定格式抽象化,因此不单单可以订阅不同位置的天气情况了,也可以订阅各种格式的类,只要进行相应的数据处理就ok。
那么接下来就看一下触发我灵感的JDK帮我们实现的观察者模式应用吧。
public class Notices extends Observable{
private String weather;
private double humidity;
private double pm2_5;
private String address;
public Notices(String address) {
this.address = address;
}
public void measurementsChanged() {
setChanged();
notifyObservers();
}
public void notice( String weather,double humidity,double pm2_5){
this.weather = weather;
this.humidity = humidity;
this.pm2_5 = pm2_5;
measurementsChanged();
}
public String getWeather() {
return weather;
}
public double getHumidity() {
return humidity;
}
public double getPm2_5() {
return pm2_5;
}
public String getAddress() {
return address;
}
}
public class PersonC implements Observer{
private Observable obs;
private String name;
public PersonC(String name) {
this.name = name;
}
public void registObserver(Observable notice){
this.obs = notice;
notice.addObserver(this);
}
public void removeObserver(Observable notice){
this.obs = notice;
notice.deleteObserver(this);
}
@Override
public void update(Observable arg0, Object arg1) {
if(arg0 instanceof Notices){
Notices o = (Notices) arg0;
System.out.println("通知"+name+" "+o.getAddress()+"天气:"+o.getWeather()+", 湿度:"
+o.getHumidity()+"%, pm2_5:"+o.getPm2_5());
}
}
}
public static void main(String[] args) {
Notices is = new Notices("shanghai");
PersonC c = new PersonC("张三");
c.registObserver(is);
is.notice("晴", 50, 35);
}
Observable 就是我们之前的主题类。相对来说JDK帮我们吧订阅删除通知帮助我们进行封装,我们不需要自己来管理只需要调用相应的接口就OK。 观察者类进本类似,调用也一样。
代码中用一个 setChanged方法来表示状态有改变,在发送改变前我们需要先使用setChanged()将其设置为true。 在 notifyObservers()之后自动调用 clearChanged() 恢复 changed 为false。所以不需要我们自己来重置。
Observable 中集合类是Vector 存储的,并且所有方法都是同步的,所以 Observable 都是线程安全的。可以用于多线程开发。
观察者模式消耗了我很长的时间,简单总结一下就是一对多依赖,多观察一,一改变,多进行自行改变。