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

时间:2022-12-09 10:06:08

We all know the observer pattern: You have a subject which is able to notify and update a list of observers of its state changes. Now suppose that the subject you would like to observe is a container, and you would like to observe the container itself, i.e. element addition and deletion of elements, and also the contained elements, i.e. state updates of the container elements.

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

How would you implement the update mechanism so that it is fast with respect to element insertion and deletions when you store massive amounts of objects in your container? In particular,

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

  • would you use the same type of container in the local copy of the observers?
  • 你会在观察者的本地副本中使用相同类型的容器吗?

  • is there a smart choice of container which the observers should use? (For instance, would it be faster to, say, always use balanced trees, even if you are observing a linked list?)
  • 观察者应该使用哪种容器的明智选择? (例如,即使您正在观察链表,它会更快,例如,总是使用平衡树吗?)

  • how do you quickly translate an iterator into the observed container into an iterator into the observer's container? (Trivial for arrays, hard for linked lists?)
  • 你如何快速将迭代器转换为观察容器到观察者容器中的迭代器? (对于数组来说是微不足道的,对链表很难?)

If your container is a linked list for instance, then you can insert elements in constant time. If m observers have to iterate through the list containing n elements, then updating takes O(n * m) expected time.

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

If your container is an array, then changing an element takes constant time, and updating m observers takes O(m) if you pass the element's index, O(n * m) if observers have to iterate through the array.

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

If it helps, consider the following examples:

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

Example 1. You are writing an operating system. The subject you would like to observe is the filesystem and its files. Your views are a file explorer, an indexer, and other applications. You would like to update the observers when files are added, deleted or modified.

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

Example 2. You are writing an address book application which should be able to handle a city of the size of New York. The subject you would like to observe is the container of your records (a person with its address, phone numbers, email...). Your observers are several views, which should update automatically when you add, delete or modify a record. (One could image one view containing a list of people who live on 53rd and another drawing dots on a map for each person whose last name is Doe).

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

How do you handle the case that a complete directory-subtree is deleted or that "53rd St" is renamed to "Dijkstra St"?

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

2 个解决方案

#1


Somehow, you must turn the container into a subject.

不知何故,你必须把容器变成一个主题。

