行为模式-迭代器模式
定义:
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.(它提供一种方法访问一个容器对象中各个元素,而又不需暴露该 对象的内部细节。)
迭代器模式通用类图
迭代器设计模式的核心是将集合对象的遍历行为抽象出来,封装到一个独立的迭代器对象中。这样,集合对象就不需要自己实现遍历逻辑,而是将遍历的责任委托给迭代器对象。客户端只需要通过迭代器提供的接口来访问集合元素,而不需要了解集合的内部结构。这种设计方式不仅使集合的接口和实现更加简洁,还可以支持多种不同的遍历方式,提高了代码的可维护性和可扩展性。
角色:
迭代器模式包含以下几个核心角色:
1、迭代器接口(Iterator)
迭代器接口定义了访问和遍历元素的方法,如`hasNext()`(判断是否还有下一个元素)、`next()`(返回下一个元素)等。所有具体迭代器都必须实现这个接口,确保客户端可以以统一的方式使用迭代器。
2、具体迭代器(Concrete Iterator)
具体迭代器实现了迭代器接口,负责跟踪集合中元素的当前位置,并实现各种遍历方法。具体迭代器通常会持有一个对具体集合对象的引用,以便访问集合中的元素。
3、集合接口(Aggregate)
集合接口定义了创建迭代器对象的方法,如`createIterator()`。所有具体集合都必须实现这个接口,以便客户端可以请求创建适合该集合的迭代器。
4、具体集合(Concrete Aggregate)
具体集合实现了集合接口,负责创建相应的具体迭代器对象。具体集合通常包含一个存储元素的数据结构,如数组、列表等。
代码示例:
下面通过一个简单的示例来展示迭代器设计模式的实现。假设我们有一个自定义的书架类,需要实现对书架上书籍的遍历功能。
// 书籍类
public class Book {private String name;private String author;public Book(String name, String author) {this.name = name;this.author = author;}public String getName() {return name;}public String getAuthor() {return author;}@Overridepublic String toString() {return "《" + name + "》 - " + author;}}
// 迭代器接口
public interface Iterator<T> {boolean hasNext();T next();}
// 具体迭代器:书架迭代器
public class BookShelfIterator implements Iterator<Book> {private BookShelf bookShelf;private int index;public BookShelfIterator(BookShelf bookShelf) {this.bookShelf = bookShelf;this.index = 0;}@Overridepublic boolean hasNext() {return index < bookShelf.getLength();}@Overridepublic Book next() {Book book = bookShelf.getBookAt(index);index++;return book;}}
// 集合接口
public interface Aggregate<T> {Iterator<T> createIterator();}
// 具体集合:书架
public class BookShelf implements Aggregate<Book> {private Book[] books;private int last = 0;public BookShelf(int maxSize) {this.books = new Book[maxSize];}public Book getBookAt(int index) {return books[index];}public void appendBook(Book book) {this.books[last] = book;last++;}public int getLength() {return last;}@Overridepublic Iterator<Book> createIterator() {return new BookShelfIterator(this);}}
// 客户端代码
public class IteratorPatternClient {public static void main(String[] args) {// 创建书架并添加书籍BookShelf bookShelf = new BookShelf(4);bookShelf.appendBook(new Book("Java编程思想", "Bruce Eckel"));bookShelf.appendBook(new Book("设计模式", "Erich Gamma"));bookShelf.appendBook(new Book("算法导论", "Thomas H. Cormen"));bookShelf.appendBook(new Book("计算机程序的构造和解释", "Harold Abelson"));// 使用迭代器遍历书架上的书籍Iterator<Book> iterator = bookShelf.createIterator();System.out.println("书架上的书籍:");while (iterator.hasNext()) {Book book = iterator.next();System.out.println(book);}}}
优点 :
1、支持多种遍历方式:迭代器模式可以为一个聚合对象提供多种不同的遍历方式,客户端可以根据需要选择合适的迭代器,而不需要修改聚合对象的代码。
2、简化聚合对象的接口:迭代器模式将遍历逻辑从聚合对象中分离出来,使得聚合对象的接口更加简洁,只需要关注元素的存储和管理,而不需要关注遍历逻辑。
3、符合单一职责原则:迭代器模式将遍历逻辑封装在迭代器类中,使得聚合对象和迭代器类各自承担自己的职责,符合单一职责原则。
4、符合开闭原则:迭代器模式可以在不修改聚合对象代码的情况下,通过增加新的迭代器类来支持新的遍历方式,符合开闭原则。
5、隐藏聚合对象的内部结构:迭代器模式可以将聚合对象的内部结构隐藏起来,客户端只需要通过迭代器提供的接口访问元素,提高了系统的安全性。
缺点:
1、增加类的数量:迭代器模式需要定义迭代器接口、具体迭代器类、聚合接口和具体聚合类等多个类,会增加系统的类数量,使代码结构变得复杂。
2、对于简单的聚合结构可能过于繁琐:对于简单的聚合结构,如数组,直接使用循环遍历可能更加简单和高效,使用迭代器模式可能会显得过于繁琐。
3、迭代器的维护成本:当聚合对象的内部结构发生变化时,可能需要修改迭代器的实现,增加了维护成本。
使用场景:
(一)需要为聚合对象提供多种遍历方式
当一个聚合对象需要支持多种不同的遍历方式时,如正向遍历、反向遍历、跳跃遍历等,可以使用迭代器模式将不同的遍历逻辑封装到不同的迭代器类中,客户端可以根据需要选择合适的迭代器。
(二)需要隐藏聚合对象的内部结构
迭代器模式可以将聚合对象的内部结构隐藏起来,客户端只需要通过迭代器提供的接口访问元素,而不需要了解聚合对象的具体实现。这样可以保护聚合对象的内部数据不被非法访问。
(三)需要为不同的聚合结构提供统一的遍历接口
当系统中存在多种不同类型的聚合结构(如数组、列表、树等),且需要为它们提供统一的遍历接口时,迭代器模式可以定义一个统一的迭代器接口,不同的聚合结构实现各自的具体迭代器,使得客户端可以使用相同的代码遍历不同的聚合结构。
(四)需要在遍历的同时对元素进行特定操作
在遍历聚合对象的过程中,可能需要对元素进行特定的操作,如过滤、转换等。迭代器模式可以在迭代器中实现这些操作,使得遍历和操作可以同时进行,提高代码的灵活性。
迭代器设计模式通过将集合对象的遍历行为抽象出来,封装到独立的迭代器对象中,为我们提供了一种统一且灵活的方式来访问集合元素。它在不暴露集合内部结构的情况下,支持多种遍历方式,简化了集合接口,提高了代码的可维护性和可扩展性。在实际开发中,当我们需要处理集合的遍历问题,特别是需要支持多种遍历方式或隐藏集合内部结构时,迭代器模式是一个不错的选择。但也要注意避免在简单场景中过度使用,以免增加系统的复杂性。掌握迭代器设计模式,能够让我们的代码更加简洁、灵活和易于维护。