2
votes

I'm trying to create an observer pattern that the subject notifies the observers with different notifications.

Normally in observer pattern implementations you can see only one method called notify where it notifies the observers that something happened and has a kind of inversion where the observer holds the pointer of the subject and ask the subject for something when it's notified.

I'm implementing this a little different, where the subject attaches the observers and notify all of them without the needed of holding the pointer of a subject inside of the observers. For example:

#include <iostream>
#include <vector>

class ObserverEvents
{
public:
    virtual addSomethingOne(int) = 0;
    virtual addSomethingTwo(float) = 0;
};

class Observer : public ObserverEvents
{
public:
    Observer();
    ~Observer();

    virtual addSomethingOne(int) {}
    virtual addSomethingTwo(float) {}
};

class Subject : public ObserverEvents
{
public:
    Subject() {}

    ~Subject()
    {
        for (int i = 0; i < observers.size(); ++i)
        {
            delete observers[i];
        }
    }

    void attach(Observer * observer)
    {
        observers.push_back(observer);
    }

    virtual addSomethingOne(int something)
    {
        for (int i = 0; i < observers.size(); ++i)
        {
            observers[i].addSomethingOne(something);
        }
    }

    virtual addSomethingTwo(float something)
    {
        for (int i = 0; i < observers.size(); ++i)
        {
            observers[i].addSomethingTwo(something);
        }
    }
private:
    std::vector<Observer *> observers;
};

class FooObserver : public Observer
{
public:
    BarObserver() {}
    ~BarObserver() {}

    addSomethingOne(int something)
    {
        // do something with something
    }

    addSomethingTwo(float something)
    {
        // do something with something
    }
};

class BarObserver : public Observer
{
public:
    BizObserver() {}
    ~BizObserver() {}

    addSomethingOne(int something)
    {
        // do something with something
    }

    addSomethingTwo(float something)
    {
        // do something with something
    }
};

int main(int argc, char const * argv[])
{
    Subject subject;
    subject.attach(new FooObserver());
    subject.attach(new BarObserver());

    return 0;
}

The only thing I'm concern is if I'm not broking any design principle like the Open and Closed or something similar, and also if I need to add a new notification I need to implement in all other classes. (which is painful - imagine 10 observers or even more).

I was thinking in make this different, create only one interface and then I can inherit it creating other notifications but there's a problem, how can the observers determine what each different type of notification are?

Example:

#include <iostream>
#include <vector>

class Notifier
{
public:
    Notifier() {}
    ~Notifier() {}

    virtual int getInt() const = 0;
};

class FooNotifier
{
public:
    FooNotifier() {}
    ~FooNotifier() {}

    int getInt() const
    {
        return 10;
    }
};

class BarNotifier
{
public:
    BarNotifier() {}
    ~BarNotifier() {}

    int getInt() const
    {
        return 50;
    }
};

class Observer : public ObserverEvents
{
public:
    Observer();
    ~Observer();

    virtual receive(Notifier *) = 0;
};

class Subject : public ObserverEvents
{
public:
    Subject() {}

    ~Subject()
    {
        for (int i = 0; i < observers.size(); ++i)
        {
            delete observers[i];
        }
    }

    void attach(Observer * observer)
    {
        observers.push_back(observer);
    }

    virtual notify(Notifier * notification)
    {
        for (int i = 0; i < observers.size(); ++i)
        {
            observers[i].receive(notification);
        }
    }
private:
    std::vector<Observer *> observers;
};

class FooObserver : public Observer
{
public:
    BarObserver() {}
    ~BarObserver() {}

    receive(Notifier * notification)
    {
        // ...
    }
};

class BarObserver : public Observer
{
public:
    BizObserver() {}
    ~BizObserver() {}

    receive(Notifier * notification)
    {
        // ...
    }
};

int main(int argc, char const * argv[])
{
    Subject subject;
    subject.attach(new FooObserver());
    subject.attach(new BarObserver());

    subject.notify(new FooNotifier());
    subject.notify(new BarNotifier());

    return 0;
}

The implementation is just an example, I know that I can be using smart pointers, deleting raw pointers and doing things better, but it's JUST an example of implementation.

The problem with this new approach is that I will need to know if Notifier's API in order to use it inside of the Observer, to call getInt.

How can I do this, what is the best way for doing that? How can I send different notifications to the observers?

3
Seems you went the Java way. Java does it their way, because the language is so restricted, not because it is good. We have pointers, lambdas and functors. - Deduplicator
Actually I don't know, I never used Java before. - yayuj

3 Answers

2
votes
template<class C> class event {
    C list;
public:
    template<class... ARGS> inline void add(ARGS&&... args)
    {list.emplace_back(std::forward<ARGS>(args)...);}
    template<class... ARGS> inline void notifyAll(ARGS&&... args)
    {for(auto&& x : list) x(args...);}
};

A small template for an event, which only supports adding listeners and calling them all.

Instantiate like

event<std::vector<void(*)()>> a;
event<std::vector<std::function<void()>>> a;

Or with any other containter of callables.

A useful change would be using a container of std::tuple<tag, callable>, where the tag must be given on adding, and can be used to remove a listener.

template<class F, class T=void*> class event {
    std::vector<std::pair<T, F>> v;
public:
    template<class... ARGS> inline void add(T tag, ARGS&&... args)
    {v.emplace_back(std::piecewise_construct, std::make_tuple(tag),
         std::forward_as_tuple(std::forward<ARGS>(args)...));}
    template<class... ARGS> inline void notifyAll(ARGS&&... args)
    {for(auto&& x : v) x(args...);}
    void remove(T tag) {v.erase(std::remove_if(v.begin(), v.end(),
        [=](const std::pair<T, F>& x){return x.first()==tag;}), v.end());}
};

The latter is instantiated with a functor-type, like std::function<...> or a function-pointer.

2
votes

Variadic templates in the new standard is a perfect way to solve your problem. You can take a look on Observer pattern implementation with templates here: https://github.com/fnz/ObserverManager

1
votes

Listeners need to know the signature they will be called with pretty fundamentally. Bundling sets of messages is useful, but only sometimes worth it. Consider this interface:

template<class...Args>
struct broadcaster {
  template<class F> // F:Args...->vpid
  std::shared_ptr<void> attach( F&& f );
  size_t operator()(Args... args)const;
};

now listeners need only pass an invokable to the broadcaster, then store a shared ptr while they want to listen. When the shared ptr expires, tue message stops being sent.

The sender invokes the broadcaster with the arguments.

template<class...Args>
struct broadcaster {
  template<class F> // F:Args...->vpid
  std::shared_ptr<void> attach( F&& f ){
    auto x = std::make_shared<std::function<void(Args...)>>(std::forward<F>(f)));
    funcs.push_back(x);
    return x;
  }
  size_t operator()(Args... args)const{
    funcs.erase(
      std::remove_if(begin(funcs),end(funcs),
        [](auto&&f){return f.expired();}
      )
      ,end(funcs)
    );
    aito fs=funcs;
    for(auto&&f:fs){
      if(auto f_=f.lock())
        f_(args...);
    }
  }
public:
  mutable std::vector<std::weak_ptr<std::function<void(Args...)>>> funcs;
};

which is a mouthful.

It does mean your two methods are decoupled. But they can be connected with a simple lambda. Store a std::vector<std::shared_ptr<void>> in the listening classes to handle l8fetime management issues.