2016-09-15 57 views
5

我至今是:使用了weak_ptr来实现观察者模式

Observer.h

class Observer 
{ 
public: 
    ~Observer(); 
    virtual void Notify() = 0; 
protected: 
    Observer(); 
}; 

class Observable 
{ 
public: 
    ~Observable(); 
    void Subscribe(std::shared_ptr<Observer> observer); 
    void Unsubscribe(std::shared_ptr<Observer> observer); 
    void Notify(); 
protected: 
    Observable(); 
private: 
    std::vector<std::weak_ptr<Observer>> observers; 
}; 

Observer.cpp

void Observable::Subscribe(std::shared_ptr<Observer> observer) 
{ 
    observers.push_back(observer); 
} 

void Observable::Unsubscribe(std::shared_ptr<Observer> observer) 
{ 
    ??? 
} 

void Observable::Notify() 
{ 
    for (auto wptr : observers) 
    { 
     if (!wptr.expired()) 
     { 
      auto observer = wptr.lock(); 
      observer->Notify(); 
     } 
    } 
} 

(DE /建设者在这里实现,但空,所以我把它们排除在外)

我被卡住的是如何实现取消订阅程序。我遇到了清除 - 删除 - 结束语,但我明白,它不会“开箱即用”,因为我已经安装了我的Observable。如何检查observers向量中的weak_ptr元素,以便我可以删除所需的Observer?

我也在寻找一些关于我的Un/Subscribe程序的参数类型的建议。使用std::shared_ptr<Observer>&还是const std::shared_ptr<Observer>&会更好?因为我们不会修改它?

我真的不想让Obserbles拥有他们的Observers,因为它似乎背叛了这个模式的意图,当然不是我想如何构建最终将使用模式的项目的其余部分。也就是说,我正在考虑的一个额外的安全/自动化层是让Observers存储weak_ptr的镜像向量。观察者在出路时可以取消订阅它订阅的所有观察对象,而观察者在其出路上可以消除观察对象的每个观察者对自身的反向引用。很显然,这两个班级在这种情况下会成为朋友。

回答

2

您可以使用std::remove_ifstd::erase这样的:

void Observable::Unsubscribe(std::shared_ptr<Observer> observer) 
{ 
    std::erase(
     std::remove_if(
      this->observers.begin(), 
      this->observers.end(), 
      [&](const std::weak_ptr<Observer>& wptr) 
      { 
       return wptr.expired() || wptr.lock() == observer; 
      } 
     ), 
     this->observers.end() 
    ); 
} 

你确实应当通过observerconst std::shared_ptr<Observer>&

+3

请注意,'std :: remove_if'确实不**从容器中删除元素。你必须使用'container.erase'。搜索擦除 - 删除成语。 – Nawaz

+0

...虽然在这种情况下,'std :: remove_if'会(可能)效率低下; 'std :: find_if'以及'.erase'会表现得更好(或者至少*语义*正确)。 – Nawaz

+1

添加了wptr.expired()检查安全性并删除死亡的观察者。 – M2tM

1

我坚持的是如何实施取消订阅程序。

我建议将观察者存储在std :: list中,因为它的迭代器在容器修改时不会失效。然后在观察者中订阅你将迭代器存储到它并取消订阅你使用迭代器删除元素。 但是,当然你可以使用std :: vector和std :: remove_if,如其他答案中的建议。

现在关于所有* _ptr的东西。在C++中RAII是你的朋友所以使用它。摆脱公共取消订阅的方法。相反,观察者必须取消订阅它的析构函数。这非常简化:不再锁定弱指针:如果观察者已被删除,则它不在列表中。如果您有多线程应用程序,请不要忘记使用互斥锁保护观察者列表。如果你使用这个设计,那么Observable只需要普通的指向观察者的指针,并且不需要观察者必须被存储。

class Observer { 
public: 
    void subscribe(std::function<void()> unsubscribe) { 
     unsubscribe_ = std::move(unsubscribe); 
    } 

    virtual ~Observer() { 
     unsubscribe_(); 
    } 
private: 
    std::function<void()> unsubscribe_; 
}; 

class Observable { 
public: 
    void subscribe(Observer* observer) { 
     std::lock_guard<std::mutex> lock(observablesMutex_); 
     auto itr = observers_.insert(observers_.end(), observer); 
     observer->subscribe([this, itr]{ 
      std::lock_guard<std::mutex> lock(observablesMutex_); 
      observers_.erase(itr); 
     }); 
    } 

private: 
    std::list<Observer*> observers_; 
    std::mutex observablesMutex_; 
}; 

注意:对于此代码观察者必须始终在Observable之前销毁。


更新:如果你更习惯于C++ lambda表达式你可能会发现,拥有的std ::功能观察员是在许多情况下比有一类特殊的层次更加方便。在这种情况下,您的API可能是这样的:

class Handle { 
public: 
    explicit Handle(std::function<void()> onDestroy) 
     : onDestroy_(std::move(onDestroy)) {} 

    Handle(const Handle&) = delete; 

    Handle(Handle&&) = default; 

    virtual ~Observer() { 
     onDestroy_(); 
    } 
private: 
    std::function<void()> onDestroy_; 
}; 

class Observable { 
public: 
    Handle subscribe(std::function<void()> observer) { 
     std::lock_guard<std::mutex> lock(observablesMutex_); 
     auto itr = observers_.insert(observers_.end(), observer); 
     return {[this, itr]{ 
      std::lock_guard<std::mutex> lock(observablesMutex_); 
      observers_.erase(itr); 
     }}; 
    } 

private: 
    std::list<std::function<void()>> observers_; 
    std::mutex observablesMutex_; 
};