目录
场景
代码示例
定义
观察者模式的优缺点
本质
何时选用
简单变型-区别对待观察者
场景
我是一家报社,每当我发布一个新的报纸时,所有订阅我家报社的读者都可以接收到
代码示例
报纸对象
package day11观察者模式;
import java.util.Observable;
/**
* 报纸对象
*/
public class NewsPaper extends Observable {
/**
* 报纸的内容
*/
private String content;
/**
* 获取报纸的具体内容
* @return 报纸的具体内容
*/
public String getContent() {
return content;
}
/**
* 示意,设置报纸的具体内容,相当于要出版报纸了
* @param content 报纸的具体内容
*/
public void setContent(String content) {
this.content = content;
// 内容有了,说明又出新报纸了,那就通知所有的读者
this.setChanged();
// 然后主动通知,用的推的方式
this.notifyObservers(this.content);
// 如果用拉的方式,这么调用
// this.notifyObservers();
}
}
读者,也就是观察者
package day11观察者模式;
import java.util.Observable;
import java.util.Observer;
/**
* 真正的读者,也就是观察者
*/
public class Reader implements Observer {
/**
* 读者的姓名
*/
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
// 这里采用推的方式
System.out.println(name + "收到报纸了,进行阅读。目标推过来的内容是===" + arg);
// 这里是获取拉的数据
// System.out.println(name + "收到报纸了,进行阅读。主动到目标对象去拉的内容是===" + ((NewsPaper)o).getContent());
}
}
client
package day11观察者模式;
/**
* 客户端使用
*/
public class Client {
public static void main(String[] args) {
// 1.创建一个报纸,作为被观察者
NewsPaper subject = new NewsPaper();
// 2.创建阅读者,也就是观察者
Reader reader1 = new Reader();
reader1.setName("张三");
Reader reader2 = new Reader();
reader2.setName("李四");
Reader reader3 = new Reader();
reader3.setName("王五");
// 3.注册阅读者
subject.addObserver(reader1);
subject.addObserver(reader2);
subject.addObserver(reader3);
// 4.要出报纸啦
subject.setContent("本期内容是观察者模式");
}
}
解析:
报社就是一个发布者,读者就是一个个订阅者,发布者提供订阅方法,每次发布者发布新的东西时,就会通知订阅者这条新的消息。
报社NewsPaper:它继承了Observable类,这个类包含了注册方法,提示改变,通知方式(推和拉)等方法,NewsPaper包含了报纸内容对象,get方法就不说了,set时会进行提示改变了,并主动通知,用的推的方式。
观察者Reader:它实现了Observer接口,实现了update方法,java实现观察者模式时默认是拉模型,如果使用推模型,那么在update这个方法里通过推拉的获取方式都可以获取到值
定义
类似发布订阅模式
观察者模式的优缺点
本质
观察者模式的本质:触发联动
在程序运行期间,通过这样的联动可以控制观察者,在update执行的时候。
何时选用
简单变型-区别对待观察者
举例:根据水质污染的严重程度来分别告知不同的管理者
思路:将水质污染当作被观察的目标,监测员、预警人员和领导就是观察者。在目标里面进行判断,满足就通知,不满足不通知。
观察者接口
1.被通知的方法2.设置观察人员的职务3.获取观察人员的职务
package day11观察者模式.高级;
/**
* 水质观察者接口定义
*/
public interface WatcherObserver {
/**
* 被通知的方法
* @param subject 传入被观察的目标对象
*/
public void update(WaterQualitySubject subject);
/**
* 设置观察人员的职务
* @param job
*/
public void setJob(String job);
/**
* 获取观察人员的职务
* @return
*/
public String getJob();
}
观察者的具体实现
package day11观察者模式.高级;
public class Watcher implements WatcherObserver{
private String job;
@Override
public void update(WaterQualitySubject subject) {
// 这里采用拉的方式
System.out.println(job + "获取到通知,当前污染级别为: " + subject.getPolluteLevel());
}
@Override
public void setJob(String job) {
this.job = job;
}
@Override
public String getJob() {
return job;
}
}
目标的父对象(抽象类) :1.定义抽象是因为我们不需要全部都发消息,所以让子类去根据业务的实际需要自己去选择性的通知。2.而且在我们传入目标对象的时候具有很好的封装性,不需要对外展示具体的目标对象,传给观察者的只是目标的父抽象类。
package day11观察者模式.高级;
import java.util.ArrayList;
import java.util.List;
/**
* 定义目标的父对象
*/
public abstract class WaterQualitySubject {
/**
* 用来保存注册的观察者对象
*/
protected List<WatcherObserver> observers = new ArrayList<WatcherObserver>();
/**
* 注册观察者对象
* @param observer 观察者对象
*/
public void attach(WatcherObserver observer){
observers.add(observer);
}
/**
* 删除观察者对象
* @param observer
*/
public void detach(WatcherObserver observer){
observers.remove(observer);
}
/**
* 通知对应的观察者对象
*/
public abstract void notifyWatchers();
/**
* 获取水质污染的级别
*/
public abstract int getPolluteLevel();
}
目标的实现(继承目标父抽象类)
package day11观察者模式.高级;
/**
* 具体的检测对象
*/
public class WaterQuality extends WaterQualitySubject{
/**
* 污染的级别,0正常 1轻度污染 2中度污染 3高度污染
*/
private int polluteLevel = 0;
/**
* 获取水质污染的级别
* @return
*/
@Override
public int getPolluteLevel() {
return polluteLevel;
}
/**
* 当检测水质情况后,设置水质污染的级别
* @param polluteLevel 水质污染的级别
*/
public void setPolluteLevel(int polluteLevel) {
this.polluteLevel = polluteLevel;
// 通知相应的观察者
this.notifyWatchers();
}
/**
* 通知相应的观察者对象
*/
@Override
public void notifyWatchers() {
// 循环所有注册的观察者
for (WatcherObserver watcher : observers) {
// 开始根据污染级别判断都通知谁
if (this.polluteLevel >= 0 ){
// 通知监测员做记录
if ("监测人员".equals(watcher.getJob())){
watcher.update(this);
}
}
if (this.polluteLevel >= 1 ){
// 通知监测员做记录
if ("预警人员".equals(watcher.getJob())){
watcher.update(this);
}
}
if (this.polluteLevel >= 2 ){
// 通知监测员做记录
if ("领导".equals(watcher.getJob())){
watcher.update(this);
}
}
}
}
}
client
package day11观察者模式.高级;
public class Client {
public static void main(String[] args) {
// 创建具体的检测对象
WaterQuality subject = new WaterQuality();
// 创建几个观察者
Watcher watcher1 = new Watcher();
watcher1.setJob("监测人员");
Watcher watcher2 = new Watcher();
watcher2.setJob("预警人员");
Watcher watcher3 = new Watcher();
watcher3.setJob("领导");
// 注册观察者
subject.attach(watcher1);
subject.attach(watcher2);
subject.attach(watcher3);
// 填写水质报告
System.out.println("水质正常---");
subject.setPolluteLevel(0);
System.out.println("轻度污染---");
subject.setPolluteLevel(1);
System.out.println("重度污染---");
subject.setPolluteLevel(2);
}
}
解析:
从client开始,先创建了具体的检测对象, 他有什么用呢?可以注册观察者,可以发布通知
创建了几个观察者实现对象,给他们赋予了不同的职务。
将观察者跟目标对象进行关联,也就是注册,点进去看一下,调用了attach方法,这个方法不是具体的检测对象本身的,而是它的抽象父类中的方法
都注册进去之后,目标对象开始运行,他现在出了一个水质为0的污染报告,这个时候应该传递给谁信息呢?
点进这个setPolluteLevel(0)看一下 。这个是具体的检测对象本身的方法。
可以看出首先将传过来的值赋值给当前对象,然后调用了 this.notifyWatchers()方法。我们接着往下。
进来这个方法后,遍历所有注册到目标对象的观察者(订阅了该目标对象的订阅者),这个observers哪里来的?(),是注册而来的。
接着判断属于哪一类的污染,此时过来的污染为0,就判断当前观察者的职务是不是检测人员,如果是就调用update更新的方法,如果不是就不用管。
我们看一下update,被通知方法的接口
接下来看看它的实现
解析:判断是不是检测人员,如果是我就给他发送消息(它就能接收到消息)。
如果污染为1,那么就满足两个条件,就会给预警人员和检测人员都发送消息