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