Iterator(迭代器)模式
前言
一直以来,我对设计模式总是模糊不清,感觉用到的地方并不多,直到正式工作后遇到了真正的难题,才发现设计模式是多么的重要。首先遇到的第一个问题是PM在开发过程中经常变更需求,这对于我们开发者来说是个难题,因为需求变了,对应的功能就要随之变更,如果需求变的太多,整个系统(软件)重构都有可能,这种碰到需求变更就要修改代码的情况真让人头疼。另外,leader让我研究一个开源项目(openTCS),其中简单的功能我大体可以明白,整个项目运行流程也很清晰,但是,唯一困难的是我对代码的设计原理搞不懂,我只知道其中有抽象类、接口,子类,实现类等等,但是它们有什么作用我不明白。当然还有很多难题,比如项目更新了,怎么让新旧版本都可用,怎么重构项目,重构理由是什么等等。所以,最终我下定决心好好学习设计模式,虽然有点亡羊补牢的感觉,但我认为这还不晚,而且这种认为自己能力不够,要通过学习来进步的态度一定要坚持下去。
Iterator模式
Iterator模式是用于在数据集合中按顺序遍历集合,它的功能和for循环一样,for循环使用方法如下代码中所示:
int arr[] = {1, 3, 4, 10};
for (int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
上面代码中的循环变量i代表数据集合中每个元素的排序位置,在数组中是从0开始,然后会递增为1,2,3,……,在大括号里对每个遍历的元素进行操作。这种每次使用都要声明一个循环变量,然后按照规则进行遍历的处理,好处是遍历规则一目了然,坏处是由于循环变量的存在,因为它代表每个元素在集合中的位置,每次获取集合中的元素都要用到这个循环变量,然而不同类型的集合,它获取元素的方式不一样,所以修改的地方很多。比如将上面代码中数组改成List类型:
List arr = new ArrayList();
arr.add(1);
arr.add(3);
arr.add(4);
arr.add(10);
for (int i = 0; i < arr.size(); i++){
System.out.println(arr.get(i));
}
由此可见,如果有很多的for循环使用了某个集合,当要修改这个集合的类型的时候,每一个for循环都要改变,这是很麻烦的事情。Iterator模式就是为了解决这个问题而产生的,它将遍历和实现分开了,也就是上述循环变量i的定义和使用那段代码重新封装成了一个方法,而用的时候直接调用即可,至于循环变量的递增都放在了封装的方法中,这样的好处是真正对集合进行遍历的地方就一个,也就是在封装的方法中,所以后期如果修改了集合的类型,只需要在封装方法中修改下代码即可,所以业务代码中用到的Iterator遍历方法都不要改动。
下面是示例程序,需求是用Iterator模式对书架中的书进行遍历,并打印书的名称。
示例程序
1、Book类
package com.design.pattern1;
public class Book {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Book() {
}
public Book(String name) {
this.name = name;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
'}';
}
}
2、Iterator接口(Interface)
主要定义了循环遍历的方法。
package com.design.pattern1;
public interface Iterator {
boolean hasNext();
Object next();
}
3、Aggregate接口(Interface)
代表所有使用Iterator遍历的集合,不同的集合类或对象通过实现该接口的iterator方法,生成一个用于遍历集合的迭代器。
package com.design.pattern1;
public interface Aggregate {
Iterator iterator();
}
4、BookShelf类(书架)
具体的集合类型,通过实现Aggregate接口生成一个迭代器(BookShelfIterator),并且将本书架类作为参数出传入BookShelfIterator类的构造方法中,实现遍历方法的个性化处理。
package com.design.pattern1;
public class BookShelf implements Aggregate{
private Book[] books;
private int last = 0;
public BookShelf(int max){
this.books = new Book[max];
}
public Book getBookAt(int index){
return books[index];
}
public void appendBook(Book book){
this.books[last] = book;
last ++;
}
public int getLength(){
return last;
}
@Override
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
5、BookShelfIterator类(实现Iterator接口)
BookShelf(书架)的迭代器实现类,该类实现了Iterator接口的遍历方法,并且通过构造方法中BookShelf参数传入书架的数据。
package com.design.pattern1;
public class BookShelfIterator implements Iterator {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
if (index < bookShelf.getLength()){
return true;
}else {
return false;
}
}
@Override
public Object next() {
Book book = bookShelf.getBookAt(index);
index ++;
return book;
}
}
6、Main类
package com.design.pattern1;
public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(3);
bookShelf.appendBook(new Book("论语"));
bookShelf.appendBook(new Book("史记"));
bookShelf.appendBook(new Book("三国演义"));
Iterator iterator = bookShelf.iterator();
while (iterator.hasNext()){
Book book = (Book) iterator.next();
System.out.println(book.getName());
}
}
}
Iterator模式包含四个元素:
1、Iterator接口(迭代器)
主要负责定义按顺序遍历元素的方法。
2、ConcreteIteartor(具体的迭代器)
主要负责实现Iterator所定义的方法。
3、Aggregate接口(集合)
主要负责定义创建Iterator的方法。
4、ConcreteAggregate(具体的集合)
主要负责Aggregate定义的接口,它会创建出具体的Iterator迭代器,也就是上面的ConcreteIteartor。
使用抽象类和接口
在大多数情况下,很多人喜欢用ConcreteIteartor和ConcreteAggregate编程,而不使用Iterator接口和Aggregate接口,他们总想用具体的类来解决所有的问题。
但是如果只使用具体的类来解决问题,很容易导致类之间的强耦合,这些类也难以作为组件被再次利用。为了弱化类之间的耦合,进而使得类更加容易作为组件被再次使用,所以需要引入抽象类和接口。不要只使用具体类来编程,要优先使用抽象类和接口来编程。