Próbuję napisać bezpieczną klasę Subject
z observer pattern. Chcę wiedzieć, czy za pomocą weak_ptr
jest najlepszym sposobem przechowywania IObserver
przypadki, w taki sposób, że:Wzorzec obserwatora za pomocą metody weak_ptr
- Nie jest możliwe użycie instancji
IObserver
po jego free'd. - Klasa
Subject
nie zawiera odniesień doIObserver
, które powinny być wolne (lapsed listener problem). - Klasa
Subject
musi być bezpieczna dla wątków.
Niestety, nasze standardy kodowania mówią, że nie możemy używać boostu. Sądzę, że byłem złym człowiekiem w poprzednim życiu. Na szczęście mogę używać C++ 11 (co jest dostarczane z Visual Studio 2012).
Oto przykładowa klasa Observer
.
// Observer interface that supports notify() method
class IObserver
{
public:
virtual void notify() const = 0;
virtual ~IObserver() {}
};
// Concrete observer implementation that prints a message
class Observer : public IObserver
{
public:
Observer(const std::string& message) : m_message(message){}
void notify() const {
printf("%s\r\n", m_message.c_str());
}
private:
std::string m_message;
};
A oto klasa Subject
.
// Subject which registers observers and notifies them as needed.
class Subject
{
public:
// Use shared_ptr to guarantee the observer is valid right now
void registerObserver(const std::shared_ptr<IObserver>& o)
{
std::lock_guard<std::mutex> guard(m_observersMutex);
m_observers.push_back(o);
}
void unregisterObserver(const std::shared_ptr<IObserver>& o)
{
std::lock_guard<std::mutex> guard(m_observersMutex);
// Code to remove the observer from m_observersMutex
}
// This is a method that is run in its own thread that notifies observers of some event
void doNotify()
{
std::lock_guard<std::mutex> guard(m_observersMutex);
// Notify any valid observers of events.
std::for_each(m_observers.cbegin(), m_observers.cend(),
[](const std::weak_ptr<IObserver>& o)
{
auto observer = o.lock();
if (observer) {
observer->notify();
}
});
// Remove any dead observers. These are ones which have expired().
m_observers.erase(std::remove_if(m_observers.begin(), m_observers.end(),
[](const std::weak_ptr<IObserver>& o)
{
return o.expired();
}), m_observers.end());
}
private:
std::vector<std::weak_ptr<IObserver>> m_observers;
std::mutex m_observersMutex;
};
Oto niektóre kod, który sprawuje Subject
:
int main(int argc, wchar_t* argv[])
{
Subject subject;
auto observerHello = std::make_shared<Observer>("Hello world");
subject.registerObserver(observerHello);
{
// Create a scope to show unregistration.
auto observerBye = std::make_shared<Observer>("Good bye");
subject.registerObserver(observerBye);
subject.doNotify();
}
printf("%s\r\n", "Observer good bye is now be destructed");
subject.doNotify();
return 0;
}
Czy mój wykorzystanie weak_ptr
wątku bezpieczny? Stąd https://stackoverflow.com/a/2160422/1517648 I myślę, że jest to.
Czy jest to uzasadniony sposób rozwiązania problemu z utratą słuchacza?
nie 2012 ma zakres oparty na pętli? dlaczego używasz for_each? (nieistotne dla tego pytania, heh) – David
To tylko nawyk, nie ma powodu, poza tym, do czego jestem przyzwyczajony. – Steve