设计模式第9式:迭代器模式

前言

我们有很多种方法可以把对象集中到一个集合中,比如列表、堆栈、散列表中。每种集合都有自己的特点和使用时机,但都有一个共同的需求:用户想要遍历这些对象。同时我们并不想用户看到集合的实现,本文将讲解如何让用户遍历对象又无法窥视存储对象的方式。

正文

1、先来看一个实际案例

有2个餐厅,它们分别用列表和数组来保存各自的菜品:

public class BreakfastMenu {
	ArrayList breakfastItems;
	...
	ArrayList getBreakfastItems() {
		return breakfastItems; // 这里会暴露类内部的数据结构
	}
}
public class DinerMenu {
	MenuItem[] dinerItems;
	...

	MenuItem[] getDinerItems() {
		return dinerItems; // 这里会暴露类内部的数据结构
	}
}

我们想同时打印这2份菜单中的菜品,会变得复杂。我们需要首先拿到2个菜单类的变量,然后再用不同的方式遍历这2个变量。如果有第3份菜单,就需要三个循环来遍历。

for (int i = 0; i < breakfastItems.size(); i++) {
	MenuItem menuItem = breakfastItems.get(i);
}
for (int i = 0; i < dinerMenu.length; i++) {
	MenuItem menuItem = dinerMenu[i];
}

2、如何优化

当然有优化方法了,设计模式最重要的思想就是封装变化的部分。很明显,上面发生的变化是:由不同的集合类型所造成的遍历。我们来看看怎么封装:我们创建一个对象,称为“迭代器”(Iterator),用它来封装“遍历集合每个对象的过程”。

我们先在ArrayList上试试,我们先从菜单对象breakfastMenu获得一个菜单迭代器,用户只需要调用迭代器的hasNext()和next()方法。

Iterator iterator = breakfastMenu.createIterator();
while (iterator.hasNext()) {
	MenuItem menuItem = iterator.next();
}

再在数组上试试:

Iterator iterator = dinerMenu.createIterator();
while (iterator.hasNext()) {
	MenuItem menuItem = iterator.next(); // 和上面的遍历一模一样
}

3、迭代器模式

看来我们的优化奏效了,这就是大名鼎鼎的迭代器模式。它依赖一个叫做迭代器的接口。下面是一个最基本的迭代器接口。

interface Iterator {
	boolean hasNext(); // 集合中是否还有更多元素
	Object next(); // 返回下一个对象
}

有了这个接口,我们可以为各种集合创建迭代器。

public class DinerMenuIterator implements Iterator {
	MenuItem[] items; // 作为数组的迭代器,需要持有数组类型

	public boolean hasNext() {
		// 判断items中还有没有元素
	}
	
	public Object next() {
		// 判断items中下一个元素
	}
}

有了数组迭代器,我们就可以改写菜单类了。

public class DinerMenu {
	MenuItem[] dinerItems;
	...
	// 现在就不用返回实例对象了,避免了暴露类的内部结构
	// MenuItem[] getDinerItems() {
	//	 return dinerItems;
	// }
	
	// 现在只需要给用户返回一个迭代器对象
	public Iterator createIterator() {
		return new DinerMenuIterator(dinerItems); 
	}
}

现在我们来正式定义一下迭代器模式

迭代器提供一种顺序访问集合元素的方法,而又不暴露其内部结构。

这样做很有意义,迭代器模式把遍历元素的职责交给迭代器,而不是集合对象本身。这不仅让集合类的实现变得简洁,也可以让集合更专注于它应该关注的事情上(也就是管理对象元素)。下面是迭代器模式的类图:
在这里插入图片描述

4、单一职责

设计原则:一个类应该只有一个引起变化的原因

“内聚”这个术语用来度量一个类或模块达到单一目的或责任。当一个类或模块被设计成只支持一组相关的功能时,我们说它“高内聚”,更容易维护。

5、JDK中的迭代器模式

JDK中已经给我们提供了迭代器接口,和上面的非常类似,用法也基本一致。各种集合都通过迭代器完成遍历,我们来看下ArrayList是怎么实现的。

ArrayList中的iterator()方法返回一个迭代器对象。

public Iterator<E> iterator() {
    return new Itr();
}

Itr类是ArrayList中的内部类,大多数集合都是用内部类来实现迭代器的。

private class Itr implements Iterator<E> {
	...
	
	public boolean hasNext() {
	    ...
	}
	
	public E next() {
	    ...
	}
}

总结

迭代器模式很好,一方面提供了统一简洁的集合遍历方式,另一方面避免暴露集合内部结构。

集合遍历方式也在持续升级,后面JDK1.5又推出了“foreach”语法糖。JDK1.8又推出了forEach()方法继续简化遍历形式,请参考文章:Java8新特性:集合遍历forEach方法
,我们甚至不用请求迭代器了。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值