是什么使迭代器成为设计模式?


9

我一直在想,与其他类似构造相比,使Iterator变得特别的原因是什么,并且使“ 四人帮”将其列为设计模式。

迭代器基于多态性(具有公共接口的集合的层次结构)和关注点分离(对集合的迭代应独立于数据的结构方式)。

但是,如果我们将集合的层次结构替换为例如数学对象的层次结构(整数,浮点数,复数,矩阵等),而迭代器由代表这些对象的某些相关操作(例如幂函数)的类替换,该怎么办?类图将是相同的。

我们可能会发现更多类似的示例,例如Writer,Painter,Encoder和更好的示例,它们的工作方式相同。但是,我从未听说过其中任何一种称为设计模式。

那么,什么使Iterator特别呢?

是否因为需要可变状态来将当前位置存储在集合中而变得更加复杂,这是事实吗?但是,通常不认为可变状态是理想的。


为了阐明我的观点,让我举一个更详细的例子。

这是我们的设计问题:

假设我们有一个类的层次结构,并且在这些类的对象上定义了一个操作。每个类的操作接口相同,但实现方式可以完全不同。还假定对同一对象(例如具有不同的参数)多次应用该操作是有意义的。

这是解决我们设计问题的明智解决方案(实际上是迭代器模式的概括):

为了分离关注点,不应将操作的实现作为功能添加到原始类层次结构(操作数对象)中。由于我们想在同一个操作数上多次应用该操作,因此它应该由一个持有对该操作数的引用的对象来表示,而不仅仅是由函数表示。因此,操作数对象应提供一个函数,该函数返回表示操作的对象。该对象提供执行实际操作的功能。

一个例子:

有一个基类或接口MathObject(愚蠢的名字,我知道,也许有人有更好的主意。)与派生类MyIntegerMyMatrix。对于每个MathObject操作,Power都应定义一个允许计算平方,立方等的运算。所以我们可以写(用Java):

MathObject i = new MyInteger(5);
Power powerOfFive = i.getPower();
MyInteger square = powerOfFive.calculate(2); // should return 25
MyInteger cube = powerOfFive.calculate(3); // should return 125

5
“类图将是相同的”-那么呢?设计模式不是类图。它是针对重复出现的问题的一类解决方案的高级抽象。
布朗

@DocBrown:是的,但是不是数学运算,将对象写入文件,图形输出或编码数据重复出现的问题,就像迭代一样吗?
Frank Puffer

设计模式的选择是主观的(即在“设计者”或判断设计的人的眼中)。设计模式的命名旨在与领域无关(这样我们就不会因为认为它是领域特定的而分心)。仅我的意见,我没有任何引用。
rwong

@FrankPuffer如果概述了用于将对象写入文件的通用解决方案,则可以写下您的解决方案并将其称为“对象写入模式”(如果这样做有帮助)。
布兰丁

3
您想得太多了。设计模式是解决常见计算问题的众所周知的解决方案,仅此而已。当您可以识别并应用其提供的好处时,便可以使用该模式。
罗伯特·哈维

Answers:


9

GoF书中的大多数模式都具有以下共同点:

  • 他们使用面向对象的方法解决基本的设计问题
  • 人们经常在任意程序中面对此类问题,而无论是来自领域还是来自企业
  • 它们是使代码更可重用的秘诀,通常是通过使代码更可靠
  • 他们提出了解决这些问题的经典方法

这些模式所解决的问题是如此基础,以至于许多开发人员主要将其理解为缺少编程语言功能的变通办法,这是恕我直言的正确观点(请注意,GoF本书出自1995年,当时Java和C ++没有提供那么多的功能)。

迭代器模式非常适合此描述:它解决了一个经常独立于任何特定领域而经常发生的基本问题,并且正如您自己编写的那样,它是“关注点分离”的一个很好的示例。如您所知,在许多当代编程语言中都可以找到直接迭代器支持。

现在将此与您选择的问题进行比较:

  • 写入文件-IMHO根本不够“基本”。这是一个非常具体的问题。也没有一个好的规范的解决方案-有很多不同的方法来写入文件,也没有明确的“最佳实践”。
  • 画家,编码器:无论您想到什么,这些问题对我来说似乎都不那么基本,甚至与领域无关。
  • 具有可用于不同种类对象的“幂”函数:乍一看,这可能值得借鉴,但是您提出的解决方案并不能使我信服-看起来更像是在尝试将幂函数分解为类似于迭代器模式。我用工程计算实现了很多代码,但我不记得有一种情况与您的幂函数对象类似,但这种方法对我有帮助(但是,迭代器是我每天必须处理的事情)。

