Design/iterator/readme.md
2024-10-28 14:07:29 +08:00

178 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
* @Description:
* @version:
* @Author: 莫邪
* @Date: 2023-10-31 09:00:24
* @LastEditors: 莫邪
* @LastEditTime: 2023-10-31 09:00:25
-->
# 迭代器模式
## 介绍
迭代器模式是一种设计模式,它提供一种方法访问一个容器对象中各个元素,而不需要暴露该对象的内部表示。
迭代器模式的主要目的是:
1. 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
2. 为遍历不同的集合结构提供一个统一的接口(即迭代器接口)。
迭代器模式的主要 PARTICIPANT 包括:
Iterator 抽象迭代器接口:定义访问和遍历元素的方法。
ConcreteIterator 具体迭代器实现:实现抽象迭代器接口中定义的方法。
Aggregate 抽象容器接口:定义创建相应迭代器对象的方法。
ConcreteAggregate 具体容器实现:实现创建具体迭代器的方法。
客户端可以通过迭代器来遍历容器中的所有元素,而不需要了解容器的内部结构。迭代器简化了聚合类的接口和聚合类的遍历。
迭代器模式在提供一个统一遍历接口的同时,封装了集合的内部结构,使客户端代码与集合的存储结构解耦。
## 定义
假设我们有一个图书管理系统,需要管理图书馆中的所有图书。图书馆有打印版图书和电子版图书,打印版图书存储在书架上,电子版存储在服务器上。
我们希望提供一个统一的界面来遍历所有图书,而不管是打印版还是电子版。
需求如下:
1. 图书馆有一个所有图书的列表,包括打印版和电子版图书。
2. 图书馆可以提供一个迭代器接口,用来遍历所有图书。
3. 打印版图书存储在书架上,需要提供具体的书架迭代器来遍历书架上的图书。
4. 电子版图书存储在服务器上,需要提供具体的服务器迭代器来遍历服务器上的电子图书。
5. 使用图书馆提供的迭代器接口,可以一致地遍历所有图书,而不需要关心图书的存储形式是打印版还是电子版。
图书接口
```cpp
// Book接口
class Book {
public:
virtual string getName() = 0;
};
// 具体书类
class DesignPatternsBook : public Book {
public:
string getName() override ;
};
// 具体书类
class RefactoringBook : public Book {
public:
string getName() override ;
};
```
具体实现
```cpp
// 具体书类
string DesignPatternsBook::getName() { return "Design Patterns"; }
// 具体书类
string RefactoringBook::getName() { return "Refactoring"; }
```
图书聚合类
```cpp
// BookShelf类,实现Aggregate接口
class BookShelf {
private:
vector<Book*> books;
public:
vector<Book*>& Books() ;
void addBook(Book* book) ;
// 创建迭代器
Iterator* createIterator() ;
};
```
实现
```cpp
vector<Book*>& BookShelf::Books() { return books; }
void BookShelf::addBook(Book* book) { books.push_back(book); }
// 创建迭代器
Iterator* BookShelf::createIterator() { return new BookShelfIterator(this); }
```
迭代器接口
```cpp
// 迭代器接口
class Iterator {
public:
virtual bool hasNext() = 0;
virtual Book* next() = 0;
};
// 具体迭代器
class BookShelfIterator : public Iterator {
private:
BookShelf* bookShelf;
int index;
public:
BookShelfIterator(BookShelf* bookShelf) ;
bool hasNext() override ;
Book* next() override ;
};
```
实现
```cpp
// 具体迭代器
BookShelfIterator::BookShelfIterator(BookShelf* bookShelf) {
this->bookShelf = bookShelf;
this->index = 0;
}
bool BookShelfIterator::hasNext() {
if (index < bookShelf->Books().size()) {
return true;
}
return false;
}
Book* BookShelfIterator::next() {
Book* book = bookShelf->Books()[index];
index++;
return book;
}
```
## 调用
```
// 客户端代码
int main() {
BookShelf* bookShelf = new BookShelf();
bookShelf->addBook(new DesignPatternsBook());
bookShelf->addBook(new RefactoringBook());
Iterator* it = bookShelf->createIterator();
while (it->hasNext()) {
Book* book = it->next();
cout << book->getName() << endl;
}
return 0;
}
```
## 效果
```cpp
./bin/design/iterator
Design Patterns
Refactoring
```
## 回顾
迭代器模式,也叫游标模式。它用来遍历集合对象。这里说的“集合对象”,我们也可以叫“容器”“聚合对象”,实际上就是包含一组对象的对象,比如,数组、链表、树、图、跳表。
一个完整的迭代器模式一般会涉及容器和容器迭代器两部分内容。为了达到基于接口而非实现编程的目的容器又包含容器接口、容器实现类迭代器又包含迭代器接口、迭代器实现类。容器中需要定义iterator()方法,用来创建迭代器。
遍历集合一般有三种方式for循环、迭代器遍历。相对于for循环遍历利用迭代器来遍历有下面三个优势
- 迭代器模式封装集合内部的复杂数据结构,开发者不需要了解如何遍历,直接使用容器提供的迭代器即可;
- 迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一;
- 迭代器模式让添加新的遍历算法更加容易,更符合开闭原则。除此之外,因为迭代器都实现自相同的接口,在开发中,基于接口而非实现编程,替换迭代器也变得更加容易。