3

我们都知道观察者模式:您有一个主体能够通知和更新其状态变化的观察者列表。现在假设您要观察的对象是一个容器,您要观察容器本身,即元素的添加和删除,以及包含的元素,即容器元素的状态更新。

当您在容器中存储大量对象时,您将如何实现更新机制以便在元素插入和删除方面快速?尤其,

  • 你会在观察者的本地副本中使用相同类型的容器吗?
  • 观察者应该使用容器的明智选择吗?(例如,即使您正在观察链表,总是使用平衡树会更快吗?)
  • 你如何快速将一个迭代器翻译成被观察的容器到一个迭代器到观察者的容器?(对于数组来说微不足道,对于链表来说很难?)

例如,如果您的容器是一个链表,那么您可以在恒定时间内插入元素。如果 m 个观察者必须遍历包含 n 个元素的列表,则更新需要 O(n * m) 预期时间。

如果你的容器是一个数组,那么改变一个元素需要恒定的时间,如果你传递元素的索引,更新 m 个观察者需要 O(m),如果观察者必须遍历数组,则需要 O(n * m)。

如果有帮助,请考虑以下示例:

示例 1. 你正在编写一个操作系统。您要观察的主题是文件系统及其文件。您的视图是文件浏览器、索引器和其他应用程序。您想在添加、删除或修改文件时更新观察者。

示例 2。您正在编写一个地址簿应用程序,它应该能够处理纽约这样大小的城市。您要观察的主题是您的记录容器(一个人及其地址、电话号码、电子邮件......)。您的观察者是多个视图,当您添加、删除或修改记录时,它们应该会自动更新。(人们可以想象一个视图,其中包含居住在 53 街的人的列表,另一个在地图上为姓氏为 Doe 的每个人绘制点)。

您如何处理完整的目录子树被删除或“53rd St”重命名为“Dijkstra St”的情况?

4

2 回答 2

4

不知何故,您必须将容器变成主题。

这里的主要问题是找到一种有效的方法来注意到变化。大多数情况下,当您遇到此问题时,是因为您要观察的事物没有提供有效的通知机制(可能是因为在编写事物时并未发明观察者设计模式)。

[编辑] 既然你要求一种有效的方法,一般的答案是“这取决于”。设计模式没有“一刀切”的解决方案。它们是解决问题的一般规则。您需要如何在特定情况下实施规则是您在这种情况下解决的问题。

通常,如果您的观察者需要识别小的更改(即属性更改或添加元素),通知消息应该包含足够的信息,以便他们可以有效地执行此操作。所以如果你有一个大列表和一个插入,发送列表和新元素的索引加上“插入的项目”。

至于属性变化,有两种解决方案。一种是为列表中的每个元素添加一个观察者。这可能很慢并且需要大量 RAM,但这意味着您可以将多种类型添加到同一个列表中。

或者,您可以有一个“修改列表服务中的项目”。这意味着禁止直接更改项目,您必须始终使用该服务。然后,该服务可以作为主题工作,并发送带有项目、旧值和更改值以及可能带有列表中的索引的通知。

[EDIT2] 一般规则是尽可能多地收集有关更改的信息并将其传递给观察者。但这真的取决于你的具体问题。假设观察者坐在远程机器上。在这种情况下,没有有效的方法将整个列表发送给它。您只能发送“项目 X 已插入”并希望这就足够了。如果容器无法注意到变化(例如,网站上的新网页),则容器必须一次又一次地遍历整个站点以找到变化,然后以有效的方式告诉观察者。

同样,细节确实取决于具体情况。谷歌运行数千个网络蜘蛛,每小时访问数百万个网页。长期以来,这是“有效的”(如“唯一的方式”)。不久前,实施了“站点地图”协议,允许管理员将他们的网站变成可以告诉 Google 观察者有关更改的主题。

所以除非你能给出一个更具体的例子你需要做什么,否则我不能给你更具体的答案。对于设计模式,有一点你需要坐下来,解决一个真正的问题并打开你的大脑。

[EDIT3] 这里有几个使用观察者模式的例子:

  • 许多 UI 框架使用这种模式将事件传播给感兴趣的各方。在 Qt 中,您有一个中心点,所有主题都可以在其中注册他们的信号(他们将发送的通知),并且观察者可以附加到主题上。这意味着有一个地方可以管理所有连接。优点是您不需要将此数据结构添加到每个对象。此外,来自外部的对象(非 Qt 对象)可以发送和接收消息。由于一切都在一个地方,因此可以轻松优化此数据结构。缺点是这种结构可能会变得非常大,因此当涉及更多方(即使是完全不相关的方)时,发送消息将花费更多时间。

  • Google 使用站点地图协议将网站转换为主题,因为这比一次又一次地遍历整个站点更有效,即使您只请求 URL 的最后修改时间(HTTP HEAD 而不是 HTTP GET)。

  • Windows 和 Linux 中的文件系统提供通知以告知应用程序有关新文件或已删除文件的信息。这里的主要问题是当应用程序未运行时文件更改时会发生什么。假设您有一个应用程序维护目录中文件的校验和。显然,您想知道应用程序关闭时的更改,但这意味着通知服务必须跟踪它发送的最后一次更改。所以在这里,应用程序必须在启动时读取整个树以查看它可能遗漏的任何内容,并且它需要使用观察者模式来在运行时发生变化。

  • 邮件客户端是观察者。它将告诉邮件服务器它看到的最后一封电子邮件的 ID,并且服务器将告诉它任何新邮件。

  • 当您在复杂模型中有大量属性更改时,通常是集中所有更改(使它们通过一个地方运行)并将观察者附加到那里(而不是将 N 个观察者附加到 M 个单独对象)的唯一方法。在这个实现中,观察者可以说“我对任何地方的任何变化感兴趣”或“任何主题中字段 X 的变化”或“主题 Y 的任何变化”(最后一个通常兼作“字段变化” X in subject Y" - 观察者将简单地忽略对字段的更改!= X)。

于 2009-06-05T10:33:53.573 回答
1

为什么不是观察者模式本身?

受试者需要告知观察者有趣的事件。然后观察者将其发送给感兴趣的各方(订阅者)。

主题的性质在这里没有任何意义。(除非我理解你的问题是错误的)。

于 2009-06-05T10:16:12.097 回答