1

我不知道是否有我需要的特殊关键字。我正在写一个基本的观察者模式,我担心一些问题。我的实现是经典的。我正在使用一个 std::set 观察者,每当我需要触发一个事件时,我都会遍历这个集合并调用每个观察者的 notify 方法。我的问题如下。在以下情况下,当可观察对象向观察者发送事件时会发生什么:

  • 一个观察者想要在事件期间从观察者集中移除自己(或任何其他观察者)?
  • 一位观察者想要清除观察者集(移除所有观察者)?
  • 一个观察者破坏了可观察的对象?

我知道所有这些情况最终都会发生。我对第三个有想法,但这是题外话。对于第一种和第二种情况,问题在于删除或清除 std::set 将使我用来枚举可观察对象的迭代器无效。即使没有,observable 也不应该通知任何在事件处理期间将被删除的观察者。

我还没有找到一个 set 的实现,它提供了一个能够在删除任何项目时保持有效的迭代器。不过,有可能实现它,但代价是一些间接并在容器中存储对活动迭代器的任何引用,以便在必要时对其进行更新。

另一种解决方案是复制观察者集合,迭代副本并检查当前迭代的观察者是否仍在真实集合中。(这会忘记在活动期间添加的任何新观察者,但这种情况我不在乎)

您对此问题有任何建议/解决方案吗?

4

4 回答 4

2

我将假设您在容器中存储指针或其他东西,而不是实际上是观察者对象本身。因为您显然不能让观察者代码删除观察者对象!

当您需要在迭代器处于活动状态时修改集合时,根本无法使用 std::set 。这不是它的用途。

如果您需要从容器中移除东西,您可以尝试从事件例程中返回一个值,该值告诉调用者(具有迭代器的代码)从容器中移除该观察者。该代码可以删除迭代器指向的东西并沿着序列正确继续。

如果您需要添加东西,请不要将它们直接添加到容器中。相反,将它们添加到队列中,并让触发事件的代码在完成对容器的迭代后添加新内容。

如果容器有少量对象,并且对象很小(指针),我可能只是复制容器并迭代副本。这样,观察者可以随心所欲地处理容器,而不会搞砸迭代器。

于 2012-10-10T16:57:24.733 回答
1

让观察者的通知方法接受一个指向该观察者的迭代器,并返回一个指向下一个观察者的迭代器:

// PSEUDO-CODE not to be taken literally.

class normalObserver : public Observer {
  iterator notify(iterator me) { 
    assert(*me == this); 
    // do stuff
    return ++me;
  }
 };

 class deleteMeObserver : public Observer {
   iterator notify(iterator me) {
    assert(*me == this); 
     // do stuff
     object.observers.erase(me++);
     return me;
   }
 };

 class deleteEveryObserver :public Observer {
   iterator notifiy(iterator me) {
    assert(*me == this); 
     // do stuff
     object.observers.clear();
     return object.observers.end();
   }
};

 class Object {
   set::set<Observer*> observers;
   void notifyObservers() {
     for(it = observers.begin(); it != observers.end(); ) {
       it = (*it)->notify(it);
     }
   }
 };
于 2012-10-10T17:01:56.187 回答
0

不要让观察者直接访问集合。让他们通过可以访问集合和迭代器的方法进行修改,在调用观察者之前应该递增。此方法可以检查迭代器处的项目是否正在被删除,如果是,则将其递增。对于删除所有情况,只需将迭代器设置为end().

于 2012-10-10T17:23:46.430 回答
0

我建议使用std::list而不是set,因为这样迭代器在删除元素时不会失效,除非它当然会自行删除。

您可以通过将指针(智能指针甚至可能最好是弱指针)指向观察者对象而不是迭代器来处理想要删除自身的观察者(这样当它删除自己时,指针不会失效(只要它是智能的)并且不会自行删除))

于 2012-10-10T17:03:43.250 回答