使用weak_ptr实现Observer模式

时间:2016-09-15 16:49:25

标签: c++ c++11 observer-pattern observers weak-ptr

到目前为止我所拥有的是:

Observer.h

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition


screen_manager = ScreenManager(transition=SlideTransition())


class RootScreen(Screen):

    settings_button = Button(text='Settings')
    settings_button.bind(on_press=screen_manager.switch_to(SettingsScreen())


class SettingsScreen(Screen):
    pass


class WifiApp(App):

    def build(self):
        screen_manager.add_widget(RootScreen(name='main'))
        screen_manager.add_widget(SettingsScreen(name='settings'))
        return screen_manager


if __name__ == '__main__':
    WifiApp().run()

Observer.cpp

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;
};

(de / constructors在这里实现但是空的,所以我把它们排除了)

我所坚持的是如何实施取消订阅程序。我遇到了擦除 - 删除 - 结尾的习语,但我明白它不起作用&#34;开箱即用&#34;我如何设置我的Observable。如何检查观察者向量中的weak_ptr元素,以便我可以删除所需的Observer?

我也在寻找关于我的Un / Subscribe程序的参数类型应该是什么的一些建议。使用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(); } } } std::shared_ptr<Observer>&会不会更好,因为我们不会修改它?

我真的不希望Observable拥有他们的Observers,因为它似乎背叛了模式的意图,当然不是我想要构建最终将利用该模式的项目的其余部分。也就是说,我正在考虑增加一层安全/自动化,让Observers存储一个weak_ptr的镜像向量。然后,一个观察员可以取消订阅它所订阅的所有Observable,并且一个Observable可以在观察它的每个观察者身上抹去对它自己的反向引用。显然,在这种情况下,这两个班级将成为朋友。

2 个答案:

答案 0 :(得分:3)

您可以将std::remove_ifObject[] values一起使用,如下所示:

std::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() ); } 作为observer传递。

答案 1 :(得分:2)

  

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

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

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

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 ++ lambdas,你可能会发现在很多情况下使用std :: function作为观察者比使用特殊的类层次结构更方便。在这种情况下,您可以使用以下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_;
};