当前位置: 首页 > article >正文

【再谈设计模式】迭代器模式~遍历集合元素的利器

一、引言

        在软件开发的世界里,数据结构和算法是构建高效程序的基石。而在处理集合类型的数据时,我们常常需要一种灵活的方式来遍历其中的元素。这时候,迭代器模式就闪亮登场了。它就像是一把万能钥匙,可以打开不同集合类型的遍历之门,无论是数组、链表还是树状结构等。迭代器模式提供了一种统一的、标准的遍历方式,使得代码的可维护性和扩展性大大增强。

二、定义与描述

        迭代器模式是一种行为设计模式,它提供了一种方法来顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。简单来说,就是将遍历的逻辑从被遍历的对象中分离出来,放入一个专门的迭代器对象中。这样,不同的集合对象可以复用相同的迭代器逻辑,而集合对象本身只需要关注自身的数据存储和管理。

三、抽象背景

        在很多应用场景中,我们需要处理各种不同类型的集合。如果直接在每个集合类中编写遍历逻辑,那么代码将会变得非常冗余和难以维护。例如,我们可能有一个存储整数的数组,一个存储字符串的链表,还有一个存储自定义对象的树结构。如果要分别为它们编写遍历代码,不仅工作量大,而且当需求发生变化时(比如增加一种新的遍历方式),需要在每个类中进行修改。迭代器模式通过将遍历逻辑抽象出来,解决了这个问题。

 

四、适用场景与现实问题解决

处理多种集合类型

        当程序中存在多种不同类型的集合,如数组、链表、栈、队列等,并且需要对它们进行遍历操作时,迭代器模式可以提供统一的遍历接口。这样,在编写遍历这些集合的代码时,可以使用相同的迭代器接口,而不需要关心具体的集合类型。

 

隐藏集合内部结构

        有时候,我们不希望外部代码直接访问集合的内部结构(比如数组的下标操作或者链表的指针操作)。迭代器模式可以将集合的遍历操作封装起来,只暴露必要的遍历接口,从而提高了集合的安全性和封装性。

支持多种遍历方式

        对于同一个集合,可能存在多种遍历方式,如正向遍历、反向遍历等。通过迭代器模式,可以方便地实现多种遍历方式,并且可以在不修改集合类的情况下添加新的遍历方式。

五、迭代器模式的现实生活的例子

餐厅菜单

        想象一家餐厅的菜单,菜单上有一系列的菜品。服务员(相当于迭代器)可以按照菜单上菜品的顺序(正向遍历)或者从最后一道菜开始(反向遍历)向顾客介绍菜品,而顾客不需要知道菜单是如何存储菜品信息的(菜单的内部结构)。

 

音乐播放器

        在音乐播放器中,播放列表可以看作是一个集合,而播放顺序(顺序播放、随机播放等)可以通过不同的迭代器来实现。用户不需要知道播放列表是如何存储音乐文件的,只需要通过播放器提供的播放功能(迭代器接口)来享受音乐。

六、初衷与问题解决

初衷:

        为了将遍历集合的逻辑从集合类本身分离出来,以提高代码的可维护性、可扩展性和复用性。

        提供一种统一的遍历接口,使得不同类型的集合可以使用相同的遍历方式,同时隐藏集合的内部结构。

问题解决:

        通过创建独立的迭代器类,避免了在每个集合类中编写重复的遍历逻辑,减少了代码冗余。

        当需要修改遍历逻辑或者添加新的遍历方式时,只需要在迭代器类中进行操作,而不需要修改集合类,遵循了开闭原则。

七、代码示例

Java

类图:

import java.util.Iterator;

// 定义一个集合接口
interface MyCollection {
    Iterator createIterator();
}

// 具体的集合类,这里以数组为例
class MyArray implements MyCollection {
    private String[] items;

    public MyArray(String[] items) {
        this.items = items;
    }

    @Override
    public Iterator createIterator() {
        return new MyArrayIterator();
    }

    // 内部的迭代器类
    private class MyArrayIterator implements Iterator {
        private int index = 0;

        @Override
        public boolean hasNext() {
            return index < items.length;
        }

        @Override
        public Object next() {
            return items[index++];
        }
    }
}

