1,什么是观察者模式?定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动 更新自己。形象点说就像 有几个人订阅邮件一样,比如订阅腾讯新闻,腾讯新闻就是一个主题(subject),你们每一个人都是一个观察者(observer);可以取消订阅,就不是观察者了,也 可以增加一个订阅的人,这个人就成了观察者,一般观察者有以下几个角色:
抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable,jdk自带的观察者模式实现,用的是这个词,不过我们最好统一用Subject这个词)角色
2, 具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
具体主题角色又叫做具体被观察者(Concrete Observable)角色
3, 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口
4, 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。
如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。
首先定抽象主题角色:Subject.java
package observer;
/**
* 抽象主题角色
* @author yt
*
*/
public interface Subject {
/**
* 向主题注册观察者
* @param o
* @author mj
*/
public void registerObserver(Observer o);
/**
* 从主题中删除观察者
* @param o
* @author mj
*/
public void removeObserver(Observer o);
/**
*
* 通知观察者
* @author mj
*/
public void notifyObservers();
}
再定义具体主题角色 TencentSubject.java
package observer;
import java.util.ArrayList;
import java.util.List;
/**
* 腾讯新闻这个主题
* @author yt
*
*/
public class TencentSubject implements Subject {
//主题中要持有所有的观察者,观察这都要被注册到主题中的这个list中
private List<Observer> list=new ArrayList<>();
//标题和内容,为了观察者能够 使用拉模式(pull)获取自己需要的信息(比如只获取标题,或者只获取内容,或者都获取),提供这两个变量的get/set方法
private String title;
private String content;
@Override
public void registerObserver(Observer o) {
list.add(o);//注册观察者
}
@Override
public void removeObserver(Observer o) {
list.remove(o);//删除观察者
}
@Override
public void notifyObservers() {
for(Observer o:list){
o.update(title, content);//推模式
//o.update(this); //拉模式
}
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
定义抽象观察者角色 Observer.java
package observer;
/**
* 抽象的观察者角色
* @author yt
* 观察者订阅新闻又分为两种,推送模式(push),和拉取模式(pull)
*/
public interface Observer {
/**
* 1,推送模式,不管观察者需不需要,一股脑的全推送给观察者。
* 观察者向主题订阅自己感兴趣的内容。这里以订阅新闻为列子,当有新的新闻时,主题把 标题,和内容推送给观察者。
* @param title
* @param content
* @author mj
*/
public void update(String title,String content);
/**
* 拉取(pull)模式,一般把主题对象传过来,观察这,根据需要到主题中拉取自己需要的内容,而不是被动的接受 主题推送过来的。
* @param subject
* @author mj
*/
public void update(Subject subject);
}
定义具体观察者角色 ZhangsanObserver.java
package observer;
/**
* 观察者张三
* @author yt
*
*/
public class ZhangsanObserver implements Observer {
/**
* 推模式
*/
@Override
public void update(String title, String content) {
System.out.println("我是观察者zhangsan收到的推送消息为:"+title+":"+content);
}
/**
* 拉模式
*/
@Override
public void update(Subject subject) {
System.out.println("我是观察者zhangsan收到的推送消息为:"+((TencentSubject)subject).getTitle());
}
}
定义具体观察者角色 LishiObserver.java
package observer;
/**
* 观察者张三
* @author yt
*
*/
public class LishiObserver implements Observer {
/**
* 推模式
*/
@Override
public void update(String title, String content) {
System.out.println("我是观察者lishi收到的推送消息为:"+title+":"+content);
}
/**
* 拉模式
*/
@Override
public void update(Subject subject) {
System.out.println("我是观察者lishi收到的推送消息为:"+((TencentSubject)subject).getTitle());
}
}
客户端调用
package observer;
public class ClientObserver {
public static void main(String[] args) {
//创建主题对象
TencentSubject subject=new TencentSubject();
//创建观察者对象
ZhangsanObserver zhangsan=new ZhangsanObserver();
LishiObserver lishi=new LishiObserver();
//把观察者注册到主题TencentSubject中
subject.registerObserver(zhangsan);
subject.registerObserver(lishi);
//当主题对象改变时(有新的新闻产生时)
subject.setTitle("美国大选");
subject.setContent("特朗普当选为美国总统!");
subject.notifyObservers();//还可以在这个方法中再添加一些逻辑,比如当满足某一条件时,再推送。
}
}
输出:
我是观察者zhangsan收到的推送消息为:美国大选:特朗普当选为美国总统!
我是观察者lishi收到的推送消息为:美国大选:特朗普当选为美国总统!
二:jdk自带的观察者模式实现。
jdk 提供了 Observerable /Observer 实现,Observerable 相当于本列的 Subject 只不过他是一个类,而不是一个接口,当需要多继承的时候就没法了,所以说有一定的缺陷。。。