观察者模式与不同的通知

时间:2014-11-09 22:11:06

标签: c++ design-patterns observer-pattern design-principles

我试图创建一个观察者模式,主体通知观察者不同的通知。

通常在观察者模式实现中,您只能看到一个名为notify的方法,它通知观察者发生了某些事情并且有一种反转,其中观察者握住主体的指针并在询问主体时向主体询问它被通知了。

我实现这一点有点不同,主题附加观察者并通知他们所有人,而不需要将观察者的指针保持在观察者之内。例如:

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

我唯一关心的是,如果我没有像开放式和封闭式等类似的设计原则那样,而且如果我需要添加新的通知,我需要在所有其他类。 (这很痛苦 - 想象10个观察者甚至更多)。

我在考虑让它变得与众不同,只创建一个界面然后我可以继承它创建其他通知但是存在问题,观察者如何确定每种不同类型的通知是什么?

示例:

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

实现只是一个例子,我知道我可以使用智能指针,删除原始指针并做得更好,但它只是一个实现的例子。

这种新方法的问题在于,我需要知道Notifier的API是否可以在观察者中使用它来调用getInt

我该怎么做呢,这样做的最佳方式是什么?如何向观察者发送不同的通知?

3 个答案:

答案 0 :(得分:2)

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

事件的小模板,仅支持添加侦听器并将其全部调用。

实例化

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

或任何其他可赎回的包装。

有用的更改是使用std::tuple<tag, callable>的容器,其中必须在添加时提供标记,并且可以用于删除侦听器。

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

后者使用仿函数类型实例化,如std::function<...>或函数指针。

答案 1 :(得分:2)

新标准中的可变参数模板是解决问题的完美方式。您可以在此处查看使用模板的Observer模式实现:https://github.com/fnz/ObserverManager

答案 2 :(得分:1)

听众需要知道他们将从根本上调用的签名。捆绑消息集是有用的,但有时只值得。考虑一下这个界面:

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

现在,侦听器只需要将一个invokable传递给广播公司,然后在他们想要收听时存储一个共享的ptr。当共享ptr到期时,tue消息停止发送。

发件人使用参数调用广播公司。

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

这是一口。

这确实意味着你的两种方法是分离的。但它们可以用简单的lambda连接起来。在监听类中存储std::vector<std::shared_ptr<void>>以处理l8fetime管理问题。

相关问题