观察者模式
要多想,理解后闭上眼睛,想象下生活中哪些场景用到,进而回顾平时代码生涯中哪些场景可以用该模式去实现。
1、概念
观察者模式是什么
有时被称作发布/订阅模式、模型-视图模式,它是对象行为型模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
主题对应观察者主要有两大类和四小类
两大类:主题、观察者
四小类:抽象主题,具体主题,抽象观察者,具体观察者
具体主题注入所有观察者,主题变动通知观察者,注册观察者,取消观察者
具体观察者注入主题,然后调用主题注册方法把自己注册进主题
画UML类关系图,理清关系
工具:PowerDesigner (离线工具),在线工具(https://www.processon.com/)
1. 主题Subject
首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察,用Vector是线程同步的,比较安全,也可以使用ArrayList,是线程异步的,但不安全。
public class Subject {
//增加一个观察者
void addObserver(Observer observer);
//删除一个观察者
void deleteObserver(Observer observer);
//通知所有观察者
void notifyObserver();
}
2. 抽象观察者Observer
继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。
public interface Observer {
//更新
public void update();
}
3. 具体主题
继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。
public class ConcreteSubject extends Subject {
//观察者数组
private Vector<Observer> oVector = new Vector<>();
//增加一个观察者
public void addObserver(Observer observer) {
this.oVector.add(observer);
}
//删除一个观察者
public void deleteObserver(Observer observer) {
this.oVector.remove(observer);
}
//通知所有观察者
public void notifyObserver() {
for(Observer observer : this.oVector) {
observer.update();
}
}
}
## 4. 具体观察者
```c
public class ConcreteObserver implements Observer {
@Override
public void update() {
System.out.println("收到消息,进行处理");
}
}
5. Client客户端
首先创建一个被观察者,然后定义一个观察者,将该被观察者添加到该观察者的观察者数组中,进行测试。
public class Client {
public static void main(String[] args) {
//创建一个主题
ConcreteSubject subject = new ConcreteSubject();
//定义一个观察者
Observer observer = new ConcreteObserver();
//观察
subject.addObserver(observer);
//开始活动
subject.doSomething();
}
}
闭上眼睛想象一下生活中有哪些类似的场景,能想多少想多少
- 手机丢了,委托别人给其他人发消息通知
- 通知老师/老板来了
- 拍卖,拍卖师观察最高标价,然后通知给其它竞价者竞价
- 在一个目录下建立一个文件,会同时通知目录管理器增加目录,并通知磁盘减少空间,文件是被观察者,目录管理器和磁盘管理器是观察者
- 猫叫了一声,吓着了老鼠,也惊到了主人,猫是被观察者,老鼠和人是观察者
- 疫情期间:一个人从疫区过来,则这个人是被观察的对象,当体温超过37度,保安拒绝出入公共场所、社区进行登记、医院找拉去隔离治疗、健康码由绿码显示为红码
代码实现案例6(一个人从疫区回来)
代码结构
1、一个从疫区回来的人
/**
* 从疫区回来的人
*/
public class EpidemicAreaPerson {
//体温
private int temperature;
//姓名
private String name;
public int getTemperature() {
return temperature;
}
public void setTemperature(int temperature) {
this.temperature = temperature;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2、测体温事件抽象
/**
* 测体温事件抽象
*/
public interface ThermometerEvent {
//注册观察者
void registerObserver(Observer observer);
//移除观察者
void removeObserver(Observer observer);
//通知所有观察者
public void notifyObserver(EpidemicAreaPerson epidemicAreaPerson);
}
3、测体温事件具体实现
public class ThermometerEventImpl implements ThermometerEvent {
//观察者数组
private Vector<Observer> oVector = new Vector<>();
@Override
public void registerObserver(Observer observer) {
this.oVector.add(observer);
}
@Override
public void removeObserver(Observer observer) {
this.oVector.remove(observer);
}
@Override
public void notifyObserver(EpidemicAreaPerson epidemicAreaPerson) {
if (epidemicAreaPerson.getTemperature() > 37) {
for (Observer observer : this.oVector) {
observer.update(epidemicAreaPerson);
}
} else {
System.out.println("没毛病,因为他是老铁");
}
}
}
4、观察者抽象
/**
* 观察者
*/
public interface Observer {
//更新
public void update(EpidemicAreaPerson epidemicAreaPerson);
}
5、具体观察者-医生
public class DoctorObserver implements Observer {
@Override
public void update(EpidemicAreaPerson epidemicAreaPerson) {
System.out.println("医生把" + epidemicAreaPerson.getName() + "带走隔离治疗" + "疑似者温度" + epidemicAreaPerson.getTemperature());
}
}
6、具体观察者-保安
public class BaoAnObserver implements Observer {
@Override
public void update(EpidemicAreaPerson epidemicAreaPerson) {
System.out.println("疑似者温度" + epidemicAreaPerson.getTemperature() + "保安拒绝" + epidemicAreaPerson.getName() + "出入公共场所");
}
}
7、模拟事件过程
//一个超过37度的人
EpidemicAreaPerson epidemicAreaPerson = new EpidemicAreaPerson();
epidemicAreaPerson.setName("xiaohong");
epidemicAreaPerson.setTemperature(38);
//创建一个主题
ThermometerEvent subject = new ThermometerEventImpl();
//定义一个观察者
Observer observer = new DoctorObserver();
Observer baoAnObserver = new BaoAnObserver();
//观察
subject.registerObserver(observer);
subject.registerObserver(baoAnObserver);
//开始活动
subject.notifyObserver(epidemicAreaPerson);
后面还可以加这样的场景,医生观察者再作为主题,如果该疑似测核酸显示阳性,则上报国家统计,通报媒体,通知疑似者家人。