DDD死党:内存Join——将复用和扩展用到极致( 四 )


  1. VO 实现 XXXFetcher 接口,实现对应方法,提供关联数据并完成数据绑定
  2. 使用 XXXFetcherExecutor 完成数据绑定
至此 , 面对新业务基本上与“绑定逻辑”说再见了 。
4.2. 重构绑定逻辑接下来让我们一起聚焦于绑定逻辑,先对比下上述的UserVOFetcherExecutor 与下面的 AddressVOFetcherExecutor,找到里面的变化与不变:
@Componentpublic class AddressVOFetcherExecutorV1 {@Autowiredprivate AddressRepository addressRepository;public void fetch(List<? extends AddressVOFetcherV1> fetchers){// 获取关联信息List<Long> ids = fetchers.stream().map(AddressVOFetcherV1::getAddressId).distinct().collect(Collectors.toList());// 查询关联数据List<Address> addresses = addressRepository.getByIds(ids);// 转为为 MapMap<Long, Address> addressMap = addresses.stream().collect(toMap(address -> address.getId(), Function.identity()));// 依次进行数据绑定fetchers.forEach(fetcher -> {Long addressId = fetcher.getAddressId();Address address = addressMap.get(addressId);if (address != null){// 转换为 VOAddressVO addressVO = AddressVO.apply(address);// 将数据写回到结果fetcher.setAddress(addressVO);}});}}仔细观察,会发现:
【不变】逻辑骨架基本一致,基本是由:
  1. 获取关联信息
  2. 查询关联数据
  3. 将其转换为 Map
  4. 讲数据转化为 VO
  5. 将 VO 绑定到结果对象
【变化】实现细节存在差异;
  1. 从什么接口中获取关联信息
  2. 如何查询关联数据
  3. 转换为 Map 的键是什么
  4. 如何将数据转换为 VO
  5. 如何完成数据的绑定
熟悉设计模式的伙伴是否眼前一亮?停顿一下好好回想一下,哪种模式就是用来处理这种问题的?
答案便是:模板方法模式
整体思想为:
  1. 将不变的逻辑骨架封装在父类方法
  2. 将变化的实现细节放在子类中进行扩展
整体设计如下:
DDD死党:内存Join——将复用和扩展用到极致

文章插图
抽取公共父类如下:
abstract class BaseItemFetcherExecutor<FETCHER extends ItemFetcher, DATA, RESULT>implements ItemFetcherExecutor<FETCHER>{@Overridepublic void fetch(List<FETCHER> fetchers) {// 获取关联信息List<Long> ids = fetchers.stream().map(this::getFetchId).distinct().collect(Collectors.toList());// 查询关联数据List<DATA> datas = loadData(ids);// 转为为 MapMap<Long, List<DATA>> dataMap = datas.stream().collect(groupingBy(this::getDataId));// 依次进行数据绑定fetchers.forEach(fetcher -> {Long id = getFetchId(fetcher);List<DATA> ds = dataMap.get(id);if (ds != null){// 转换为 VOList<RESULT> result = ds.stream().map( data -> convertToVo(data)).collect(Collectors.toList());// 将数据写回到结果setResult(fetcher, result);}});}protected abstract Long getFetchId(FETCHER fetcher);protected abstract List<DATA> loadData(List<Long> ids);protected abstract Long getDataId(DATA data);protected abstract RESULT convertToVo(DATA data);protected abstract void setResult(FETCHER fetcher, List<RESULT> result);}基于 BaseItemFetcherExecutor 的 UserFetcherExecutor 如下:
@Componentpublic class UserVOFetcherExecutorV2extends BaseItemFetcherExecutor<UserVOFetcherV2, User, UserVO>{@Autowiredprivate UserRepository userRepository;@Overrideprotected Long getFetchId(UserVOFetcherV2 fetcher) {return fetcher.getUserId();}@Overrideprotected List<User> loadData(List<Long> ids) {return this.userRepository.getByIds(ids);}@Overrideprotected Long getDataId(User user) {return user.getId();}@Overrideprotected UserVO convertToVo(User user) {return UserVO.apply(user);}@Overrideprotected void setResult(UserVOFetcherV2 fetcher, List<UserVO> userVO) {if (CollectionUtils.isNotEmpty(userVO)) {fetcher.setUser(userVO.get(0));}}@Overridepublic boolean support(Class<UserVOFetcherV2> cls) {// 暂时忽略,稍后会细讲return UserVOFetcherV2.class.isAssignableFrom(cls);}}UserVOFetcherExecutor究竟发生什么变化呢?好像变得更复杂了:
  1. 从代码量角度(行数)变得更多了 , 因为类函数明显变大


    推荐阅读