个人主页:金鳞踏雨
个人简介:大家好,我是金鳞,一个初出茅庐的Java小白
目前状况:22届普通本科毕业生,几经波折了,现在任职于一家国内大型知名日化公司,从事Java开发工作
我的博客:这里是CSDN,是我学习技术,总结知识的地方。希望和各位大佬交流,共同进步 ~
【23种设计模式】观察者模式(Observer Pattern)
观察者模式概念
观察者模式角色
观察者模式代码实现
案例一:电子商务平台&&代码分析
(1)定义目标(Subject)接口
(2)定义观察者(Observer)接口
(3)实现具体目标(ConcreteSubject)类
(4)实现具体观察者(ConcreteObserver)类
(5)创建一个演示示例
运行结果
工作流程
案例二:异步多渠道群发消息(余胜军课程-案例)
(1)定义目标(Subject)接口
(2)定义观察者(Observer)接口
(3)实现具体观察者(ConcreteObserver)类
(4)实现具体目标(ConcreteSubject)类
(5)启动SpringBoot、访问路由
运行结果
观察者模式使用场景
观察者模式是一种行为型设计模式。
它定义了一种对象之间的一对多依赖关系,使得当一个对象的状态发生变化时,其他依赖该对象的对象都能够自动收到通知并进行相应的更新。
这种模式有时又称作发布-订阅模式、模型-视图模式。
一句话概括:当一个对象发送改变的时候,可以通知给其他所有的对象
在观察者模式(Observer Pattern)中,通常会涉及以下几个角色:
四者的关系图如下所示:
在观察者模式中,目标和观察者之间是松耦合的,目标只知道观察者的接口,并不知道具体的观察者实现。这样可以实现目标和观察者之间的解耦,使系统更加灵活可扩展。
电子商务平台,用户可以在该平台上发布商品,并且其他用户可以关注这些商品的状态变化。
/**
* 目标(Subject)接口
*/
public interface ProductSubject {
// 注册
void registerObserver(ProductObserver observer);
// 删除
void removeObserver(ProductObserver observer);
// 通知
void notifyObservers(String productName, String status);
}
ProductSubject 接口定义了目标(具体目标)的通用方法,包括注册观察者、移除观察者和通知观察者。
目标接口提供了一种规范,使得具体目标和具体观察者能够通过接口进行交互,实现松耦合的关系。
/**
* 观察者(Observer)接口:
*/
public interface ProductObserver {
// 更新
void update(String productName, String status);
}
ProductObserver 接口定义了观察者的通用方法,包括接收目标通知的更新。
观察者接口提供了一种规范,使得具体观察者能够实现该接口,并根据自己的需求定义具体的更新行为。
/**
* 具体目标(ConcreteSubject)
*/
public class ProductPublisher implements ProductSubject {
private Map productStatus;
private List observers;
public ProductPublisher() {
productStatus = new HashMap<>();
observers = new ArrayList<>();
}
@Override
public void registerObserver(ProductObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(ProductObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String productName, String status) {
for (ProductObserver observer : observers) {
observer.update(productName, status);
}
}
public void updateProductStatus(String productName, String status) {
productStatus.put(productName, status);
notifyObservers(productName, status);
}
}
ProductPublisher 类是具体目标的实现。它维护了一个 productStatus 的状态变量,用于存储商品的状态信息。
具体目标类实现了目标接口(ProductSubject),提供了注册观察者、移除观察者和通知观察者的方法。当商品的状态发生变化时,具体目标会调用 notifyObservers() 方法,通知所有已注册的观察者。
/**
* 具体观察者(ConcreteObserver)
*/
public class ProductSubscriber implements ProductObserver {
private String name;
public ProductSubscriber(String name) {
this.name = name;
}
@Override
public void update(String productName, String status) {
System.out.println(name + " 收到商品状态更新:");
System.out.println("商品名称:" + productName);
System.out.println("状态:" + status);
System.out.println();
}
}
ProductSubscriber 类是具体观察者的实现。它实现了观察者接口(ProductObserver),提供了更新方法(update())用于接收目标(ProductPublisher)通知的商品状态变化。
每个具体观察者(一对多)可以根据自己的需求做出相应的响应,比如打印商品状态更新。
public class ObserverPatternDemo {
public static void main(String[] args) {
// 创建具体目标
ProductPublisher productPublisher = new ProductPublisher();
// 创建两个具体观察者
ProductObserver subscriber1 = new ProductSubscriber("Subscriber 1");
ProductObserver subscriber2 = new ProductSubscriber("Subscriber 2");
// 注册
productPublisher.registerObserver(subscriber1);
productPublisher.registerObserver(subscriber2);
// 更新
productPublisher.updateProductStatus("Product 1", "In Stock");
productPublisher.updateProductStatus("Product 2", "Out of Stock");
System.out.println("=======================================");
// 删除观察者2
productPublisher.removeObserver(subscriber2);
// 更新(此时只有观察者1收到通知并更新)
productPublisher.updateProductStatus("Product 1", "Low Stock");
System.out.println("=======================================");
productPublisher.updateProductStatus("Product 3", "In Stock");
}
}
Subscriber 1 收到商品状态更新:
商品名称:Product 1
状态:In StockSubscriber 2 收到商品状态更新:
商品名称:Product 1
状态:In StockSubscriber 1 收到商品状态更新:
商品名称:Product 2
状态:Out of StockSubscriber 2 收到商品状态更新:
商品名称:Product 2
状态:Out of Stock=======================================
Subscriber 1 收到商品状态更新:
商品名称:Product 1
状态:Low Stock=======================================
Subscriber 1 收到商品状态更新:
商品名称:Product 3
状态:In Stock
定义多种发送消息的方式,采用异步多渠道群发消息。
/**
* 目标(Subject)接口
*/
public interface Subject {
// 添加
void addObServer(ObServer obServer);
// 通知
void notifyObServer(JSONObject jsonObject);
// 注册功能在StartService中,因为这是一个SpringBoot的项目,将观察者注册的过程放在SpringBoot启动成功后
}
/**
* 观察者接口
*/
public interface ObServer {
void sendMsg(JSONObject jsonObject);
}
SMS的观察者
/**
* 具体观察者(ConcreteObserver) 订阅者 (sub) 接收消息
*/
@Component
public class SMSObServer implements ObServer {
@Override
public void sendMsg(JSONObject jsonObject) {
System.out.println("使用观察者模式发送短信!!!");
}
}
Email的观察者
/**
* 具体观察者(ConcreteObserver) 订阅者 (sub) 接收消息
*/
@Component
public class EmailObServer implements ObServer {
@Override
public void sendMsg(JSONObject jsonObject) {
System.out.println("使用观察者模式发送Email!!!");
}
}
/**
* 具体目标(ConcreteSubject)
*/
@Component
public class MsgSubject implements Subject{
private List listObserver = new ArrayList<>();
// 新增Observer
public void addObServer(ObServer obServer) {
listObserver.add(obServer);
}
private ExecutorService executorService;
public MsgSubject() {
executorService = Executors.newFixedThreadPool(10);
}
// 发通知,给所有的观察者
public void notifyObServer(JSONObject jsonObject) {
for(ObServer obServer : listObserver) {
executorService.execute(new Runnable() {
@Override
public void run() {
obServer.sendMsg(jsonObject);
}
});
}
}
}
定义了添加观察者observer的方法,发送通知的方法(异步-线程池)。
注册观察者
/**
* 注册
*/
@Component
public class StartService implements ApplicationRunner, ApplicationContextAware {
@Autowired
private SMSObServer smsObServer;
@Autowired
private EmailObServer emailObServer;
@Autowired
private MsgSubject msgSubject;
private ApplicationContext applicationContext;
/**
* 当SpringBoot启动成功后,注册SMSObServer、EmailObServer
* @param args
* @throws Exception
*/
@Override
public void run(ApplicationArguments args) throws Exception {
// MsgSubject.addObServer(smsObServer);
// MsgSubject.addObServer(emailObServer);
/**
* 自动注册所有的观察者对象
* 1.使用spring获取该Observer下有哪些对象
* 2.直接添加到集合中
*/
Map map = applicationContext.getBeansOfType(ObServer.class);
for(String key : map.keySet()) {
ObServer obServer = map.get(key);
msgSubject.addObServer(obServer);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
在SpringBoot启动成功后,注册SMSObServer、EmailObServer。
这里使用反射的方式获取到所有的观察者对象。只不过,由于这里使用了Spring的相关内容,故我们直接 在SMSObServer、EmailObServer上加上 @Component注解,将其注册为Bean。
实际上,不使用上述方法,单纯使用反射也是可以实现的。
我们可以通过反射找到哪些类实现了 ObServer 接口,然后将它们添加到集合中,完成注册。
@RestController
public class OrderService {
Logger logger = Logger.getLogger("com.harmony.observer.MsgSubject.OrderService");
@Autowired
private MsgSubject msgSubject;
@RequestMapping("/addOrder")
public String addOrder() {
logger.info("调用数据库下订单代码:");
JSONObject jsonObject = new JSONObject();
jsonObject.put("sms","18159020099");
jsonObject.put("email","[email protected]");
msgSubject.notifyObServer(jsonObject);
return "success";
}
}
/**
* 启动类
*/
@SpringBootApplication
public class SpringApplicationDemo {
public static void main(String[] args) {
SpringApplication.run(SpringApplicationDemo.class);
}
}
先用浏览器访问一下这个地址
控制台输出
调用数据库下订单代码:
使用观察者模式发送短信!!!
使用观察者模式发送Email!!!
总之,当存在一对多的依赖关系,一个对象的状态变化需要通知多个其他对象进行相应的操作时,观察者模式是一种常用的设计模式。它能够实现对象之间的解耦,提高系统的可扩展性和灵活性。
文章到这里就结束了,如果有什么疑问的地方,可以在评论区指出~
希望能和大佬们一起努力,诸君顶峰相见
再次感谢各位小伙伴儿们的支持!!!