而且,在您的幂函数示例中,我看不到任何不能解释为策略模式或命令模式的应用程序的内容,这意味着这些基本部分已经在GoF书中。更好的解决方案可能包含运算符重载或扩展方法,但是这些都是受语言特性约束的,而这正是“帮派”使用的“ OO手段”无法提供的。


The problems solved by these patterns are so basic that many developers think their main purpose is to be workarounds for missing programming language features具有讽刺意味的是,软件开发人员通常会使用已有20年历史的软件设计模式,同时仍然认为自己正在编写最新的代码。
罗伯特·哈维

@RobertHarvey:我认为今天没有很多开发人员会采用GoF建议的“ OO方式”来实现迭代器模式。他们通常通过语言或标准库(例如,在C#中使用IEnumerableyield)提供的方式来实现它。但是对于其他GoF模式,您编写的内容可能是正确的。
布朗

1
workarounds for missing programming language features: blog.plover.com/prog/johnson.html
jrw32982支持Monica

8

四人帮引用克里斯托弗·亚历山大的图案定义:

每个模式都描述了一个在我们的环境中反复发生的问题,然后描述了该问题的解决方案的核心[…]

迭代器解决了什么问题?

目的:提供一种顺序访问聚合对象的元素而不暴露其底层表示的方法。

适用性:使用迭代器模式

  • 在不暴露其内部表示的情况下访问聚合对象的内容
  • 支持聚合对象的多次遍历
  • 提供用于遍历不同聚合结构的统一接口(即,支持多态迭代)。

因此,可以认为迭代器模式从定义上来说是特定于集合的。那完全可以。其他模式(例如解释器模式)是特定于域的特定于语言的语言,工厂模式是特定于域的特定于对象的创建,…。当然,这是对“特定于域的”的相当愚蠢的理解。只要是重复出现的问题-解决方案对,我们就可以称其为模式。

存在Iterator模式是件好事。如果您不使用它,则会发生不好的事情。我最喜欢的反示例是Perl。在这里,每个集合(数组或哈希)都将迭代器状态作为集合的一部分。为什么这样不好?我们可以使用while-each循环轻松地遍历哈希:

while (my ($key, $value) = each %$hash) {
  say "$key => $value";
}

但是,如果我们在循环体中调用函数呢?

while (my ($key, $value) = each %$hash) {
  do_something_with($key, $value, $hash);
}

现在,此功能几乎可以执行任何所需的操作,但以下情况除外:

  • 添加或删除哈希条目,因为它们会不可预测地改变迭代顺序(用C ++讲,它们会使迭代器无效)。
  • 在不执行复制的情况下迭代相同的哈希表,因为这将消耗相同的迭代状态。哎呀。

如果调用的函数应使用迭代器,则循环的行为将变得不确定。那是个问题。迭代器模式有一个解决方案:将所有迭代状态放在每个迭代创建的单独对象中。

是的,当然,迭代器模式与其他模式有关。例如,迭代器如何实例化?在Java中,我们有一个泛型Iterable<T>Iterator<T>接口。具体的可迭代对象ArrayList<T>会创建特定类型的迭代器,而a HashSet<T>可能会提供完全不同的迭代器类型。这让我很想起抽象工厂模式,其中的Iterable<T>是抽象工厂,Iterator而是产品。

多态迭代器也可以解释为策略模式的示例。例如,一棵树可能提供不同类型的迭代器(预排序,有序,后排序等)。在外部,它们都将共享一个迭代器接口并按一定顺序产生元素。客户端代码仅需要依赖于迭代器接口,而不依赖于任何特定的树遍历算法。

模式不是孤立存在的,彼此独立。有些模式是针对同一问题的不同解决方案,有些模式在不同的上下文中描述了相同的解决方案。有些模式暗示着另一种。另外,当您翻到“设计模式”书的最后一页时,模式空间不会关闭(另请参见您先前的问题,“四人帮”是否彻底探索了“模式空间”?)。“设计模式”书中描述的模式非常灵活和广泛,可以无限变化,而且绝对不是唯一存在的模式。

您列出的概念(书写,绘画,编码)不是模式,因为它们没有描述问题与解决方案的组合。诸如“我需要写入数据”或“我需要编码数据”之类的任务实际上并不是设计问题,也不包含解决方案。仅由“我知道,我将创建Writer类”组成的“解决方案”是没有意义的。但是,如果出现类似“我不想在屏幕上绘制半渲染图形”的问题,则可能存在一种模式:“我知道,我将使用双缓冲图形!”


好的答案,谢谢。仍然不能完全确信您在上一段中所写的内容在这里适用。我已经编辑了我的问题,以解释我的意思。
Frank Puffer
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.