观察者模式
# 观察者模式
观察者模式类似于生活中关注微信公众号、明星微博等,当公众号或明星发布信息时,粉丝就会收到信息。
# 定义
观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
# 结构
主题(Subject):也称为被观察者或可观察对象,它是具有状态并且可以被观察的对象。主题维护一个观察者列表,并提供方法用于添加、删除和通知观察者。当主题的状态发生变化时,它会通知所有观察者。
观察者(Observer):观察者是依赖于主题的对象,当主题的状态发生变化时,观察者会接收到相应的通知,并根据通知进行更新。观察者一般定义了一个更新方法,用于接收主题的通知。
- img: https://bitouyun.com/images/design-pattern/observer.png
link: https://bitouyun.com/images/design-pattern/observer.png
name: 观察者模式
1
2
3
2
3
# 优点
开闭原则。 你无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)。
# 应用场景
当一个对象状态的改变需要改变其他对象, 或实际对象是事先未知的或动态变化的时, 可使用观察者模式。
# 示例代码1
场景1
使用观察者模式实现了一对多的消息通知机制,当主题状态发生变化时,通知所有观察者,观察者都能收到相应的通知并进行处理。
/**
* 主题接口
* 被观察者或可观察对象,它是具有状态并且可以被观察的对象。
*/
public interface Subject {
// 注册观察者
void registerObserver(Observer observer);
// 移除观察者
void removeObserver(Observer observer);
// 通知所有观察者
void notifyObservers(String message);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 具体主题类
* 实现主题接口,维护一个观察者列表,并提供了注册、移除和通知观察者的方法。
*/
public class ConcreteSubject implements Subject {
// 维护了一个观察者列表
private List<Observer> 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(String message) {
observers.forEach(observer -> {
observer.update(message);
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 观察者接口
* 观察者是依赖于主题的对象,当主题的状态发生变化时,观察者会接收到相应的通知,并根据通知进行更新。观察者一般定义了一个更新方法,用于接收主题的通知
*/
public interface Observer {
// 更新方法,处理接收到的消息
void update(String message);
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
/**
* 具体观察者
* 实现观察者接口,定义了update方法来处理接收到的消息。
*/
@Slf4j
public class ConcreteObserver implements Observer {
// 观察者名称
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
log.info("{} 接收消息:{}", name, message);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 测试类
* 使用观察者模式实现了一对多的消息通知机制,当主题状态发生变化时,通知所有观察者,观察者都能收到相应的通知并进行处理。
*/
@Slf4j
public class Client {
public static void main(String[] args) {
// 创建主题
Subject subject = new ConcreteSubject();
// 创建观察者
Observer observer1 = new ConcreteObserver("观察者1");
Observer observer2 = new ConcreteObserver("观察者2");
Observer observer3 = new ConcreteObserver("观察者3");
// 注册观察者
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.registerObserver(observer3);
// 发布消息
log.info("发布消息------");
subject.notifyObservers("你好,观察者!");
// 移除观察者
subject.removeObserver(observer1);
// 发布消息
log.info("移除观察者,再次发布消息------");
subject.notifyObservers("再见,观察者!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Make sure to add code blocks to your code group
# 示例代码2
场景2
网上购物,顾客下单后除了要记录订单信息外,还需要增加积分、发送下单短信等,
主业务中只保存订单信息,增加积分、发送短信让观察者去实现,当保存订单信息后通知观察者添加积分记录、发送短信。 主业务开启事务,如过不想让子业务影响主业务,则捕获子业务的异常。
public class OrderEvent extends ApplicationEvent {
private Order order;
public OrderEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() {
return order;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
@Slf4j
@Component
public class OrderListener implements ApplicationListener<OrderEvent> {
@Resource
private PointRecordRepository pointRecordRepository;
@Override
@Transactional
public void onApplicationEvent(OrderEvent orderEvent) {
log.info("接收到订单消息:{}", orderEvent);
// 增加积分记录
PointRecord pointRecord = new PointRecord();
pointRecord.setUserId(orderEvent.getOrder().getUserId()); // 用户id
pointRecord.setOrderNo(orderEvent.getOrder().getOrderNo()); // 订单编号
pointRecord.setPoint(orderEvent.getOrder().getAmount()); // 积分
this.pointRecordRepository.insert(pointRecord);
// 发送短信
log.info("您的订单已创建,我们将尽快给您发货");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private OrderRepository orderRepository;
@Resource
private ApplicationContext applicationContext;
@Override
@Transactional
public String buy() {
log.info("客户下单");
// 1.创建订单
Order order = new Order();
String orderNo = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN);
order.setOrderNo(orderNo); // 订单号
order.setUserId(IdUtil.fastSimpleUUID()); // 用户id
order.setAmount(1000); // 订单金额
this.orderRepository.insert(order);
// 2.其它业务逻辑
applicationContext.publishEvent(new OrderEvent(this, order)); // 积分记录
applicationContext.publishEvent(new MessageEvent(order)); // 发送消息
return "购买成功";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Slf4j
@RestController
@RequestMapping(value = "/api")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@GetMapping(value = "/order/buy")
public ResponseEntity<?> buy() {
log.debug("请求下单");
this.orderService.buy();
return ResponseEntity.ok("下单成功");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
@AllArgsConstructor
public class MessageEvent {
private Order order;
}
1
2
3
4
5
2
3
4
5
// @Async // 需要异步放开注释,并在启动类增加@EnableAsync注解
@Slf4j
@Component
public class MessageListener {
@EventListener(MessageEvent.class)
public void sendMessage(MessageEvent messageEvent) {
log.info("发送短信,订单编号:{}, 积分:{}", messageEvent.getOrder().getOrderNo(), messageEvent.getOrder().getAmount());
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
// Make sure to add code blocks to your code group
上次更新: 2023/11/17, 09:38:49