在C++中创建观察者设计模式的好方法

kad*_*ina 2 c++ design-patterns observer-pattern

我试图在C++中实现观察者设计模式,如下所示

#include <iostream>
#include <vector>

using namespace std;

class observer
{
    public:
        observer() = default;
        ~observer() = default;

        virtual void notify() = 0;
};

class subject
{
    vector <observer *> vec;

    public:
        subject() = default;
        ~subject() = default;

        void _register(observer *obj)
        {
            vec.push_back(obj);
        }

        void unregister(observer *obj)
        {
            int i;
            for(i = 0; i < vec.size(); i++)
            {
                if(vec[i] == obj)
                {
                    cout << "found elem. unregistering" << endl;
                    vec.erase(vec.begin() + i);
                    break;
                }
            }

            if(i == vec.size())
            {
                cout << "elem not found to unregister" << endl;
            }
        }

        void notify()
        {
            vector <observer *>::iterator it = vec.begin();
            while(it != vec.end())
            {
                (*it)->notify();
                it ++;
            }
        }
};

class obsone : public observer
{
    void notify()
    {
        cout << "in obsone notify" << endl;
    }
};

class obstwo : public observer
{
    void notify()
    {
        cout << "in obstwo notify" << endl;
    }
};

int main()
{
    subject sub;

    obsone *one = new obsone();
    obstwo *two = new obstwo();

    sub._register(one);   
    sub._register(two);
    sub.notify();

    sub.unregister(one);
    sub.notify();

    //delete two;
    //sub.notify();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我正在明确地注册主题的对象.这是正确的做法,还是只需要通过观察者类注册.上述方法有问题吗?

Che*_*Alf 7

这是一个使用function回调集合中的lambda和对象进行回调的示例.

细节可能差别很大!所以,这段代码不是"那种"方式,只是你的代码以一种特定的方式重写,具有无数的可能性.但它有望展示现代C++中的一般概念.

#include <iostream>
#include <functional>               // std::function
#include <stdint.h>                 // uint64_t
#include <unordered_map>            // std::unordered_map
#include <utility>                  // std::move    
#include <vector>                   // std::vector
using namespace std;

namespace my
{
    using Callback = function<void()>;
    template< class Key, class Value > using Map_ = unordered_map<Key, Value>;

    class Subject
    {
    public:
        enum Id: uint64_t {};

    private:
        Map_<uint64_t, Callback> m_callbacks;

        static auto id_value()
            -> uint64_t&
        {
            static uint64_t the_id;
            return the_id;
        }

    public:
        auto add_listener( Callback cb )
            -> Id
        {
            const auto id = Id( ++id_value() );
            m_callbacks.emplace( id, move( cb ) );
            return id;
        }

        auto remove_listener( const Id id )
            -> bool
        {
            const auto it = m_callbacks.find( id );
            if( it == m_callbacks.end() )
            {
                return false;
            }
            m_callbacks.erase( it );
            return true;
        }

        void notify_all() const
        {
            for( const auto& pair : m_callbacks )
            {
                pair.second();
            }
        }
    };
}

struct Observer_1
{
    void notify() { cout << "Observer_1::notify() called." << endl; }
};

struct Observer_2
{
    void notify() { cout << "Observer_2::notify() called." << endl; }
};

auto main()
    -> int
{
    my::Subject     subject;
    Observer_1      one;
    Observer_2      two;

    using Id = my::Subject::Id;
    const Id listener_id_1 = subject.add_listener( [&]{ one.notify(); } );
    const Id listener_id_2 = subject.add_listener( [&]{ two.notify(); } );

    cout << "After adding two listeners:" << endl;
    subject.notify_all();
    cout << endl;

    subject.remove_listener( listener_id_1 )
        and (cout << "Removed listener 1." << endl)
        or (cout << "Did not find registration of listener 1." << endl);
    cout << endl;
    cout << "After removing or attempting to remove listener 1:" << endl;
    subject.notify_all();
}
Run Code Online (Sandbox Code Playgroud)