web-dev-qa-db-ja.com

メッセージングライブラリC ++のアーキテクチャ

バイナリメッセージのセットを送受信し、それらを解析するライブラリがあります。

これまでのところ、デザインに継承を使用しています。

class BaseMsg 
{
    // init msg from rx'd binary stream
    virtual bool fromBinary(std::vector<char> b) = 0;
    // write msg content to binary stream to tx
    virtual std::vector<char> toBinary() const = 0;

   MsgType type;
};

class MsgA : public BaseMsg 
{
    MsgA()
    {
        type = MsgType::A;
    }

    bool fromBinary(std::vector<char> b)
    {
        // parse binary stream here and init members
    }

    std::vector<char> toBinary() const
    {
        // encode members to binary stream here
    }
private:
    std::string freetext;
    // more members
};

class MsgB : public BaseMsg
{
    MsgB()
    {
        type = MsgType::B;
    }

    bool fromBinary(std::vector<char> b)
    {
        // parse binary stream here and init members
    }

    std::vector<char> toBinary() const
    {
        // encode members to binary stream here
    }
private:
    double latitude;
    double longitude;
    // more members
};

さらに、メッセージングクラスMessengerがあります。これには、送受信するメソッドがあります。

bool sendMessage(std::unique_ptr<BaseMsg> msg)
{
    std::vector<char> bin = msg->toBinary();
    // send bin
}

std::unique_ptr<BaseMsg> receive(std::vector<char> receivedBinaryStream)
{
    if (/*check in binary stream, if msg is MsgA*/)
    {
        auto msg = std::make_unique<MsgA>();
       msg->fromBinary(receivedBinaryStream);
    return std::move(msg);
    }
if (/*check in binary stream, if msg is MsgB*/)
    {
        auto msg = std::make_unique<MsgB>();
       msg->fromBinary(receivedBinaryStream);
    return std::move(msg);
    }
}

これは非常にうまく機能しますが、ライブラリを使用すると、基本クラスから特定のサブクラスにたくさんキャストすることになります。これは特に、メッセージを受信し、アプリケーションでメッセージを使用した後です。

// 
auto msg = messenger.receive(binStream);

if (msg->type == MsgType::A)
{
    // cast to MsgA and continue processing
}
else if (msg->type == MsgType::B)
{
    // cast to MsgB and continue processing
}

したがって、問題は、最後のコードサンプルでのキャストを回避する代替アーキテクチャ/デザインがある場合です。

問題は実装の詳細ではなく、現在継承を使用しているよりも優れた設計があるかどうかです。

編集:

メッセージにはさまざまなパラメーターがあります。 MsgAはフリーテキストを提供し、MsgBはオブジェクトの位置情報などを提供します。

したがって、アプリケーションによってメッセージが消費されると、メッセージはそのサブクラスにキャストされ、タイプ固有のパラメーターを取得します。

3
Simon

まず、ハンドラからのメッセージを分離する

MessageTypeAでバイナリとの間の独自の解析を処理する代わりに、これを行う仲介者MessageHandlerTypeAを紹介します。このようにして、メッセージ自体をプレーンオルのデータ型に、または少なくとも-単純なデータコンテナーに削減し、実際のハンドラーを毎回新しいハンドラーを作成する代わりにインスタンス化しておくことができます。

次に、メッセージ受信コードで、バイナリデータを渡すメッセージタイプごとにMessageHandlerを登録できます。これは、登録関数を公開して新しいメッセージを任意に解釈できるようにするため、ライブラリの外部から拡張できます。

次に、コールバックを使用を使用して、さらにメッセージを処理します。

receiveの代わりに、使用しているプロセスアプローチの代わりに、MessageHandlerにメッセージを適切なmessageタイプにデコードしてから、その特定の情報を知る必要があるコードを呼び出します。メッセージ。したがって、呼び出しコードは、メッセージを解釈するのではなく、単にコールバックをハンドラーに登録し、メッセージが受信されるたびに解釈されたメッセージを含むコールバックを受信します。

3
Jack Aidley