设计模式22-迭代器模式

设计模式22-迭代器模式

  • 迭代器模式(Iterator Pattern)
  • 动机
  • 定义结构
    • 定义
    • 结构
      • 结构图解释
      • 注意事项
  • C++代码推导
    • 多态属性(虚函数)实现迭代器
      • 1. **返回值问题**
      • 2. **对象切割问题**
      • 3. **内存管理问题**
      • 4. **迭代器生命周期问题**
      • 5. **接口设计问题**
      • 6. **接口返回值的问题**
    • 模版形式实现迭代器
    • 为什么以模版形式实现迭代器而不使用多态性来实现迭代器
      • 1. **性能考虑**
      • 2. **灵活性和类型安全**
      • 3. **类型擦除的避免**
      • 4. **代码复用和泛型编程**
      • 5. **减少内存开销**
      • 6. **模板元编程**
      • 什么时候选择多态性?
      • 总结
  • 优缺点
  • 应用场景
  • 总结

迭代器模式(Iterator Pattern)

动机

  • 软件构建过程集合对象内部结构经常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,也可以让外部客户代码透明的访问其中包含的元素,同时这种透明便利也为同一种算法在多种集合对象上进行操作提供了可能。
  • 使用面向对象技术将这种便利机制抽象为迭代器对象。为应对变化中的集合对象提供了一种优雅的方式。
  • 在软件开发中,经常需要遍历某种集合对象(如数组、链表、树等)中的元素。集合的具体实现通常不应暴露给客户端,以确保集合的封装性和灵活性。为了遍历集合中的元素而不暴露集合的内部结构,迭代器模式引入了一种通用的遍历机制,使得客户端可以遍历集合而不关心集合的内部实现。

定义结构

定义

迭代器模式提供一种方法,顺序访问一个聚合对象中的各个元素,而不暴露该对象的内部表示。

结构

在这里插入图片描述

结构图解释

1. 类和接口

  • Aggregate(聚合类):这是一个集合类,它提供了创建迭代器对象的方法(通常是CreateIterator()或类似的命名)。在这个例子中,Aggregate类有一个Createlterator()方法,该方法返回一个新的迭代器实例。这个迭代器实例是基于当前Aggregate对象创建的,以便能够遍历它的元素。注意,图片中的Createlterator()方法名似乎有一个小错误,应该是CreateIterator(),但我将遵循图片中的实际文本。

  • lterator(迭代器接口):这是一个定义迭代器行为的接口。它声明了用于遍历元素的方法,如First()(定位到序列的第一个元素),Next()(移动到序列中的下一个元素),IsDone()(检查序列中是否还有更多的元素),以及CurrentItem()(获取当前位置的元素)。在标准的命名习惯中,接口名通常是单数且以大写字母开头,但这里使用了lterator,可能是一个打字错误,正确的可能是Iterator

  • Client(客户端类):这是使用迭代器遍历聚合类中的元素的类。它持有一个迭代器对象的引用,并通过这个迭代器来遍历聚合类中的元素。在这个例子中,Client类有一个名为lterator的私有成员变量(可能是Iterator的一个实例,但这里使用了lterator),并使用了迭代器的First()Next()IsDone()方法来遍历元素。

2. 实现类

  • ConcreteAggregate(具体聚合类):这是Aggregate类的一个具体实现,它实现了CreateIterator()方法来返回一个Concretelterator(应为ConcreteIterator)实例。这个实例能够遍历ConcreteAggregate对象中的元素。

  • Concretelterator(具体迭代器类):这是lterator(应为Iterator)接口的一个具体实现。它实现了接口中定义的所有方法,包括First()Next()IsDone()CurrentItem(),以便能够遍历ConcreteAggregate对象中的元素。在这个例子中,Concreteelterator的构造函数接收一个ConcreteAggregate对象作为参数,以便它能够知道要遍历哪个集合。注意,这里的Concretelterator也是一个小错误,正确的应该是ConcreteIterator

