一、场景问题
在激烈的市场竞争中,为了提高自己的硬实力,A公司和B公司决定合并。现在目前二者的员工信息都存放在不同的数据库中,且存储的方式不同,请设计出统一的读取所有员工(A、B公司之和)信息的接口。
为了简化问题,假定
- A公司使用List集合存储员工信息
- B公司使用数组存储员工信息
二、传统解决方案
最直观的做法是分别查询出来二者的员工数据,采用其中一种格式转化为另一种格式的方式来遍历读取。这种方式处理起来最简单,也最容易理解,但是没有考虑以后的扩展性。如果新增一种采用另外的数据结构存储用户信息,需要对应修改遍历的代码,违背开闭原则。且这种方式需要解析各个存储方式的细节。因此迭代器模式应运而生。
三、模式剖析
1、模式定义
迭代器模式(Iterator Pattern):提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
2、模式结构
迭代器模式一般包含如下角色
-
Aggregate
:抽象聚合角色它用于存储和管理元素对象,声明一个
createIterator
()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。interface Aggregate { Iterator createIterator(); }
-
Concrete Aggregate
:具体聚合角色它实现了在抽象聚合类中声明的
createIterator
()方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator
实例。class ConcreteMediator extends Mediator { //实现业务方法,封装同事之间的调用 public void operation() { ...... ((Colleague)(colleagues.get(0))).method1(); //通过中介者调用同事类的方法 ...... } }
-
Iterator
:抽象迭代器角色它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法
interface Iterator { public void first(); //将游标指向第一个元素 public void next(); //将游标指向下一个元素 public boolean hasNext(); //判断是否存在下一个元素 public Object currentItem(); //获取游标指向的当前元素 }
-
Concrete Iterator
:具体迭代器角色它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时,游标通常是一个表示位置的非负整数
class ConcreteIterator implements Iterator { private ConcreteAggregate objects; //维持一个对具体聚合对象的引用,以便于访问存储在聚合对象中的数据 private int cursor; //定义一个游标,用于记录当前访问位置 public ConcreteIterator(ConcreteAggregate objects) { this.objects=objects; } public void first() { ...... } public void next() { ...... } public boolean hasNext() { ...... } public Object currentItem() { ...... } }
3、模式结构图
4、使用迭代器模式改进案例
我们可以根据不同的数据结构定义不同的迭代器实现。例如上面的案例是采用List和数组来存储,我们可以定义出List和数组的迭代器实现来遍历数组。
4.1、代码展示
-
抽象迭代器接口
public interface Iterator { /** * 是否存在下一索引值 * @return boolean **/ boolean hasNext(); /** * 游标移动,并返回遍历到的元素 * @return Employee **/ Employee next(); }
-
具体迭代器类
-
List迭代器类
public class ListIterator implements Iterator { /** * list集合 */ private List<Employee> employeeList; /** * 索引位置 */ private int index = -1; public ListIterator(List<Employee> employeeList) { this.employeeList = employeeList; } @Override public boolean hasNext() { return index < employeeList.size() - 1; } @Override public Employee next() { return employeeList.get(++index); } }
-
数组迭代器类
public class ArrayIterator implements Iterator { /** * 数组集合 */ private Employee[] employees; /** * 索引位置 */ private int index = -1; public ArrayIterator(Employee[] employees) { this.employees = employees; } @Override public boolean hasNext() { return index < employees.length - 1; } @Override public Employee next() { return employees[++index]; } }
-
-
员工基础信息
public class Employee { /** * 员工编号 */ private String id; /** * 员工姓名 */ private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Employee(String id, String name) { this.id = id; this.name = name; } @Override public String toString() { final StringBuilder sb = new StringBuilder("员工编号:"); sb.append(id); sb.append(", 员工姓名:").append(name); return sb.toString(); } }
-
抽象聚合接口
public interface AggregateGroup { /** * 创建迭代器 * @return **/ Iterator createIterator(); }
-
具体聚合类
-
公司A
public class CompanyA implements AggregateGroup { /** * 公司员工集合 */ private List<Employee> employees = new ArrayList<>(); //模拟数据库数据 { employees.add(new Employee("A001", "张三")); employees.add(new Employee("A002", "李四")); employees.add(new Employee("A003", "王五")); } @Override public Iterator createIterator() { //返回list迭代器 return new ListIterator(employees); } }
-
公司B
public class CompanyB implements AggregateGroup { /** * 公司员工集合 */ private Employee[] employees = new Employee[5]; //模拟数据库数据 { employees[0] = new Employee("B001", "王小二"); employees[1] = new Employee("B002", "张伟"); employees[2] = new Employee("B003", "李大嘴"); employees[3] = new Employee("B004", "上官皓"); employees[4] = new Employee("B005", "欧阳峰"); } @Override public Iterator createIterator() { //返回数组迭代器 return new ArrayIterator(employees); } }
-
-
聚合团体管理类
public class AggregateGroupManager { /** * 聚合团体集合 */ private List<AggregateGroup> groups = new ArrayList<>(); /** * 添加团体 * @param aggregateGroup 1 * @return void **/ public void addGroup(AggregateGroup aggregateGroup) { groups.add(aggregateGroup); } /** * 遍历员工信息(可以统一使用迭代器来进行遍历) * @return void **/ public void traverseEmployees() { for (AggregateGroup group : groups) { //获取集合的迭代器 Iterator iterator = group.createIterator(); //遍历集合中的元素 while (iterator.hasNext()) { System.out.println(iterator.next()); } } } }
4.2、客户端测试
public static void main(String[] args) {
//公司A和公司B
AggregateGroup companyA = new CompanyA();
AggregateGroup companyB = new CompanyB();
//定义团体管理类
AggregateGroupManager aggregateGroupManager = new AggregateGroupManager();
aggregateGroupManager.addGroup(companyA);
aggregateGroupManager.addGroup(companyB);
//遍历员工信息
aggregateGroupManager.traverseEmployees();
}
输出
员工编号:A001, 员工姓名:张三
员工编号:A002, 员工姓名:李四
员工编号:A003, 员工姓名:王五
员工编号:B001, 员工姓名:王小二
员工编号:B002, 员工姓名:张伟
员工编号:B003, 员工姓名:李大嘴
员工编号:B004, 员工姓名:上官皓
员工编号:B005, 员工姓名:欧阳峰
5、优缺点
- 优点
- 它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。
- 迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。
- 在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足“开闭原则”的要求
- 缺点
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
- 抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如
JDK
内置迭代器Iterator
就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator
等来实现,而ListIterator
迭代器无法用于操作Set
类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。
6、使用场景
- 访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。
- 需要为一个聚合对象提供多种遍历方式。
- 为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口。