design-patterns - 如果主题是一个巨大的容器,您如何有效地实现观察者模式?

标签 design-patterns scalability containers observers

我们都知道observer pattern:您有一个主题,可以通知和更新其状态更改的观察者列表。现在假设您要观察的主题是一个容器,并且您想观察容器本身,即元素的元素添加和删除,以及所包含的元素,即容器元素的状态更新。

当在容器中存储大量对象时,如何实现更新机制以便在元素插入和删除方面快速完成?特别是,

  • 您是否会在观察者的本地副本中使用相同类型的容器?
  • 是否有观察者应使用的明智的容器选择? (例如,即使您正在观察链表,总是使用平衡树会更快吗?)
  • 如何快速将迭代器转换为观察到的容器,再将迭代器转换为观察者的容器? (对于数组而言是竞争性的,对于链表而言是困难的?)

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

    如果您的容器是一个数组,那么更改元素将花费固定时间,如果传递元素的索引,则更新m个观察者将花费O(m),如果观察者必须遍历该数组,则更新O(n * m)会花费O(n * m)。

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

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

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

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

    最佳答案

    您必须以某种方式将容器变成主题。

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

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

    通常,如果您的观察者需要识别微小的更改(即属性更改或添加元素),则通知消息应包含足够的信息以使他们可以有效地做到这一点。因此,如果您有一个很大的列表和一个插入,请发送该列表和新元素的索引以及“插入的项目”。

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

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

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

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

    因此,除非您能给出更具体的示例,否则您将无法给出更具体的答案。使用设计模式时,您需要坐下来解决一个真正的问题并动脑筋。

    [EDIT3]以下是使用观察者模式的几个示例:

  • 许多UI框架都使用此模式将事件传播给感兴趣的各方。在Qt中,您可以找到一个中心位置,在这里所有主题都可以注册其信号(它们将发送的通知),观察者可以在其中附加主题。这意味着在一个位置管理所有连接。优点是您无需将此数据结构添加到每个对象。同样,来自外部的对象(非Qt对象)可以发送和接收消息。由于所有内容都在一个地方,因此可以轻松优化此数据结构。缺点是这种结构可能会变得很大,因此当涉及到更多的参与方(甚至是完全不相关的参与方)时,发送消息将花费更多时间。
  • Google使用站点地图协议将网站变成主题,因为即使您只请求URL的最后修改时间(HTTP HEAD而不是HTTP GET),这比一次又一次遍历整个站点效率更高。
  • Windows和Linux中的
  • 文件系统提供通知,以告知应用程序有关新文件或已删除文件的信息。这里的主要问题是当应用程序不运行而文件发生更改时应该怎么办。假设您有一个应用程序可维护目录中文件的校验和。显然,您想了解应用程序关闭时的更改,但这意味着通知服务必须跟踪其发送的最新更改。因此,在这里,该应用程序必须在启动时读取整个树,以查看它可能遗漏的所有内容,并且需要使用观察者模式来在运行时进行更改。
  • 邮件客户端是观察者。它将告诉邮件服务器最近看到的电子邮件的ID,服务器将告诉它任何新邮件。
  • 当您在复杂模型中进行大量属性更改时,通常这是集中所有更改(使更改在单个位置运行)并将观察者附加到那里的唯一方法(而不是将N个观察者附加到M个单个对象上)。在此实现中,观察者可以说“我对任何地方的任何更改都感兴趣”或“任何主题中字段X的更改”或“主题Y中的任何更改”(最后一个通常是“字段更改中的两倍”主题Y中的X”-观察者将仅忽略对字段!= X的更改。
  • 关于design-patterns - 如果主题是一个巨大的容器,您如何有效地实现观察者模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/955155/

    相关文章:

    mysql - 从 1 个 Web 服务器 + 1 个数据库服务器进行扩展

    database - 关于 Elasticsearch 的查询

    mysql - 在表中查找常见行的最佳方法是什么

    python - 如何将软件或其他包添加到 docker 容器中?

    c++ - 在容器之间 move 对象而无需复制开销

    linux - 使用 ssh 或 kubectl exec 的不同环境变量

    c++ - 编写一个经常更改的类/结构

    ios - 如何制作防崩溃的 ios 应用程序

    python - 包装对象的最佳 Python 方式是什么?

    email - 我应该在通知系统中使用什么设计模式?