如何在C++中实现Observer模式

Mir*_*pas 9 c++ design-patterns c++11

我有一个动画类.我需要有一些观察员Play,PauseStop在动画的事件.我找到了2个解决这个问题的方法,但我不知道该选择什么.

  1. 使用boost :: signals或类似的东西,并为每个事件注册回调

  2. 使用3个纯虚函数(OnPlay(),)创建一个简单的接口OnPause(),OnStop()并传递给实现此接口的Animation类对象.

每种方法都有优点和缺点.我将尝试列举到目前为止我发现的那些:

优点1.

  • 我可以使用任何成员函数/自由函数作为回调
  • 如果我不关心所有这些功能,我不必实现所有3个功能
  • 相同的对象可以用作多个动画的观察者,而无需从Animation类传递额外的参数

缺点1.

  • 我必须为每个回调创建一个可调用对象
  • 如果我想稍后添加一个新事件,将很难找到它所使用的位置(编译器无法强制我实现或忽略新事件).
  • 不知何故奇怪的语法(我必须使用std :: bind/boost :: bind无处不在).

优点2.

  • 易于理解的结构
  • 如果我将在Animation/Observer接口类中添加一个新事件,编译器将强制我实现(可能为空)新函数.

2的缺点.

  • 即使我只使用一个函数,我也必须实现3个函数
  • 如果不从动画(ID或其他内容)发送一些额外参数,则不能将相同对象用作不同动画的观察者.
  • 无法使用免费功能.

你能告诉我用什么吗?根据您的经验,对于这个问题有什么好处 - 从第一个方面获得免费或从第二个方面清晰易懂的代码?您能否告诉我这两种方法或其他解决方案的其他优点/缺点?

Ste*_*sca 3

首先,了解“绑定”在编译时是否已知会很有用。如果是这样,我建议您研究一下政策课程。

除此之外,我会混合使用两种解决方案,即使用接口方法并实现一个接口,该接口充当信号/自由函数的中继器。通过这种方式,您可以拥有默认行为,您可以添加实现整个界面的自定义对象,并且基本上具有两种方法的优点以及很大的灵活性。

这是所提出方法的基本示例,希望对您有所帮助。

#include <functional>

using namespace std;

template <class ObserverPolicy>
class Animation : public ObserverPolicy{

};

class MonolithicObserver{
    public:
    void play(){
        state = playing;
    }
    void pause(){
        if(playing == state)
            state = stopped;
    }
    void stop(){
        state = stopped;
    }
    private:
    enum {playing, paused, stopped} state;
};

struct doNothing{
    static void play(){}
    static void pause(){}
    static void stop(){}
};

struct throwException{
    class noPlay{};
    class noPause{};
    class noStop{};
    static void play(){
        throw noPlay();
    }
    static void pause(){
        throw noPause();
    }
    static void stop(){
        throw noStop();
    }
};

template <class DefaultPolicy = doNothing>
class FreeFunctionObserver{
    public:
    void play(){
        if(playHandle)
            playHandle();
        else
            DefaultPolicy::play();
    }
    void pause(){
        if(pauseHandle)
            pauseHandle();
        else
            DefaultPolicy::pause();
    }
    void stop(){
        if(stopHandle)
            stopHandle();
        else
            DefaultPolicy::stop();
    }
    void setPlayHandle(std::function<void(void)> p){
        playHandle = p;
    }
    void setPauseHandle(std::function<void(void)> p){
        pauseHandle = p;
    }
    void setStopHandle(std::function<void(void)> p){
        stopHandle = p;
    }
    private:
    std::function<void(void)> playHandle;
    std::function<void(void)> pauseHandle;
    std::function<void(void)> stopHandle;
};

void play(){}
void pause(){}
void stop(){}

int main(){
    Animation<FreeFunctionObserver<> > affo;
    affo.setPlayHandle(play);
    affo.setPauseHandle(pause);
    affo.setStopHandle(stop);
    affo.play();
    affo.pause();
    affo.stop();

    Animation<FreeFunctionObserver<throwException> > affot;
    try{
        affot.play();
    }
    catch(throwException::noPlay&){}

    Animation<MonolithicObserver> amo;
    amo.play();
    amo.pause();
    amo.stop();
}
Run Code Online (Sandbox Code Playgroud)

你可以在这里尝试一下。特别是,此示例使用策略类(因此没有“正式”定义接口,并且您可以“丰富”接口,就像使用 setPlayHandle 所做的那样)。但是,您也可以对运行时绑定执行类似的操作。