注意事项

  • 图片中的类名和方法名中存在一些拼写错误,如lterator应该是IteratorCreatelterator()应该是CreateIterator()Currentitem()应该是CurrentItem()
  • 迭代器模式的核心在于将遍历聚合类对象的责任从聚合类本身转移到迭代器对象上,这有助于保持聚合类的简洁,并使得遍历逻辑可以在不同的迭代器之间重用。

希望这个解释能够帮助您理解这张关于迭代器模式的结构图。

C++代码推导

多态属性(虚函数)实现迭代器

template<typename T>
class Iterator
{
public:virtual void first() = 0;virtual void next() = 0;virtual bool isDone() const = 0;virtual T& current() = 0;
};template<typename T>
class MyCollection{public:Iterator<T> GetIterator(){//...}};template<typename T>
class CollectionIterator : public Iterator<T>{MyCollection<T> mc;
public:CollectionIterator(const MyCollection<T> & c): mc(c){ }void first() override {}void next() override {}bool isDone() const override{}T& current() override{}
};void MyAlgorithm()
{MyCollection<int> mc;Iterator<int> iter= mc.GetIterator();for (iter.first(); !iter.isDone(); iter.next()){cout << iter.current() << endl;}}

这种迭代器实现方式存在一些问题,使得它在实际应用中可能并不是最佳选择。以下是一些关键问题及原因:

1. 返回值问题

Iterator<int> iter = mc.GetIterator();
  • 问题Iterator<int>是一个抽象类,不能直接实例化。当你尝试返回一个Iterator<int>对象时,编译器会报错,因为不能创建一个抽象类的实例。

  • 改进:应返回一个指向Iterator<int>的指针或者使用智能指针,例如std::unique_ptr<Iterator<int>>,以确保可以动态地分配一个具体的迭代器实例。

    std::unique_ptr<Iterator<T>> GetIterator() {return std::make_unique<CollectionIterator<T>>(*this);
    }
    

2. 对象切割问题

Iterator<int> iter = mc.GetIterator();
  • 问题:即使Iterator<int>能够返回一个对象(假设Iterator<int>不是抽象类),由于Iterator<int>的拷贝会发生对象切割问题,导致派生类的部分(CollectionIterator<int>的特定实现)丢失,剩下的只是一个基类的对象,这会使得运行时的多态行为失效。

  • 改进:避免对象切割,返回迭代器的指针或引用。

3. 内存管理问题

  • 问题:如果使用原始指针(如Iterator<T>*)返回具体迭代器,会面临内存管理问题。如果用户忘记释放内存,可能会导致内存泄漏。

  • 改进:使用智能指针来管理内存,例如std::unique_ptrstd::shared_ptr。智能指针会自动管理对象的生命周期,避免内存泄漏。

4. 迭代器生命周期问题

  • 问题CollectionIterator持有MyCollection的副本(通过成员变量mc)。这意味着迭代器中保存的集合与原集合可能是两个不同的对象。如果集合发生改变,迭代器将无法反映这些变化。

  • 改进:迭代器应持有集合的引用而不是副本,以确保它们始终引用相同的集合对象。

    template<typename T>
    class CollectionIterator : public Iterator<T> {const MyCollection<T>& mc;
    public:CollectionIterator(const MyCollection<T>& c) : mc(c) { }// 实现迭代器方法
    };
    

5. 接口设计问题

  • 问题:迭代器接口设计可能存在一些细节问题。例如,current()方法返回对当前元素的引用时,如果集合为空,调用current()会导致未定义行为。

  • 改进:在实现current()时,应该确保集合非空或抛出异常以避免未定义行为。也可以提供一个安全的接口,如返回指向元素的指针。

6. 接口返回值的问题

  • 问题GetIterator()方法在你的设计中返回Iterator<T>,但是实际应该返回指向具体Iterator的指针或引用,以支持多态。

