谁的青春不军训? 教官用了Iterator Pattern都说好

111 阅读3分钟

今天与大家聊聊迭代器模式( Iterator Pattern )。干货来喽,走起!⛽️

01

目的

不需要知道具体的数据存储方式对数据进行不同方式的遍历😏

02
**例子代码**

最近靠答(bai)题(du) 获得了 10.24 元狗物券, 猛地想起抹茶妹妹👧

兄弟告诉我:不努力就不是我兄弟👬。现在只要设计模式学得好, 女朋友就还在初中军训🤭

比如我们有一个军训方队, 每天要点名, 这时候我们实现了如下代码:

先定义一个学生类👩‍🎓:

@Data@AllArgsConstructorpublic class Student {    private String name;    private Boolean sexIsBoy;}

我们需要按照从左到右的顺序进行报名:

public static final void printAllName(        List<List<Student>> students) {    for (List<Student> studentList : students) {        for (Student student : studentList) {            System.out.println(student.getName());        }    }}

我们试验一下:

List<List<Student>> students = new ArrayList<>(16);for (int i = 0; i < 10; i++) {    students.add(Arrays.asList(            new Student("朱坚强" + i, true),            new Student("兰兰" + i, false),            new Student("codog代码狗" + i, true)));}printAllName(students);
03
**问题分析**

这个代码我们认为有二处可能有隐患的地方🤔:

一处是我们存储学生队列的这个结构(list) 可能会改变;

另外一处是我们目前只支持从左到右访问, 如果有新的遍历要求我们就需要更新大量的上层代码。

我们写完了这个代码, 想要回家陪(da)老(fei)婆(ji), 结果走到半路上, 被叫着改了二个需求😠

一个是把 list 改成二维数组方式存储, 另外一个是需要队列中的女生单独报, 男生不报名了, 这时候我们说 printAllName 这个方法用了1万处了, 需要逐个改。

04
**迭代器模式**

先定义一个学生队列类👨‍🎓:

@Data@AllArgsConstructorpublic class StudentLineUp {    private List<List<Student>> students;}

定义一个迭代器:

public class LineUpIterator             implements Iterator<Student> {    private StudentLineUp studentLineUp;    private Integer totalCount;    private Integer currentRow;    private Integer currentCol;    private Integer currentCount;    public LineUpIterator(                StudentLineUp studentLineUp) {        this.studentLineUp = studentLineUp;        this.totalCount = studentLineUp                .getStudents()                .stream().map(list -> list.size())                .reduce(Integer::sum).get();        this.currentRow = 0;        this.currentCol = 0;        this.currentCount = 0;    }    @Override    public boolean hasNext() {        return currentCount                .compareTo(totalCount) < 0;    }    @Override    public Student next() {        if(!hasNext()) {            throw new RuntimeException                        ("没有下一个数据了");        }        if(studentLineUp.getStudents()            .get(currentRow).size() <= currentCol) {            currentRow++;            currentCol = 0;        }        currentCount++;        return studentLineUp.getStudents()                .get(currentRow).get(currentCol++);    }}

实际使用:

List<List<Student>> students = new ArrayList<>(16);for (int i = 0; i < 10; i++) {    students.add(Arrays.asList(            new Student("朱坚强" + i, true),            new Student("兰兰" + i, false),            new Student("codog代码狗" + i, true)));}LineUpIterator lineUpIterator =new LineUpIterator(new StudentLineUp(students));while (lineUpIterator.hasNext()) {    System.out.println(lineUpIterator.next().getName());}
05
**更进一步**

如果我们变化的维度减少一个, 比如说是这个遍历方式基本不变, 但是内部存储方式可能会变, 那我们可以对数据结构采用 Iterable 方式:

@Data@AllArgsConstructorpublic class StudentLineUp implements Iterable<Student> {    private List<List<Student>> students;    @Override    public Iterator<Student> iterator() {        return new LineUpIterator(this);    }}

使用方式:

List<List<Student>> students = new ArrayList<>(15);for (int i = 0; i < 10; i++) {    students.add(Arrays.asList(            new Student("朱坚强" + i, true),            new Student("兰兰" + i, false),            new Student("codog代码狗" + i, true)));}for (Student student : new StudentLineUp(students)) {    System.out.println(student.getName());}

类图👇:

06
迭代器模式课后作业

1. 把 Iterable 模式变成自定义 Java 8 Stream 模式

2. 作为银行开发人员, 这次我们接到要将一批用户的流水导出到 Excel, 导出 Excel 的时候, 数据量可能很大, 不能够因为用户数据量大就导致内存溢出, 所以把全部用户放到 List 里面传给 exportExcel(List<Object> objs) 方法不是很合理, 需要传给 exportExcel(Iterable<Object> objs)[此方法实现不需要关心], 请自定义 Iterable 从数据库分页查询数据导出 excel 功能

3. 我们需要从远程接口查询用户的还款计划来做汇总, 还款计划是个分页 http 接口, 封装一个迭代器使得调用者不知道数据来源是 http 接口还是本地数据库

4. 陈述迭代器模式的缺点

微信: