简介
本文介绍观察者模式最基础的写法:手动注册。
本写法的优缺点
优点
- 纯手写,有利于理解观察者模式的原理。
缺点
- 需要手动注册观察者,不能自动处理。
实例
本处使用一个贴近真实场景的案例:订单取消时,要有一系列操作,比如:账户业务需要给用户退钱、库存业务需要给商品退回数量。
在这个场景中,取消订单就是被观察者,账户业务和库存业务是观察者。
项目结构

源码地址
此隐藏内容仅限VIP查看升级VIP
业务数据类
取消订单时,主题会将这个数据给到感兴趣的观察者。
package com.knife.designPattern.bo;
import lombok.Data;
@Data
public class CancelOrderBO {
private String orderNo;
}
主题(取消订单)
本处只使用到了被观察者的具体类,无需抽象被观察者。
package com.knife.designPattern.subject;
import com.knife.designPattern.bo.CancelOrderBO;
import com.knife.designPattern.observer.CancelOrderObserver;
import java.util.ArrayList;
import java.util.List;
/**
* 具体被观察者(主题): 订单取消
*/
public class CancelOrderSubject {
private final List<CancelOrderObserver> list = new ArrayList<>();
public void addObserver(CancelOrderObserver cancelOrderObserver) {
list.add(cancelOrderObserver);
}
public void removeObserver(CancelOrderObserver cancelOrderObserver) {
list.remove(cancelOrderObserver);
}
public void process(CancelOrderBO cancelOrderBO) {
System.out.println("订单取消的相关数据:" + cancelOrderBO);
for (CancelOrderObserver cancelOrderObserver : list) {
cancelOrderObserver.process(cancelOrderBO);
}
}
}
观察者
抽象观察者
package com.knife.designPattern.observer;
import com.knife.designPattern.bo.CancelOrderBO;
/**
* 抽象观察者:业务
*/
public interface CancelOrderObserver {
/**
* 业务收到订单取消数据后的操作
*/
void process(CancelOrderBO cancelOrderBO);
}
具体观察者(账户业务)
package com.knife.designPattern.observer.impl;
import com.knife.designPattern.bo.CancelOrderBO;
import com.knife.designPattern.observer.CancelOrderObserver;
/**
* 具体观察者: 账户业务
*/
public class AccountCancelOrderObserverImpl implements CancelOrderObserver {
@Override
public void process(CancelOrderBO cancelOrderBO) {
System.out.println("账户业务开始处理订单取消。接收到的消息:" + cancelOrderBO);
}
}
具体观察者(库存业务)
package com.knife.designPattern.observer.impl;
import com.knife.designPattern.bo.CancelOrderBO;
import com.knife.designPattern.observer.CancelOrderObserver;
/**
* 具体观察者: 库存业务
*/
public class StorageCancelOrderObserverImpl implements CancelOrderObserver {
@Override
public void process(CancelOrderBO cancelOrderBO) {
System.out.println("库存业务开始处理订单取消。接收到的消息:" + cancelOrderBO);
}
}
主题持有者
只有主题还不行,需要有一个地方去调用它,添加一个主题持有者
package com.knife.designPattern.subject;
import com.knife.designPattern.bo.CancelOrderBO;
import com.knife.designPattern.observer.CancelOrderObserver;
import com.knife.designPattern.observer.impl.AccountCancelOrderObserverImpl;
import com.knife.designPattern.observer.impl.StorageCancelOrderObserverImpl;
public class SubjectHolder {
private static final CancelOrderSubject instance = init();
private SubjectHolder() {
}
private static CancelOrderSubject init() {
// 创建被观察者
CancelOrderSubject orderCancelSubject = new CancelOrderSubject();
// 创建观察者
CancelOrderObserver accountCancelOrderObserver =
new AccountCancelOrderObserverImpl();
CancelOrderObserver storageCancelOrderObserver =
new StorageCancelOrderObserverImpl();
// 添加观察者
orderCancelSubject.addObserver(accountCancelOrderObserver);
orderCancelSubject.addObserver(storageCancelOrderObserver);
return orderCancelSubject;
}
public static void process(CancelOrderBO cancelOrderBO) {
instance.process(cancelOrderBO);
}
}
测试
测试类
package com.knife.designPattern.controller;
import com.knife.designPattern.bo.CancelOrderBO;
import com.knife.designPattern.subject.SubjectHolder;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "订单")
@RestController
@RequestMapping("order")
public class OrderController {
@ApiOperation("取消订单")
@PostMapping("cancel")
public String cancel(@RequestBody CancelOrderBO cancelOrderBO) {
SubjectHolder.process(cancelOrderBO);
return "success";
}
}
访问接口
启动项目后,访问Knife4j(接口文档)的地址:http://localhost:8080/doc.html
(当然,你用postman也完全可以)

结果
订单取消的相关数据:CancelOrderBO(orderNo=1234) 账户业务开始处理订单取消。接收到的消息:CancelOrderBO(orderNo=1234) 库存业务开始处理订单取消。接收到的消息:CancelOrderBO(orderNo=1234)

请先 !