Sunday

Software Design Patterns: Observer pattern using C++

Under behavioral patterns

Why?
Define a one-to-many dependency among objects
so that when one observable object changes state,
all the observer objects depended on the observable
are notified and updated automatically.

Code:
#include <iostream>
#include <forward_list>
#include <unordered_map>
#include <memory> // shared_ptr

// event message types or view-controller input message types
// or observer message types
enum class EventMessageTypes {PLAYSOUND, HANDLEPHYSICS, LOG};

// Abstract class or interface class
class AbstractObserver
{
public:
    virtual ~AbstractObserver() {}

    virtual void OnNotify()=0;
    virtual std::string GetName() const=0;
};

// event or view
class SoundEvent : public AbstractObserver
{
public:
    explicit SoundEvent(const std::string& name) : mName(name)
    {
    }
    void OnNotify() override
    {
        std::cout << mName << " sound event did something" << std::endl;
    }
    std::string GetName() const override
    {
        return mName;
    }
private:
    std::string mName;
};

class PhysicsEvent : public AbstractObserver
{
public:
    explicit PhysicsEvent(const std::string& name) : mName(name)
    {
    }
    void OnNotify() override
    {
        std::cout << mName << " physics event did something" << std::endl;
    }
    std::string GetName() const override
    {
        return mName;
    }
private:
    std::string mName;
};

class LogEvent : public AbstractObserver
{
public:
    explicit LogEvent(const std::string& name) : mName(name)
    {
    }
    void OnNotify() override
    {
        std::cout << mName << " log event did something" << std::endl;
    }
    std::string GetName() const override
    {
        return mName;
    }
private:
    std::string mName;
};

class AbstractObservable
{
public:
    AbstractObservable() {};
    virtual ~AbstractObservable() {}

    virtual void AddObserver(EventMessageTypes eventMessage,
                             std::shared_ptr<AbstractObserver>& observer)
    {
        auto it = mObservers.find(eventMessage);
        if( it==mObservers.end() )
        {
            mObservers[eventMessage] = ObserversList();
        }
        mObservers[eventMessage].push_front(observer);
    }

    virtual void RemoveObserver(EventMessageTypes eventMessage,
                                std::shared_ptr<AbstractObserver>& observer)
    {
        auto it = mObservers.find(eventMessage);
        if(it != mObservers.end())
        {
            ObserversList& observersList = mObservers[eventMessage];
            for(ObserversList::iterator li = observersList.begin();
                li != observersList.end(); )
            {
                if( (*li) == observer )
                {
                    std::cout << "Goodbye " << observer->GetName() << std::endl;
                    observersList.remove(observer);
                    break;
                }
                else
                {
                    ++li;
                }
            }
        }
    }

    virtual void NotifyAll()
    {
        for(ObserversMap::iterator it = mObservers.begin();
            it!=mObservers.end(); ++it)
        {
            for(auto& o : mObservers[it->first])
            {
                o->OnNotify();
            }
        }
    }

    virtual void Notify(EventMessageTypes message)
    {
        for(auto& o: mObservers[message])
        {
            o->OnNotify();
        }
    }

private:
    // Two typedefs here
    // first for the forward_list of observers
    // second for the unordered_map of EventMessageTypes map key
    // and ObserversList map value
    typedef std::forward_list<std::shared_ptr<AbstractObserver>>  ObserversList;
    typedef std::unordered_map<EventMessageTypes, ObserversList>  ObserversMap;

    ObserversMap mObservers;
};

class ModelChildObservable : public AbstractObservable
{
public:
    EventMessageTypes eventMessage;
};

int main()
{
    ModelChildObservable modelChildObservable;

    std::shared_ptr<AbstractObserver> observable1a = std::make_shared<SoundEvent>("observable1a");
    std::shared_ptr<AbstractObserver> observable1b = std::make_shared<SoundEvent>("observable1b");
    std::shared_ptr<AbstractObserver> observable2 = std::make_shared<PhysicsEvent>("observable2");
    std::shared_ptr<AbstractObserver> observable3 = std::make_shared<LogEvent>("observable3");

    modelChildObservable.AddObserver(EventMessageTypes::PLAYSOUND, observable1a);
    modelChildObservable.AddObserver(EventMessageTypes::PLAYSOUND, observable1b);
    modelChildObservable.AddObserver(EventMessageTypes::HANDLEPHYSICS, observable2);
    modelChildObservable.AddObserver(EventMessageTypes::LOG, observable3);

    modelChildObservable.NotifyAll();
    std::cout << std::endl;

    modelChildObservable.Notify(EventMessageTypes::PLAYSOUND);
    std::cout << std::endl;

    modelChildObservable.RemoveObserver(EventMessageTypes::PLAYSOUND, observable1a);
    std::cout << std::endl;

    modelChildObservable.Notify(EventMessageTypes::PLAYSOUND);
    std::cout << std::endl;

    return 0;
}

Program output:
observable3 log event did something
observable1b sound event did something
observable1a sound event did something
observable2 physics event did something

observable1b sound event did something
observable1a sound event did something

Goodbye observable1a

observable1b sound event did something

No comments:

If a hater attacked your age and not the goodness of you

Whether young or old, I've always been known what endures. I've known the very idea of people that were all created equal and deserv...