The main problem here is to find an efficient way to notice changes. Most of the time when you run into this problem, it's because the thing you want to observe doesn't offer an efficient notification mechanism (probably because the observer design pattern wasn't invented when they thing was written).

这里的主要问题是找到一种注意变化的有效方法。大多数情况下,当你遇到这个问题时,这是因为你想要观察的东西没有提供有效的通知机制(可能是因为观察者设计模式不是在他们编写东西时发明的)。

[EDIT] Since you ask for an efficient way, the general answer is "it depends". Design patterns don't have a "one-size-fits-all" solution. They are general rules how to approach a problem. How you need to implement the rules in a specific situation is something that you solve when you're in the situation.

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

Generally, if your observers need to identify small changes (i.e. an attribute change or adding an element), the notification message should contain enough information that they can do this efficiently. So if you have a big list and an insert, send the list and the index of the new element plus "item as inserted".

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

As for attribute changes, there are two solutions. One is to add an observer to every element in the list. This can be slow and need a lot of RAM but it means you can add several types into the same list.

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

Alternatively, you can have a "modify item in list service". This means that it's forbidden to change items directly, you must always use the service. The service can then work as a subject and send notifications with the item, the old and changed value and possibly with the index in the list.

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

[EDIT2] The general rule is to collect as much information about the change as possible and pass that to the observers. But it really depends on your specific problem. Let's say the observer is sitting on a remote machine. In this case, there is no efficient way to send it the whole list. You can only send it "item X was inserted" and hope that's enough. If the container has no way to notice changes (for example, new web pages on a web site), the container has to traverse the whole site again and again to find changes which it can then tell the observers in an efficient manner.

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

Again, the details really depend on the specific situation. Google runs thousand of web spiders which visit millions of web pages every hour. For a long time, this was "efficient" (as in "the only way"). A while ago, the "sitemap" protocol was implemented which allows admins to turn their web sites into subjects that can tell the Google observer about changes.

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

So unless you can give a more specific example what you need to do, I can't give you a more specific answer. With design patterns, there is a point where you need to sit down, take a real problem and turn on your brain.

因此,除非您能够提供更具体的示例,否则我无法给您更具体的答案。通过设计模式,您需要坐下来,解决真正的问题并开启您的大脑。

[EDIT3] Here are a couple of examples for uses of the observer pattern:

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

  • Many UI frameworks use this pattern to spread events to interested parties. In Qt, you have a central spot where all subjects can register their signals (notifications they will send) and where observers can attach to subjects. This means there is a single spot where all connections are managed. The advantage is that you don't need to add this data structure to every object. Also, objects from outside (non-Qt objects) can send and receive messages. Since everything is in a single place, this data structure can be optimized easily. The drawback is that this structure can become very big, so sending a message will take more time when there are more parties involved (even ones which are completely unrelated).

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

  • Google uses the sitemap protocol to turn web sites into subjects since that's much more efficient than traversing the whole site again and again, even if you only request the last modification time of a URL (HTTP HEAD instead of HTTP GET).

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

  • Filesystems in Windows and Linux offer notifications to tell applications about new or deleted files. The main problem here is what should happen when files change while an application doesn't run. Say you have an app which maintains checksums of files in a directory. Obviously, you'd like to know about changes when the app was down but that would mean the notification service would have to keep track of the last change it sent. So here, the app has to read the whole tree in at startup to see anything it might have missed and it needs to use the observer pattern for changes happening while it runs.

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

  • A mail client is an observer. It will tell the mail server the ID of the last email it has seen and the server will tell it about any new ones.

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

  • When you have lots of attribute changes in a complex model, it's usually the only way to centralize all changes (make them run through a single place) and attach the observers there (instead of attaching N observers to M individual objects). In this implementation, the observers can say "I'm interested in any change anywhere" or "a change of the field X in any subject" or "any change in subject Y" (the last one usually doubles as a "change of field X in subject Y" - the observer will simply ignore changes to fields != X).

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

#2


Why not observer pattern itself ?

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

The subject needs to inform the observer about the interesting events. Then the observer shall dispatch it to interested parties (subscribers).

主题需要告知观察者有趣的事件。然后观察员应将其发送给感兴趣的各方(订户)。

The nature of the subject is not of any significance here. (Unless I understood your question wrong).

这个主题的性质没有任何意义。 (除非我理解你的问题不对)。

#1


Somehow, you must turn the container into a subject.

不知何故,你必须把容器变成一个主题。

The main problem here is to find an efficient way to notice changes. Most of the time when you run into this problem, it's because the thing you want to observe doesn't offer an efficient notification mechanism (probably because the observer design pattern wasn't invented when they thing was written).

这里的主要问题是找到一种注意变化的有效方法。大多数情况下,当你遇到这个问题时,这是因为你想要观察的东西没有提供有效的通知机制(可能是因为观察者设计模式不是在他们编写东西时发明的)。

[EDIT] Since you ask for an efficient way, the general answer is "it depends". Design patterns don't have a "one-size-fits-all" solution. They are general rules how to approach a problem. How you need to implement the rules in a specific situation is something that you solve when you're in the situation.

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

Generally, if your observers need to identify small changes (i.e. an attribute change or adding an element), the notification message should contain enough information that they can do this efficiently. So if you have a big list and an insert, send the list and the index of the new element plus "item as inserted".

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

As for attribute changes, there are two solutions. One is to add an observer to every element in the list. This can be slow and need a lot of RAM but it means you can add several types into the same list.

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

Alternatively, you can have a "modify item in list service". This means that it's forbidden to change items directly, you must always use the service. The service can then work as a subject and send notifications with the item, the old and changed value and possibly with the index in the list.

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

[EDIT2] The general rule is to collect as much information about the change as possible and pass that to the observers. But it really depends on your specific problem. Let's say the observer is sitting on a remote machine. In this case, there is no efficient way to send it the whole list. You can only send it "item X was inserted" and hope that's enough. If the container has no way to notice changes (for example, new web pages on a web site), the container has to traverse the whole site again and again to find changes which it can then tell the observers in an efficient manner.

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

Again, the details really depend on the specific situation. Google runs thousand of web spiders which visit millions of web pages every hour. For a long time, this was "efficient" (as in "the only way"). A while ago, the "sitemap" protocol was implemented which allows admins to turn their web sites into subjects that can tell the Google observer about changes.

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

So unless you can give a more specific example what you need to do, I can't give you a more specific answer. With design patterns, there is a point where you need to sit down, take a real problem and turn on your brain.

因此,除非您能够提供更具体的示例,否则我无法给您更具体的答案。通过设计模式,您需要坐下来,解决真正的问题并开启您的大脑。

[EDIT3] Here are a couple of examples for uses of the observer pattern:

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

  • Many UI frameworks use this pattern to spread events to interested parties. In Qt, you have a central spot where all subjects can register their signals (notifications they will send) and where observers can attach to subjects. This means there is a single spot where all connections are managed. The advantage is that you don't need to add this data structure to every object. Also, objects from outside (non-Qt objects) can send and receive messages. Since everything is in a single place, this data structure can be optimized easily. The drawback is that this structure can become very big, so sending a message will take more time when there are more parties involved (even ones which are completely unrelated).

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

  • Google uses the sitemap protocol to turn web sites into subjects since that's much more efficient than traversing the whole site again and again, even if you only request the last modification time of a URL (HTTP HEAD instead of HTTP GET).

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

  • Filesystems in Windows and Linux offer notifications to tell applications about new or deleted files. The main problem here is what should happen when files change while an application doesn't run. Say you have an app which maintains checksums of files in a directory. Obviously, you'd like to know about changes when the app was down but that would mean the notification service would have to keep track of the last change it sent. So here, the app has to read the whole tree in at startup to see anything it might have missed and it needs to use the observer pattern for changes happening while it runs.

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

  • A mail client is an observer. It will tell the mail server the ID of the last email it has seen and the server will tell it about any new ones.

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

  • When you have lots of attribute changes in a complex model, it's usually the only way to centralize all changes (make them run through a single place) and attach the observers there (instead of attaching N observers to M individual objects). In this implementation, the observers can say "I'm interested in any change anywhere" or "a change of the field X in any subject" or "any change in subject Y" (the last one usually doubles as a "change of field X in subject Y" - the observer will simply ignore changes to fields != X).

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

#2


Why not observer pattern itself ?

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

The subject needs to inform the observer about the interesting events. Then the observer shall dispatch it to interested parties (subscribers).

主题需要告知观察者有趣的事件。然后观察员应将其发送给感兴趣的各方(订户)。

The nature of the subject is not of any significance here. (Unless I understood your question wrong).

这个主题的性质没有任何意义。 (除非我理解你的问题不对)。