原代码
    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就是开发时设计的了,不然写出来的代码我自己看着都头疼。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注