迭代器模式
原代码
private <T, RR> List<T> getExportVoList(Map<String, Object> params, HttpServletRequest request, Callback<RR, T> callback, ServiceTypeEnum serviceTypeEnum) {
// 分页循环查询订单
int page = 1;
int size = 1000; //一次查1000数据处理
Integer totalPage = null;
// 分页参数
Map<String, Object> paramsPage;
if (ServiceTypeEnum.GOOGLE_ORDER.equals(serviceTypeEnum)){
paramsPage = params;
} else {
paramsPage = new HashMap<>();
params.put("params", paramsPage);
}
paramsPage.put(Constant.LIMIT, size);
List<T> dataList = new ArrayList<>();
while (totalPage == null || page <= totalPage) {
paramsPage.put(Constant.PAGE, page);
R orderPage = getOrderPage(params, request);
// 数据解析
Object o = orderPage.get(R.DATA_KEY);
if (o instanceof PageUtils) {
PageUtils<RR> data = (PageUtils<RR>) o;
if (totalPage == null){
totalPage = data.getTotalPage();
}
page++;
data.getList().forEach(orderInfoVo -> {
// 处理数据
dataList.add(callback.call(orderInfoVo));
});
} else {
log.warn("查询导出数据结果异常,中断数据处理,R: {}", JSON.toJSONString(orderPage));
throw new RuntimeException("查询导出数据结果异常,中断数据处理");
// break;
}
}
return dataList;
}
调整后的代码
private <T, RR> List<T> getExportVoList(Map<String, Object> params, HttpServletRequest request, Callback<RR, T> callback, ServiceTypeEnum serviceTypeEnum) {
// 分页参数
Map<String, Object> paramsPage;
if (ServiceTypeEnum.GOOGLE_ORDER.equals(serviceTypeEnum)){
paramsPage = params;
} else {
paramsPage = new HashMap<>();
params.put("params", paramsPage);
}
List<T> dataList = new ArrayList<>();
ExportIterator<RR> iterator = new ExportIterator<>(params, paramsPage, request);
while (iterator.hasNext()) {
iterator.next().forEach(orderInfoVo -> {
// 处理数据
dataList.add(callback.call(orderInfoVo));
});
}
return dataList;
}
/**
* 订单导出迭代器
*/
public class ExportIterator<RR> {
int page = 1;
int size = 1000; //一次查1000数据处理
Integer totalPage = null;
//查询参数
Map<String, Object> params;
// 分页参数
Map<String, Object> paramsPage;
HttpServletRequest request;
public ExportIterator(Map<String, Object> params, Map<String, Object> paramsPage, HttpServletRequest request) {
this.params = params;
this.request = request;
this.paramsPage = paramsPage;
paramsPage.put(Constant.LIMIT, size);
}
public boolean hasNext() {
if (totalPage == null) {
return true;
}
return page <= totalPage;
}
public List<RR> next() {
paramsPage.put(Constant.PAGE, page);
R orderPage = getOrderPage(params, request);
// 数据解析
Object o = orderPage.get(R.DATA_KEY);
if (o instanceof PageUtils) {
PageUtils<RR> data = (PageUtils<RR>) o;
if (totalPage == null){
totalPage = data.getTotalPage();
}
page++;
return data.getList();
} else {
log.warn("查询导出数据结果异常,中断数据处理,R: {}", JSON.toJSONString(orderPage));
throw new RuntimeException("查询导出数据结果异常,中断数据处理");
// break;
}
}
}
调整后其实业务代码没什么变化,主要是让主流程看起来简洁,可读性强了非常多,隐藏了阅读者不太需要关注的迭代细节。
之前还写过一个业务更复杂的迭代器,业务需要从两个集合(数据好像位于数据库中)中获取数据,两个集合之间有些关系,不用迭代器的话代码复杂度非常高,做代码审查时很头疼,粗一看大概知道这代码是干嘛的,但是你需要确认是不是你想的那样,只能顺着代码去看。迭代器实现用了嵌套迭代器,不仅封装了代码,让主代码看起来简洁逻辑清楚,而且迭代器的调用方式也简化了封装前的复杂逻辑控制,是的,类对象之间的调用有时会简化代码逻辑。
我找找代码。
下面是主代码:
public CheckResponse check(Task task) {
super.check(task);
TRSDailyBatchDTO dto = task.getTaskBody();
// 检查时间段冲突
List<TRSDailyCheckVO> dailyCheckVOList = CollUtil.newArrayList();
DataQueryHandler.RangeIterator queryHandler = DataQueryHandler.RangeIteratorFactory.create(support, dto.getScenicId(), dto.getDayRangeList());
// 每次查询100
int limit = 100;
while (queryHandler.hasNext()) {
compareAndGenCheckVO(dto, dailyCheckVOList, queryHandler.next(limit));
// 有检查出异常数据停止继续
if (CollUtil.isNotEmpty(dailyCheckVOList)) {
break;
}
}
if (CollUtil.isEmpty(dailyCheckVOList)) {
return CheckResponse.pass();
}
return CheckResponse.noPass(dailyCheckVOList);
}
这段代码的主要目的是,检查指定多个时间段的数据是否存在于数据库,这需要把数据库中的数据查出后与传入数据做时间冲突比对,涉及到日期与日期范围的比对等,业务复杂度很高,数据量不定,也不好从数据库一次性查出来,而且多个时间段的数据也需要多个sql去查询,这就导致整体逻辑很杂乱,迭代器封装了查询数据的细节,而且分化了查询过程,使数据一点点从数据库取出。下面是封装代码
DataQueryHandler
package com.scnyw.trip.reservation.core.task;
import com.scnyw.trip.reservation.dto.TRSDailyBatchDTO;
import com.scnyw.trip.reservation.entity.TripTimesharingReservationDailyEntity;
import lombok.Data;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* <p>描述类的功能</p>
*
* @author zhuangjiali
* @date 2025/4/8
*/
@Data
public class DataQueryHandler {
/**
* 接口:通用分页迭代器
* @param <T>
*/
public interface RangeIterator<T> {
/**
* 查询数据
* @param limit 指定本次查询的数量
* @return 默认每次返回最多 100 条数据
*/
List<T> next(int limit);
/**
* 是否还有数据
* @return
*/
boolean hasNext();
}
/**
* 单个日期范围的分页迭代器
*/
public static class DateRangeIterator implements RangeIterator<TripTimesharingReservationDailyEntity> {
private final IDataSupport support;
private final String scenicId;
private final LocalDate beginDate;
private final LocalDate endDate;
private final List<Integer> weekdays;
private int offset = 0;
private boolean finished = false;
public DateRangeIterator(IDataSupport support, String scenicId, LocalDate beginDate, LocalDate endDate, List<Integer> weekdays) {
this.support = support;
this.scenicId = scenicId;
this.beginDate = beginDate;
this.endDate = endDate;
this.weekdays = weekdays;
}
@Override
public List<TripTimesharingReservationDailyEntity> next(int limit) {
if (finished) {
return List.of();
}
List<TripTimesharingReservationDailyEntity> list = support.findByDayRange(scenicId, beginDate, endDate, weekdays, offset, offset + limit);
offset += list.size();
if (list.size() < limit) {
finished = true;
}
return list;
}
@Override
public boolean hasNext() {
return !finished;
}
}
/**
* 多个日期范围组合成分页迭代器
*/
public static class CompositeRangeIterator implements RangeIterator<TripTimesharingReservationDailyEntity> {
private final List<RangeIterator<TripTimesharingReservationDailyEntity>> iterators;
private int currentIndex = 0;
public CompositeRangeIterator(List<RangeIterator<TripTimesharingReservationDailyEntity>> iterators) {
this.iterators = iterators;
}
@Override
public List<TripTimesharingReservationDailyEntity> next(int limit) {
List<TripTimesharingReservationDailyEntity> result = new ArrayList<>();
while (currentIndex < iterators.size() && result.size() < limit) {
RangeIterator<TripTimesharingReservationDailyEntity> current = iterators.get(currentIndex);
List<TripTimesharingReservationDailyEntity> part = current.next(limit - result.size());
result.addAll(part);
if (!current.hasNext()) {
currentIndex++;
}
}
return result;
}
@Override
public boolean hasNext() {
return currentIndex < iterators.size();
}
}
/**
* 工厂类:从 DTO 构建组合迭代器
*/
public static class RangeIteratorFactory {
public static CompositeRangeIterator create(
IDataSupport support,
String scenicId,
List<TRSDailyBatchDTO.DayRange> ranges
) {
List<RangeIterator<TripTimesharingReservationDailyEntity>> iterators = ranges.stream().map(r -> {
List<Integer> weekdays = r.getWeekdaysList();
return new DateRangeIterator(support, scenicId, r.getBeginDate(), r.getEndDate(), weekdays);
}).collect(Collectors.toList());
return new CompositeRangeIterator(iterators);
}
}
}
类没写功能描述[doge],一般来说有功能描述,再扫一下内容结构,看一下其中的关键参数,就能基本了解其功能,相比按行阅读代码直观的多。类中声明了四个子类,其中一个是接口,另外两个是组合迭代器与日期迭代器,还有一个是迭代器工厂,根据参数生成组合迭代器返回,组合迭代器中包含多个子日期迭代器,它会控制数据的获取量与来源,保证每次定量输出数据,直到所有子日期迭代器无数据为止。 现在看到这种类似的代码就会想封装一下,不然看着难受(当前前提了有时间),本来是因为导出表有感觉比较大,所以做了一下分页查询并调理导出数据,原表字段有点多,小小的压缩成结果数据再统一导出比较好,后面改bug时间比较多就优化成迭代器了。当然DataQueryHandler就是开发时设计的了,不然写出来的代码我自己看着都头疼。