  • 改进:改用返回智能指针或指针以支持多态:

    std::unique_ptr<Iterator<T>> GetIterator() {return std::make_unique<CollectionIterator<T>>(*this);
    }
    

模版形式实现迭代器

以下是一个使用迭代器模式遍历一个简单集合(例如整数数组)的C++代码示例。

迭代器接口类:

template <typename T>
class Iterator {
public:virtual ~Iterator() {}virtual T first() = 0;virtual T next() = 0;virtual bool isDone() const = 0;virtual T currentItem() const = 0;
};

具体迭代器类:

template <typename T>
class ConcreteIterator : public Iterator<T> {
private:const std::vector<T>& collection;size_t currentIndex;public:ConcreteIterator(const std::vector<T>& collection): collection(collection), currentIndex(0) {}T first() override {return collection[0];}T next() override {currentIndex++;if (currentIndex < collection.size()) {return collection[currentIndex];}throw std::out_of_range("Iterator out of range");}bool isDone() const override {return currentIndex >= collection.size();}T currentItem() const override {if (currentIndex < collection.size()) {return collection[currentIndex];}throw std::out_of_range("Iterator out of range");}
};

聚合接口类:

template <typename T>
class Aggregate {
public:virtual ~Aggregate() {}virtual Iterator<T>* createIterator() const = 0;
};

具体聚合类:

template <typename T>
class ConcreteAggregate : public Aggregate<T> {
private:std::vector<T> items;public:ConcreteAggregate(std::initializer_list<T> elements) : items(elements) {}Iterator<T>* createIterator() const override {return new ConcreteIterator<T>(items);}size_t size() const {return items.size();}T getItem(size_t index) const {if (index < items.size()) {return items[index];}throw std::out_of_range("Index out of range");}
};

客户端代码:

#include <iostream>
#include <vector>
#include <stdexcept>int main() {ConcreteAggregate<int> aggregate({1, 2, 3, 4, 5});Iterator<int>* iterator = aggregate.createIterator();for (iterator->first(); !iterator->isDone(); iterator->next()) {std::cout << iterator->currentItem() << " ";}delete iterator;return 0;
}

运行结果:

1 2 3 4 5

为什么以模版形式实现迭代器而不使用多态性来实现迭代器

在 C++ 中,实现迭代器时选择使用模板而非多态性(即虚函数)主要有以下几个原因:

1. 性能考虑

模板是在编译时进行类型替换的,这意味着模板代码在编译时生成特定类型的代码实例,而不需要在运行时进行类型检查或虚函数调用。这带来了以下几个性能上的好处:

  • 无虚函数开销:使用模板可以避免虚函数调用的开销。虚函数调用通常涉及通过虚函数表(vtable)间接访问函数,可能导致一些额外的性能开销。而模板直接生成目标类型的代码,函数调用可以内联,从而提高运行时性能。

  • 优化机会:编译器在模板实例化时有更多的优化机会。由于编译器知道确切的类型,可以进行更多的优化,比如内联、消除不必要的操作等。

2. 灵活性和类型安全

  • 强类型检查:模板提供了更强的类型安全性,因为所有类型信息在编译时就已经确定。模板迭代器在编译时会检查是否所有操作都符合类型约束,避免了在运行时发生类型错误。

  • 支持更多容器:模板允许你为不同类型的集合(如std::vector<T>std::list<T>等)生成不同的迭代器实现,而不需要定义多个派生类。这样,模板迭代器可以更好地适应各种容器类型,而不需要为每种类型手动定义派生类。

3. 类型擦除的避免

  • 避免类型擦除:在多态性实现中,为了支持不同类型的对象,通常需要使用指针或引用来指向基类。这涉及到类型擦除(type erasure),即在运行时丢失部分类型信息。而模板不会丢失类型信息,能够保留完整的类型信息,并在编译时确保类型的正确性。

4. 代码复用和泛型编程

