多线程并发执行,整体耗时 = 获取订单耗时 + max(抓取数据耗时)
如果你能够这样实现的,那恭喜你,你已步入高级程序员行列 。
然后呢,到此为止了?NO,接下来才是高潮?。。?
让我们打开认知,开启“抽象+封装”之旅 。
4. Fetcher封装仔细研究上述代码,寻找里面的==“变与不变”==,你会发现:
- 由于“我的订单” 和 “订单详情” 返回的是不同的 VO , 导致在实现绑定操作时写了两套基本一样的逻辑;
- Address、User、Product 的绑定逻辑骨架是一样的,一些细节操作存在差异;
4.1. 消除方法中的重复代码对于 “我的订单” 和 “订单详情” 返回==不同的 VO==,该怎么处理呢?
非常简单,思路如下:
- 【不变】抽象出“行为接口” Fetcher,统一操作行为
- 【变化】基于多态 , 不同的 VO 派生自相同的接口,但可以自己定义实现,从而实现个性化变化

文章插图
简单示例如下:
// 以 UserVO 为例,ProductVO、AddressVO , PayInfoVO 基本一致,不在赘述public interface UserVOFetcherV1 {Long getUserId();void setUser(UserVO user);}// OrderDetailVO 实现对应的接口,为了突出重点暂时忽略具体实现public class OrderDetailVOFetcherV1 extends OrderDetailVOimplements AddressVOFetcherV1,ProductVOFetcherV1,UserVOFetcherV1,PayInfoVOFetcherV1{}// OrderListVO 实现对应接口,为了突出重点暂时忽略具体实现public class OrderListVOFetcherV1 extends OrderListVOimplements AddressVOFetcherV1,ProductVOFetcherV1,UserVOFetcherV1 {}有了统一的操作接口,接下来便是抽取具体的绑定逻辑,以 UserVOFetcherExecutor 为例:@Componentpublic class UserVOFetcherExecutorV1 {@Autowiredprivate UserRepository userRepository;public void fetch(List<? extends UserVOFetcherV1> fetchers){List<Long> ids = fetchers.stream().map(UserVOFetcherV1::getUserId).distinct().collect(Collectors.toList());List<User> users = userRepository.getByIds(ids);Map<Long, User> userMap = users.stream().collect(toMap(user -> user.getId(), Function.identity()));fetchers.forEach(fetcher -> {Long userId = fetcher.getUserId();User user = userMap.get(userId);if (user != null){UserVO userVO = UserVO.apply(user);fetcher.setUser(userVO);}});}}实现逻辑没有变化,最重要的变化在于“入参类型”,不在是具体的 VO , 而是抽象的 UserVOFetcher 接口 。AddressVOFetcherExecutor、ProductVOFetcherExecutor、PayInfoVOFetcherExecutor 与 UserVOFetcherExecutorV1 逻辑基本一致 , 篇幅问题不在赘述 。
这样一个小小的调整,会给使用方带来什么便利?一起看下使用方的变化:
@Servicepublic class OrderServiceFetcherV1 implements OrderService {@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate AddressVOFetcherExecutorV1 addressVOFetcherExecutorV1;@Autowiredprivate ProductVOFetcherExecutorV1 productVOFetcherExecutorV1;@Autowiredprivate UserVOFetcherExecutorV1 userVOFetcherExecutorV1;@Autowiredprivate PayInfoVOFetcherExecutorV1 payInfoVOFetcherExecutorV1;@Overridepublic List<OrderListVO> getByUserId(Long userId) {List<Order> orders = this.orderRepository.getByUserId(userId);List<OrderListVOFetcherV1> orderDetailVOS = orders.stream().map(order -> new OrderListVOFetcherV1(OrderVO.apply(order))).collect(toList());// 直接使用 FetcherExecutor 完成数据绑定this.addressVOFetcherExecutorV1.fetch(orderDetailVOS);this.productVOFetcherExecutorV1.fetch(orderDetailVOS);this.userVOFetcherExecutorV1.fetch(orderDetailVOS);return orderDetailVOS.stream().collect(toList());}@Overridepublic OrderDetailVO getDetailByOrderId(Long orderId) {Order order = this.orderRepository.getById(orderId);OrderDetailVOFetcherV1 orderDetail = new OrderDetailVOFetcherV1(OrderVO.apply(order));List<OrderDetailVOFetcherV1> orderDetailVOS = Arrays.asList(orderDetail);// 直接使用 FetcherExecutor 完成数据绑定this.addressVOFetcherExecutorV1.fetch(orderDetailVOS);this.productVOFetcherExecutorV1.fetch(orderDetailVOS);this.userVOFetcherExecutorV1.fetch(orderDetailVOS);this.payInfoVOFetcherExecutorV1.fetch(orderDetailVOS);return orderDetail;}}两个方法直接使用 FetcherExecutor 完成数据抓取和绑定,实现了==绑定逻辑的复用== 。如果再有 VO 需要进行数据绑定 , 只需:
推荐阅读
- 能如何看电脑内存,如何查看电脑c盘和d盘内存
- 你知道怎样在 Python 中管理内存吗
- 如何查看手机运行内存大小,如何查看手机软件占用的内存
- 能咋滴看电脑内存,怎么查看自己电脑内存多大
- sd卡和内存卡有什么区别
- 电脑能插3根内存条,电脑能咋滴共存两个窗口
- 苹果手机怎么在电脑上清理内存,苹果手机怎样可以清理内存拉圾
- oppo reno ace能插内存卡吗
- 手机内存满了该怎么处理,手机里照片太多,占内存太多该怎么办?
- 能咋地看电脑内存,如何查看电脑的内存条型号大小
