目录
目标
熟悉观察者设计模式,了解观察者设计模式的使用场景、具体实现(包括:推设计模式、拉设计模式、被动观察者设计模式)。
概述
一、行为设计模式
行为设计模式是设计模式的一种类型。该类型的设计模式关注的重点在于对象的行为(通信和交互),而非对象的创建方式。较为常见的行为设计模式有:观察者模式、策略模式、命令模式、责任链模式等。优点是减少了对象之间的耦合度。
二、观察者设计模式
对象之间存在一对多的依赖关系,多个观察者对象同时监听某一个主题对象(被观察者对象)。当主题对象的状态发生改变时会通知其他观察者对象。主题对象实现思路:
- 主题对象类定义一个集合observerList用来保存多个观察者对象。
- 定义观察者对象注册到主题对象的方法、主题对象从注册表中剔除指定观察者对象的方法、定义通知方法。
- 当对象状态发生改变时调用该方法,以遍历observerList的方式通知所有观察者对象。
优点:
- 被观察者使用注册表的方式囊括了所有观察者,降低了观察者对象和被观察者对象之间的依赖关系,提高了系统的扩展性和可维护性。
- 被观察者类提供了注册,剔除的方法,使得代码扩展性增强。
- 该模式实现了事件驱动的方式,使业务实现方案变得更灵活。
缺点:
- 观察者过多会导致内存开销大。
- 容易发生循环依赖问题。
种类:
- 推设计模式:最简单的模式,指被观察者主动将状态推送给观察者。
- 拉设计模式:被观察者只告知观察者状态发生了改变,观察者需要通过主动去“拉”的方式,获取被观察者的具体状态。
- 被动观察者设计模式:引入了一个中介者对象来协调主题对象和观察者之间的交互。主题对象不再直接通知观察者,而是将状态变化通知给中介者对象,由中介者对象负责通知观察者。
三、使用场景
- 一个对象发生改变需要通知其他对象,其他对象可以自由选择监听或者不监听。
- 一个对象发生改变需要通知其他对象,其他对象的类型和数量不确定时。
四、列举观察者模式在成熟的框架中的应用
- 在RabbitMQ中,Exchange和Queue之间的绑定关系。
- Java AWT/Swing事件处理机制。
- Spring Framework事件机制。
- Java Message Service(JMS)。
实现
推设计模式
需求
多个用户订阅了天气预报系统,则天气预报作为被观察者对象,一旦天气预报发生更新,则订阅者需要马上感知并获取天气预报状态。
观察者接口
package com.ctx.observer.push;
// 定义用户作为观察者对象
interface Observer {
public void update(String weather);
}
观察者接口实现类
package com.ctx.observer.push;
// 实现具体的观察者对象
class User implements Observer {
private String name;
public User(String name) {
this.name = name;
}
// 接收天气预报信息
public void update(String weather) {
System.out.println(name + "收到天气预报信息:" + weather);
}
}
被观察者类
package com.ctx.observer.push;
import java.util.ArrayList;
import java.util.List;
// 定义天气预报机构作为主题对象
class WeatherForecast {
private List<Observer> observers = new ArrayList<Observer>();
private String weather;
// 注册观察者对象
public void registerObserver(Observer observer) {
observers.add(observer);
}
// 移除观察者对象
public void removeObserver(Observer observer) {
observers.remove(observer);
}
// 通知所有观察者对象
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(weather);
}
}
// 更新天气预报信息
public void setWeather(String weather) {
this.weather = weather;
notifyObservers();
}
}
调用测试类
package com.ctx.observer.push;
// 测试代码
public class Test {
public static void main(String[] args) {
WeatherForecast weatherForecast = new WeatherForecast();
// 创建多个用户作为观察者对象
User user1 = new User("张三");
User user2 = new User("李四");
User user3 = new User("王五");
// 注册观察者对象
weatherForecast.registerObserver(user1);
weatherForecast.registerObserver(user2);
weatherForecast.registerObserver(user3);
// 发布天气预报信息
weatherForecast.setWeather("今天晴朗,温度26℃");
// 移除观察者对象
weatherForecast.removeObserver(user2);
// 发布天气预报信息
weatherForecast.setWeather("明天有雨,温度22℃");
}
}
拉设计模式
需求
多个用户订阅了新闻频道,则新闻频道作为被观察者对象,一旦新闻频道发生更新,则订阅者需要马上感知新闻频道发生了更新。但是具体更新了什么新闻,需要由用户主动去获取。
观察者接口
package com.ctx.observer.pull;
// 定义观察者对象
interface Observer {
void update(News news);
}
观察者接口实现类
package com.ctx.observer.pull;
// 定义具体的观察者对象(居民对象)
class Resident implements Observer {
private String name;
public Resident(String name) {
this.name = name;
}
// 拉模式:观察者主动获取消息内容
public void update(News news) {
System.out.println(name + "收到消息: " + news.getNewsContent());
}
public String getName() {
return name;
}
}
被观察者类
package com.ctx.observer.pull;
import java.util.ArrayList;
import java.util.List;
// 定义被观察者对象(新闻对象是被观察者)
class News {
private List<Observer> observers = new ArrayList<>();
private String newsContent;
public void addObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void setNewsContent(String newsContent) {
this.newsContent = newsContent;
notifyObservers();
}
// 通知观察者更新消息
private void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
// 获取消息内容
public String getNewsContent() {
return newsContent;
}
}
调用测试类
package com.ctx.observer.pull;
public class Test {
public static void main(String[] args) {
News news = new News();
// 居民订阅新闻
Resident resident1 = new Resident("张三");
Resident resident2 = new Resident("李四");
news.addObserver(resident1);
news.addObserver(resident2);
// 设置新闻内容,观察者拉取消息内容
news.setNewsContent("今天是个好日子!");
}
}
被动观察者设计模式
需求
患者住院治疗,则患者作为被观察者,医生和护士作为观察者。但是医生和护士不能一直陪在患者身边,为了及时响应,医院安装了医疗监护系统,一旦患者有需要则通过医疗监护系统通知医生和护士,医疗监护系统起到了中介者的作用,它来协调观察者和被观察者之间的交互。
观察者接口
package com.ctx.observer.passive;
// 定义观察者接口
interface Observer {
void update(); // 更新观察者状态
}
观察者接口实现类
package com.ctx.observer.passive;
// 定义具体的观察者,即医生和护士类
class MedicalStaff implements Observer {
private String name;
private Subject patient;
public MedicalStaff(String name, Subject patient) {
this.name = name;
this.patient = patient;
patient.registerObserver(this); // 注册观察者
}
@Override
public void update() {
String condition = ((Patient) patient).getCondition(); // 获取被观察者状态
System.out.println(name + "收到病人病情更新通知,病情为:" + condition); // 处理病情变化
}
}
中介者
package com.ctx.observer.passive;
// 定义中介者,即医疗监护系统类
class MedicalMonitor {
private Subject patient;
public MedicalMonitor(Subject patient) {
this.patient = patient;
}
public void setPatientCondition(String condition) {
((Patient) patient).setCondition(condition); // 更新被观察者状态
}
}
被观察者接口
package com.ctx.observer.passive;
// 定义被观察者接口
interface Subject {
void registerObserver(Observer observer); // 注册观察者
void removeObserver(Observer observer); // 移除观察者
void notifyObservers(); // 通知观察者
}
被观察者接口实现类
package com.ctx.observer.passive;
import java.util.ArrayList;
import java.util.List;
// 定义具体的被观察者,即病人类
class Patient implements Subject {
private List<Observer> observers; // 观察者列表
private String condition; // 病情
public Patient() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(); // 通知每个观察者更新状态
}
}
public void setCondition(String condition) {
this.condition = condition;
notifyObservers(); // 状态变化后通知所有观察者更新状态
}
public String getCondition() {
return condition;
}
}
调用测试类
package com.ctx.observer.passive;
// 测试
public class Test {
public static void main(String[] args) {
Patient patient = new Patient(); // 创建被观察者
MedicalMonitor monitor = new MedicalMonitor(patient); // 创建中介者
MedicalStaff doctor = new MedicalStaff("张医生", patient); // 创建观察者
MedicalStaff nurse = new MedicalStaff("李护士", patient); // 创建观察者
monitor.setPatientCondition("发热"); // 病人状态变化,触发通知
patient.removeObserver(doctor); // 病人移除一个观
System.out.println("--------------------------");
monitor.setPatientCondition("感冒"); // 病人状态变化,触发通知
}
}