2013-07-29 4 views
9

私は1つのAnimationクラスを持っています。アニメーションでPlayPauseStopというイベントのオブザーバーが必要です。私はこの問題のための2つの解決策を見つけましたが、私は何を選ぶべきか分かりません。C++でObserverパターンを実装する方法

  1. 使用3つの純粋仮想関数(OnPlay()OnPause()OnStop())とのシンプルなインターフェイスを作成し、そのアニメーションのクラスオブジェクトに渡す

  2. を::信号または類似した何かを高め、すべてのイベントのためのコールバックを登録このインタフェースを実装します。

すべての方法にメリットとデメリットがあります。

  • 私が持っていない私は、コールバックとして任意のメンバ関数/フリー機能を使用することができます。1.

    • ため

      利点:私は、私がこれまでに見つかったものを列挙してみます私はそれら

    • 同じオブジェクトが複数のアニメーションのためのオブザーバーとして使用することができ、すべての気にしない場合はアニメーションクラスから余分なパラメータを渡すことなく、すべての3つの機能を実装するために
    .

    • ための10の

      欠点は、私はすべてのコールバック

    • 私は使用された場所を見つけるのは難しいだろう、後に新しいイベントを追加したい場合は(コンパイラの呼び出し可能オブジェクトを作成する必要があります新しいイベントを実装または無視するように強制することはできません)。
    • 何とか変な構文(私はstd :: bind/boost :: bindをどこでも使用しなければなりません)。

    メリット2.

    • ため、私は、コンパイラが(多分空の)を実装するために私を強制されますアニメーション/オブザーバー・インターフェース・クラスに新しいイベントを追加します場合は建設
    • を理解しやすいです新しい機能私は一つだけ
    • 同じオブジェクトを使用します場合でも、(多分空の)3つの機能を実装する必要があります。2.

      • ため

      短所が異なるためにオブザーバーとして使用することはできませんアニメーション(IDまたは何か)からいくつかの余分なパラメータを送信することなくアニメーション。

    • フリー機能は使用できません。

    何を使用するか教えてもらえますか?あなたの経験から、この問題にはより良いものがあります。最初の問題が解決されているか、または2番目の問題のコードをわかりやすく理解していますか?あなたは私に両方の方法または他の解決法に他の長所/短所を教えてもらえますか?

  • +6

    に慣れます1 "となる。 –

    +0

    'std :: bind'を使用するのが奇妙な構文(特に' OnWhatever'仮想関数とのインタフェースに比べて)であれば、あなたの言語を再考するべきです。 –

    +0

    @ChristianRau私のためではありませんが、私はコードベースで働く唯一の人ではありません。 – Felics

    答えて

    3

    まず、「バインディング」がコンパイル時にわかっているかどうかを知ることは有益でしょう。もしそうなら、私はあなたにポリシークラスを調べるよう勧めます。

    それ以外は、2つのソリューションの組み合わせ、つまりインターフェイスアプローチを使用して、シグナル/フリー機能のリレイヤーとして機能する1つのインターフェイスを実装します。このようにしてデフォルトの動作を持たせることができます。インターフェイス全体を実装するカスタムオブジェクトを追加し、基本的に2つのアプローチの利点と柔軟性を得ることができます。

    ここに提案されたアプローチの基本的な例があります。私はそれが助けになることを願っています。

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

    hereを試すことができます。この例では特に、ポリシークラスを使用しているため、インターフェイスは「正式に定義されていません。また、setPlayHandleのようにインターフェイスを「充実」することができます。しかし、実行時バインディングと同様のことができます。

    0

    私はどちらも使うことができると思いますが、それは必要に応じて決まります。私はこのパターンの両方を使うコードをいくつか持っています。 onSomething()(onMouseButton()、onKey()、onDragStart()など多くの関数がありますが、コールバックもあります。私はいくつかの動作を実装する必要があるが、オブジェクトのクラス全体に対して、私はonSomething()アプローチを使用します。しかし、同じクラスのオブジェクトがたくさんあるにもかかわらず、その一部だけが拡張機能を必要とする場合、コールバックは完璧な方法です。 結果がfalseの場合、onSomething()メソッド(boolを返す)を使用しようとするディスパッチコードがいくつか存在します - コールバックが定義されている場合はチェックがあり、それは実行されます。

    1

    もっとも単純なおもちゃの例以外は、Boost.Signals2が私の意見では優れた解決策になります。それはうまく設計され、十分にテストされ、十分に文書化されています。ホイールを再考することは、宿題タイプの演習には適していますが、プロダクションコードには適していません。例えば。あなた自身のオブザーバをスレッドセーフにすることは、適切かつ効率的になることは自明ではありません。具体的にあなたは私にはないC++ 11のラムダの代わりに、名前の関数オブジェクトを作成するか(とにかくほとんどの使用のための本当に複雑ではありません)boost::bind構文

  • を使用することができますあなたの列挙された欠点

    • を議論

      未使用のイベントについてのあなたの点をよく理解しています。スロットからの信号を照会して切断するには、かなり進化したconnnection managementを行うことができます。

    TL; DR:については「欠点の大部分を除去C++(私はあなたがそれで質問をタグ付けするので、あなたが、使用することができると仮定)11ラムダでBoost.Signals2

  • +0

    私は、Boost.SignalsまたはBoost.Signals2のいずれかがパフォーマンス面で「効率的」であると言っていることを躊躇します。しかし、私はそれらが完全に文書化され、維持されていることに同意します。 @OP:実際には、これを使って両方の方法を試してみてください。最初の賛否両論が実際に期待通りに成立しているかどうかを知ることができます。シグナルライブラリについては、定期的にメンテナンスされているものを選択し、適切なドキュメントを用意し、スレッド安全性のためにスレッドセーフであるライブラリを選択します。 – ApEk

    +0

    @ApEk私はBoostライブラリが(ほぼ)すべてのBoostライブラリが効率的であると言います*彼らが提供しているものを*提供していますが、それらを使わない唯一の理由は時にはあなたが必要以上に提供することができるからです。使用しません。 – TemplateRex

    関連する問題