  • 代码复用:模板允许编写泛型代码,这样可以避免重复为不同类型编写类似的代码。通过使用模板,可以编写一次泛型迭代器,然后在不同类型的集合上复用该迭代器代码。

  • 泛型编程的自然适应性:模板是一种强大的工具,尤其适用于泛型编程。在 C++ 标准库中,几乎所有容器的迭代器都是通过模板实现的,形成了一个一致的泛型编程体系(如 STL)。这使得模板迭代器可以无缝地与 C++ 标准库的算法(如 std::sort, std::find 等)协同工作。

5. 减少内存开销

  • 避免额外的对象分配:使用多态性时,通常需要动态分配内存以存储不同类型的对象,这会增加内存开销。模板迭代器由于不需要动态分配内存,可以在栈上分配,并且避免了额外的对象开销。

6. 模板元编程

  • 支持高级编程技巧:模板不仅仅是类型安全和高效的,它还支持复杂的模板元编程技巧。这种能力允许在编译时执行计算和生成代码,从而进一步优化代码的执行效率。

什么时候选择多态性?

尽管模板具有很多优势,但在某些情况下,多态性(即使用虚函数)仍然是合适的选择:

  • 需要在运行时处理异构集合:如果需要在运行时处理不同类型的对象(如一个集合中包含不同类型的对象),模板可能无法提供直接的支持,此时使用多态性可以更好地处理这些需求。

  • 接口稳定性:如果需要提供一个稳定的接口供外部使用,而不希望外部依赖模板实现(模板通常定义在头文件中,容易暴露实现细节),则可以选择使用虚函数接口来隐藏实现。

总结

在 C++ 中,模板实现迭代器的方式由于性能、类型安全性、灵活性、和代码复用性等方面的优势,通常是优于使用多态性的。模板通过在编译时确定类型和生成代码,避免了运行时开销,使得迭代器更高效且灵活。但在处理需要运行时多态性或异构集合的情况下,多态性仍然是不可替代的。

优缺点

优点:

  1. 一致的遍历接口:迭代器模式为不同的聚合结构提供了一致的遍历接口,使得遍历操作与聚合对象的实现解耦。
  2. 封装性:迭代器模式可以避免暴露聚合对象的内部表示,保持了集合对象的封装性。
  3. 灵活性:可以为同一个聚合对象创建不同的迭代器,以支持不同的遍历方式(如正向、反向遍历)。

缺点:

  1. 可能增加复杂性:引入了额外的迭代器类,可能会增加系统的复杂性,特别是当需要支持多种迭代方式时。
  2. 对象数量增加:对于大型聚合对象,迭代器的创建和管理可能会导致较多的对象实例,增加内存开销。

应用场景

迭代器模式适用于以下场景:

  1. 需要遍历集合对象:如数组、链表、树形结构等,需要对集合对象中的元素进行遍历时。
  2. 需要多种遍历方式:如正序、倒序或特定条件下的遍历,可以为同一个聚合对象定义不同的迭代器。
  3. 隐藏聚合对象的实现细节:希望客户端在遍历集合时无需了解集合的具体实现(如内部数据结构)时。

总结