public class Main {
    public static void main(String[] args) {
        String[] array = {"item1", "item2", "item3"};
        MyArray myArray = new MyArray(array);
        Iterator iterator = myArray.createIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

流程图:

 时序图:

C++

#include <iostream>
#include <vector>

// 迭代器抽象类
class Iterator {
public:
    virtual bool hasNext() = 0;
    virtual int next() = 0;
};

// 集合抽象类
class Collection {
public:
    virtual Iterator* createIterator() = 0;
};

// 具体的集合类,这里以vector为例
class MyVector : public Collection {
private:
    std::vector<int> items;

public:
    MyVector(std::vector<int> items) : items(items) {}

    Iterator* createIterator() override {
        return new MyVectorIterator(this);
    }

    // 内部的迭代器类
    class MyVectorIterator : public Iterator {
    private:
        MyVector* collection;
        size_t index = 0;

    public:
        MyVectorIterator(MyVector* collection) : collection(collection) {}

        bool hasNext() override {
            return index < collection->items.size();
        }

        int next() override {
            return collection->items[index++];
        }
    };
};

int main() {
    std::vector<int> vector = {1, 2, 3};
    MyVector myVector(vector);
    Iterator* iterator = myVector.createIterator();
    while (iterator->hasNext()) {
        std::cout << iterator->next() << std::endl;
    }
    delete iterator;
    return 0;
}

Python

# 定义迭代器抽象类
class Iterator:
    def hasNext(self):
        pass

    def next(self):
        pass


# 定义集合抽象类
class Collection:
    def createIterator(self):
        pass


# 具体的集合类,这里以列表为例
class MyList(Collection):
    def __init__(self, items):
        self.items = items

    def createIterator(self):
        return MyListIterator(self)


# 内部的迭代器类
class MyListIterator(Iterator):
    def __init__(self, collection):
        self.collection = collection
        self.index = 0

    def hasNext(self):
        return self.index < len(self.collection.items)

    def next(self):
        item = self.collection.items[self.index]
        self.index += 1
        return item


my_list = MyList([1, 2, 3])
iterator = my_list.createIterator()
while iterator.hasNext():
    print(iterator.next())

Go

package main

import "fmt"

// 迭代器接口
type Iterator interface {
    hasNext() bool
    next() interface{}
}

// 集合接口
type Collection interface {
    createIterator() Iterator
}

// 具体的集合类,这里以切片为例
type MySlice struct {
    items []interface{}
}

func (m *MySlice) createIterator() Iterator {
    return &MySliceIterator{
        slice: m,
        index: 0,
    }
}

// 内部的迭代器类
type MySliceIterator struct {
    slice *MySlice
    index int
}

func (m *MySliceIterator) hasNext() bool {
    return m.index < len(m.slice.items)
}

func (m *MySliceIterator) next() interface{} {
    item := m.slice.items[m.index]
    m.index++
    return item
}

func main() {
    mySlice := MySlice{items: []interface{}{1, 2, 3}}
    iterator := mySlice.createIterator()
    for iterator.hasNext() {
        fmt.Println(iterator.next())
    }
}

八、迭代器模式的优缺点

优点

单一职责原则

        迭代器模式将遍历集合的职责分离到迭代器类中,使得集合类只需要关注自身的数据存储和管理,符合单一职责原则。

开闭原则

        当需要添加新的遍历方式或者修改遍历逻辑时,只需要在迭代器类中进行操作,不需要修改集合类,遵循了开闭原则。

统一的遍历接口

        为不同类型的集合提供了统一的遍历接口,使得代码的可维护性和可读性提高。

缺点

增加复杂性

        引入了额外的迭代器类,使得代码结构相对复杂,对于简单的遍历操作可能会显得有些冗余。

性能开销

        在某些情况下,创建和使用迭代器可能会带来一定的性能开销,比如在频繁遍历小型集合时。

特点描述
单一职责原则迭代器模式将遍历集合的职责分离到迭代器类中,使得集合类只需要关注自身的数据存储和管理,符合单一职责原则。
开闭原则当需要添加新的遍历方式或者修改遍历逻辑时,只需要在迭代器类中进行操作,不需要修改集合类,遵循了开闭原则。
统一的遍历接口为不同类型的集合提供了统一的遍历接口,使得代码的可维护性和可读性提高。
增加复杂性引入了额外的迭代器类,使得代码结构相对复杂,对于简单的遍历操作可能会显得有些冗余。
性能开销在某些情况下,创建和使用迭代器可能会带来一定的性能开销,比如在频繁遍历小型集合时。

九、迭代器模式的升级版 - 内部迭代器

        内部迭代器是迭代器模式的一种改进形式。在传统的迭代器模式中,迭代操作是由外部的迭代器对象控制的,而内部迭代器则是将迭代逻辑封装在集合类内部。例如,在JavaScript中,可以定义一个数组的forEach方法,这个方法就是一种内部迭代器。它接受一个回调函数作为参数,然后在数组内部自动遍历每个元素,并将元素传递给回调函数进行处理。内部迭代器的优点是使用更加方便,代码更加简洁,缺点是灵活性相对较差,不太适合需要复杂遍历逻辑的场景。


http://www.kler.cn/a/558154.html

相关文章:

  • relief=tk.RAISED详细介绍 relief是指定控件的边框样式
  • 【练习】【回溯:组合:一个集合 元素可重复】力扣 39. 组合总和
  • 敏捷开发07:敏捷项目可视化管理-ScrumBoard(Scrum板)使用介绍
  • 论文略读:Uncovering Hidden Representations in Language Models
  • 分类解析决策模型
  • 快速定位并优化CPU 与 JVM 内存性能瓶颈
  • 学习Linux准备2
  • figure机器人技术架构的演进初探——Helix人形机器人控制的革新
  • PL/SQL 异常处理
  • 无人机挂载5G通信基站网络恢复技术详解
  • WordPress自定义排序插件:Simple Custom Post Order完全指南(SEO优化版)
  • 前端面试-JavaScript 数据类型详解
  • asp.net mvc、webform 跨域问题
  • 手机怎样玩电脑游戏?
  • 搭建一个基于Django框架的WebApi项目
  • [Android]如何查看APK是使用的什么签名方案
  • 安全面试2
  • 单例模式代码示例
  • 力扣LeetCode:2595 奇偶位数
  • 根据音频中的不同讲述人声音进行分离音频 | 基于ai的说话人声音分离项目