《Android源码设计模式解析与实战》读书笔记(十四)——迭代器模式

来源:互联网 发布:dns优化器 安卓 编辑:程序博客网 时间:2024/05/16 10:22

迭代器模式又称游标(cursor,看到这个单词有没有很熟悉)模式,主要用于对容器的访问,比如 List 、 Map 、数组等,因为访问容器一般会用到遍历算法。如果将遍历方法封装在容器类中,容器类就会承担过多功能,而且由于遍历状态的存储问题,还不能对同一个容器同时进行多个遍历操作,但如果让使用者自己实现遍历方法,又会暴露容器内部实现,因此,迭代器模式应运而生,在客户访问类和容器类之间插入一个第三者——迭代器。


第十四章 解决问题的“第三者”——迭代器模式

1.定义

提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部实现。


2.使用场景

1).需要遍历一个容器对象时。


3.简单实现

我们假设一个这样的场景,某一天人事部想统计一下各个部门的人员情况,然后让每个部门自己先将人员信息收集起来。

定义一个员工实体类:

public class Employee {    private String name;    private int age;    private String sex;    private String position;    public Employee(String name, int age, String sex, String position) {        this.name = name;        this.age = age;        this.sex = sex;        this.position = position;    }    @Override    public String toString() {        return "Employee{" +                "name='" + name + '\'' +                ", age=" + age +                ", sex='" + sex + '\'' +                ", position='" + position + '\'' +                '}';    }}


技术部门:

public class TechnicalDepartment {    private List<Employee> employeeList = new ArrayList<>();    public TechnicalDepartment() {        employeeList.add(new Employee("小赵", 21, "男", "工程师"));        employeeList.add(new Employee("小钱", 22, "男", "工程师"));        employeeList.add(new Employee("小孙", 23, "男", "工程师"));        employeeList.add(new Employee("小李", 24, "男", "项目经理"));    }    public List<Employee> getEmployeeList() {        return employeeList;    }}


测试部门:

public class TestingDepartment {    private Employee[] employees = new Employee[2];    public TestingDepartment() {        employees[0] = new Employee("小周", 25, "女", "测试人员");        employees[1] = new Employee("小吴", 26, "女", "测试人员");    }    public Employee[] getEmployees() {        return employees;    }}


先只统计这两个部门的信息,然后在人事部门开始查看每一个人员情况:

        TechnicalDepartment mTechnicalDepartment = new TechnicalDepartment();        for (int i = 0; i < mTechnicalDepartment.getEmployeeList().size(); i++) {            System.out.println(mTechnicalDepartment.getEmployeeList().get(i).toString());        }        TestingDepartment mTestingDepartment = new TestingDepartment();        for (int i = 0; i < mTestingDepartment.getEmployees().length; i++) {            System.out.println(mTestingDepartment.getEmployees()[i]);        }


可以看到打印如下:



也可以看到每个人员的详细信息,但是问题是,技术部门统计时用的容器是 List ,而测试部门用的是数组,所以人事部门在查看信息的时候,用了两种不同的遍历逻辑。那加入其他部门用的又是另外的容器呢,还得增加不同的遍历逻辑。

所以我们可以增加一个迭代器接口:

public interface Iterator {    boolean hasNext();    Employee next();}


然后每个部门都创建一个迭代器来实现遍历:

public class TechnicalDepartmentIterator implements Iterator {    private List<Employee> employeeList = new ArrayList<>();    private int index = 0;    public TechnicalDepartmentIterator(List<Employee> employeeList) {        this.employeeList = employeeList;    }    @Override    public boolean hasNext() {        //当下标大于 List 的 size 时,或者当前下标的对象为空,则返回false,停止遍历        return !(index > employeeList.size() - 1 || employeeList.get(index) == null);    }    @Override    public Employee next() {        Employee mEmployee = employeeList.get(index);        index++;        return mEmployee;    }}

public class TestingDepartmentIterator implements Iterator {    private Employee[] employees = new Employee[2];    private int index = 0;    public TestingDepartmentIterator(Employee[] employees) {        this.employees = employees;    }    @Override    public boolean hasNext() {        //当下标大于 Array 的 length 时,或者当前下标的对象为空,则返回false,停止遍历        return !(index > employees.length - 1 || employees[index] == null);    }    @Override    public Employee next() {        Employee mEmployee = employees[index];        index++;        return mEmployee;    }}


每个部门需要统一有个返回迭代器的接口:

public interface Department {    Iterator iterator();}


然后将每个部门修改一下:

public class TechnicalDepartment implements Department {    private List<Employee> employeeList = new ArrayList<>();    public TechnicalDepartment() {        employeeList.add(new Employee("小赵", 21, "男", "工程师"));        employeeList.add(new Employee("小钱", 22, "男", "工程师"));        employeeList.add(new Employee("小孙", 23, "男", "工程师"));        employeeList.add(new Employee("小李", 24, "男", "项目经理"));    }    @Override    public Iterator iterator() {        return new TechnicalDepartmentIterator(employeeList);    }}
public class TestingDepartment implements Department {    private Employee[] employees = new Employee[2];    public TestingDepartment() {        employees[0] = new Employee("小周", 25, "女", "测试人员");        employees[1] = new Employee("小吴", 26, "女", "测试人员");    }    @Override    public Iterator iterator() {        return new TestingDepartmentIterator(employees);    }}

于是技术部门就可以这样查看了:

        TechnicalDepartment mTechnicalDepartment = new TechnicalDepartment();        Iterator mIterator = mTechnicalDepartment.iterator();        while (mIterator.hasNext()) {            System.out.println(mIterator.next());        }        TestingDepartment mTestingDepartment = new TestingDepartment();        mIterator = mTestingDepartment.iterator();        while (mIterator.hasNext()) {            System.out.println(mIterator.next());        }


而且由于迭代器和各个部门都是依赖于抽象,所以遍历的逻辑代码都一样,所以可以抽取成一个方法变成这样:

        TechnicalDepartment mTechnicalDepartment = new TechnicalDepartment();        Iterator mIterator = mTechnicalDepartment.iterator();        ergodic(mIterator);        TestingDepartment mTestingDepartment = new TestingDepartment();        mIterator = mTestingDepartment.iterator();        ergodic(mIterator);

    private void ergodic(Iterator mIterator) {        while (mIterator.hasNext()) {            System.out.println(mIterator.next());        }    }

重新运行可以看到打印输出是一样的。

这样的话,就算增加多少不同的容器都会有相同的遍历逻辑,对于客户端来说也就更简单了。


4.总结

迭代器的规定不像其他模式那么严格,实现也因人而异,而且大部分高级语言的容器类都提供了相应的迭代器供开发者使用,而不需要我们自己去实现。拿 Android 来说,除了各种数据体结构(如 List 、 Map 等),自身源码很多也提供了迭代器,比如数据库查询使用的 Cursor ,Cursor 对象其实就是一个迭代器,可以利用它遍历数据库,还有内容提供者的 Cursor 等等。迭代器模式发展至今,几乎每种高级语言都有内置实现,开发者已经很少会自己去实现迭代器了,所以对这个模式也是重在了解原理而非使用。

优点:

1).将多种容器的遍历方式形成统一,弱化了容器类和遍历算法之间的关系。

缺点:

1).类文件的增加。

Demo下载

阅读全文
0 0