  • 有些模式运用的技术机制可能会过时但是它的思想不会过时
  • 迭代抽象:访问一个聚合对象的内容,而无需暴露它的内部表示。
  • 迭代多态:在遍历不同的集合结构提供一个统一的接口。从而支持同样的算法在不同的集合结构上进行操作。
  • 迭代器的健壮性考虑:便利的同时更改迭代器所在的集合结构会导致问题。
  • 迭代器模式为集合对象提供了一种通用的遍历接口,使得客户端可以以一致的方式访问集合中的元素,而不需要了解集合的内部结构。通过封装遍历逻辑,迭代器模式有效地解耦了集合的实现和使用,但也可能引入额外的复杂性和开销。在需要对复杂集合进行多种遍历时,迭代器模式特别有用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/405241.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【保姆级教程】5分钟上手 Coze 自建插件,把 AI 接入个人微信

上篇&#xff0c;给大家介绍了一款搭建微信机器人的开源项目&#xff1a; 搭建微信机器人的第4种方式&#xff0c;我造了一个摸鱼小助手 不同于需要付费的项目&#xff0c;它的定制化程度非常高~ 问题来了&#xff1a;怎么接入 AI 能力呢&#xff1f; 考虑到大家对 Coze 智能…

(第三期)书生大模型实战营——OpenXLab部署InternLM2实践——上传模型

OpenXLab 部署 InternLM2 实践指南 上传模型 初始化git设置 # install git sudo apt-get update sudo apt-get install git# install git lfs sudo apt-get update sudo apt-get install git-lfs# use git install lfs git lfs installOpenXLab 使用你在平台的用户名作为 Git…

如何使用midjourney?MidJourney订阅计划及国内订阅教程

国内如何订阅MidJourney 第三方代理 参考&#xff1a; zhangfeidezhu.com/?p474 使用信用卡订阅教程 办理国外信用卡&#xff1a; 这个各自找国外的银行办理就好了。 登录MidJourney&#xff1a; 登录MidJourney网站&#xff0c;进入订阅中心。如果是在Discord频道&#x…

MySQL 高阶三 (索引性能分析)

执行过程 Explain explain select * from student s, course c , student_coure sc where s.id sc.studentid and c.id sc.courseid;EXPLAIN执行计划各字段含义: 【ld】 id相同&#xff0c;执行顺序从上到下; id不同&#xff0c;值越大&#xff0c;越先执行)。 【select_type…

C语言程序设计(初识C语言后部分)

1024M1GB&#xff0c;1GB1级棒。关爱一级棒的程序员们&#xff0c;宠TA没商量&#xff01; 5&#xff09;函数的嵌套调用和链式访问 函数和函数之间可以根据实际的需求进行组合的&#xff0c;也就是相互调用的。 1.嵌套调用 函数可以嵌套调用&#xff0c;但不可以嵌套定义&a…

作业帮 TiDB 7.5.x 使用经验

作者&#xff1a; 是我的海 原文来源&#xff1a; https://tidb.net/blog/5f9784d3 近期在使用 TiDB 时遇到的一些小问题的梳理总结&#xff0c;大部分版本都在6.5.6和7.5.2 1、limit 导致的扫描量过大的优化 研发定时任务每天需要扫描大量数据&#xff0c;到时机器网卡被…

TCP并发服务器模型

1.阻塞IO CPU占用率低,等待资源时将任务挂起,不占用CPU资源,等到拿到资源后继续向下执行 向管道中写入数据 write.c #include "../head.h" int main() {int fd;mkfifo("/tmp/myfifo",0777);char tmpbuff[100]{0};fdopen("/tmp/myfifo",O_WRON…

三十八、【人工智能】【机器学习】【监督贝叶斯网络(Bayesian Networks)学习】- 算法模型

系列文章目录 第一章 【机器学习】初识机器学习 第二章 【机器学习】【监督学习】- 逻辑回归算法 (Logistic Regression) 第三章 【机器学习】【监督学习】- 支持向量机 (SVM) 第四章【机器学习】【监督学习】- K-近邻算法 (K-NN) 第五章【机器学习】【监督学习】- 决策树…

39_WAF的概念、功能,ModSecurity部署配置、LAMP环境部署、Ubuntu搭建DVWA靶机测试、测试WAF防御、OWASP规则集的部署

一、WAF的概念 WAF&#xff08; Web Application Firewall &#xff09;&#xff0c;即Web应用防火墙 通过执行一系列HTTP/HTTPS&#xff08;应用层的协议&#xff09;的安全策略为Web应用提供保护的一种网络安全产品。增加攻击者的难度和成本&#xff0c;但不是100%安全。工…

halcon的HObject被释放

经过简述 某项目由我统一管理HObject(区域和图像)的释放。发现某区域被系统外部所释放。可能有两种情况&#xff1a;a&#xff0c;区域交给我后&#xff0c;释放了。b&#xff0c;获取我的区域后释放了。 最终证明是第二种情况&#xff0c;证明如下&#xff1a; a&#xff0c;…

Python数据分析:Pandas与NumPy结合,实现高效数值计算,提升数据分析效率的最佳实践

目前小编的借调任务已经完成&#xff0c;借调到其他组完成了自己的工作&#xff0c;有需要的同学可以看下相关的文章&#xff1a;Python&#xff08;Flask&#xff09; React && Golang&#xff08;Gin&#xff09; Vue(Element)&#xff0c;然后小编认为可以回到原来的…

深入探讨 Nginx:安装、配置及优化指南

一、Nginx 概述及编译安装 1、概述 Nginx是一个高性能的开源HTTP和反向代理服务器&#xff0c;同时也是一个IMAP/POP3邮件代理服务器。它最初由Igor Sysoev于2002年开发&#xff0c;并于2004年首次发布。Nginx以其高并发性、低资源消耗和灵活的配置能力而受到广泛关注&#x…

[鹏城杯 2022]简单的php

题目源代码 <?phpshow_source(__FILE__); $code $_GET[code]; if(strlen($code) > 80 or preg_match(/[A-Za-z0-9]|\|"||\ |,|\.|-|\||\/|\\|<|>|\$|\?|\^|&|\|/is,$code)){die( Hello); }else if(; preg_replace(/[^\s\(\)]?\((?R)?\)/, , $code…

网安新声 | 从微软“狂躁许可”漏洞事件看安全新挑战与应对策略

网安加社区【网安新声】栏目&#xff0c;汇聚网络安全领域的权威专家与资深学者&#xff0c;紧跟当下热点安全事件、剖析前沿技术动态及政策导向&#xff0c;以专业视野和前瞻洞察&#xff0c;引领行业共同探讨并应对新挑战的策略与可行路径。 近期&#xff0c;微软披露了一个最…

JSON, YAML, XML, CSV交互可视化

1、jsoncrack https://jsoncrack.com/editor

双亲委派机制的优势与劣势

三次双亲委派机制的破坏

反向代理:定义与核心作用

反向代理&#xff1a;定义与核心作用 一、反向代理的定义二、反向代理的核心作用 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 反向代理&#xff0c;作为网络架构中的重要组件&#xff0c;扮演着关键角色。本文将简洁介绍反向代理的定义及…

整体思想以及取模

前言&#xff1a;一开始由于失误&#xff0c;误以为分数相加取模不能&#xff0c;但是其实是可以取模的 这个题目如果按照一般方法&#xff0c;到达每个节点再进行概率统计&#xff0c;但是不知道为什么只过了百分之十五的测试集 题目地址 附上没过关的代码 #include<bits…

【区块链+商贸零售】预付宝:商家数字经济服务平台 | FISCO BCOS应用案例

预付宝商家数字经济服务平台是人民链在金融领域落地的 一个区块链应用&#xff0c;致力于营造诚信消费环境&#xff0c;专治诸如健 身房老板卷款跑路、充值后餐馆倒闭钱拿不回来等令消费 者头疼不已的行业乱象&#xff0c;让消费者可以放心地预付费。 平台应用FISCO BCOS区块链…

设计模式学习[3]---单一职责原则+开放封闭原则

文章目录 前言1. 单一职责1.1 原理阐述1.2 处理方式 2. 开放-封闭原则2.1 原理阐释2.2 举例说明 总结 前言 小项目写多了&#xff0c;比如一些什么管理系统之类的&#xff0c;在接触大型项目的时候往往会将之前的一些面向过程的写法代入。 比如UI以及逻辑处理都放在一个类里面…