我目前正在重构一个应用程序,如果它们的状态发生变化,类可以在其中调用观察者。这意味着无论何时都会调用观察者:
- 类更改的实例中的数据
- 创建类的新实例
- 类的实例被删除
正是这最后一个案例让我担心。
假设我的班级是 Book。观察者存储在一个名为 BookManager 的类中(BookManager 还保存所有书籍的列表)。这意味着我们有这个:
class Book
{
...
};
class BookManager
{
private:
std::list<Book *> m_books;
std::list<IObserver *> m_observers;
};
如果一本书被删除(从列表中删除并从内存中删除),则调用观察者:
void BookManager::removeBook (Book *book)
{
m_books.remove(book);
for (auto it=m_observers.cbegin();it!=m_observers.cend();++it) (*it)->onRemove(book *);
delete book;
}
问题是我无法控制观察者的逻辑。观察者可以通过插件或客户开发人员编写的代码来交付。
因此,尽管我可以编写这样的代码(并且我确保在删除实例的情况下获得列表中的下一个):
auto itNext;
for (auto it=m_books.begin();it!=m_books.end();it=itNext)
{
itNext = it:
++itNext;
Book *book = *it;
if (book->getAuthor()==string("Tolkien"))
{
removeBook(book);
}
}
观察者总是有可能从列表中删除其他书籍:
void MyObserver::onRemove (Book *book)
{
if (book->getAuthor()==string("Tolkien"))
{
removeAllBooksFromAuthor("Carl Sagan");
}
}
在这种情况下,如果列表包含托尔金的书,然后是卡尔萨根的书,则删除所有托尔金书的循环可能会崩溃,因为下一个迭代器 (itNext) 已无效。
图示的问题也可能出现在其他情况下,但删除问题是最严重的,因为它很容易使应用程序崩溃。
我可以通过确保在应用程序中首先获取我想要删除的所有实例,将它们放入第二个容器中,然后遍历第二个容器并删除实例来解决这个问题,但由于总是存在观察者显式删除了已经在我的待删除列表中的其他实例,我必须放置显式观察者以使第二个副本也保持最新。
还要求所有应用程序代码在调用观察者(直接或间接)时在容器上迭代时复制列表,这使得编写应用程序代码变得更加困难。
是否有 [设计] 模式可用于解决此类问题?最好不要使用共享指针方法,因为我不能保证整个应用程序都使用共享指针来访问实例。