2

私は C++ を初めて使用し、クラスでオーバーヘッドを生成するところまで来ました。QTcpSocket があり、そこからメッセージを読み取り、MessageJoin、MessagePart、MessageUserData などのオブジェクトを作成します。これらのオブジェクトをクライアントに送信して表示します (+ UI の更新を行います)。

ここに私の問題があります。いくつかの設計手法をテストしましたが、どれもそれほど素晴らしいものではありません。

  • シグナル/スロット接続でメッセージ オブジェクトの各パラメータをクライアントに渡す - オーバーヘッドは小さいが見栄えはよくない
  • Message-Type (messageJoinReceived、messageNoticeReceived など) ごとにメソッドを作成します。
  • 1 つのメソッドを作成し、dynamic_cast を使用して各クラスをキャストし、テストします

理解を深めるために、自分の dynamic_cast バージョンを追加しました。言われているように、コードは見苦しく使い物になりません。私の質問は次のとおりです。

  • (a)dynamic_castでそれを行うより良い方法はありますか
  • このような問題を解決する別の方法 (たとえば設計パターン) はありますか? クラスにメソッドを追加して、型などを返すかもしれません
  • ビジターパターンについて読みました。このパターンは、Getter/Setter メソッドの動的オブジェクト タイプ専用ですか?

いくつかの補足事項

  • RTTIが使える
  • 速度は大した問題ではありません。クリーンでわかりやすいコードがより重要
  • 私は Qt を使用しており、qobject_cast と signal/slots を使用する可能性があります

これが私のコードです(Pastebin-Link):

// Default class - contains the complete message (untouched)
class Message
{
public:
    QString virtual getRawMessage() { return dataRawMessage; }
protected:
    QString dataRawMessage;
};

// Join class - cointains the name of the joined user and the channel
class MessageJoin : public Message
{
public:
    MessageJoin(const QString &rawmessage, const QString &channel, const QString &user)
    {
        dataRawMessage = rawmessage;
        dataChannel = channel;
        dataUser = user;
    }

    QString getChannel() { return dataChannel; }
    QString getUser(){ return dataUser; }

private:
    QString dataChannel;
    QString dataUser;
};

// Notice class - contains a notification message
class MessageNotice : public Message
{
public:
    MessageNotice(const QString &rawmessage, const QString &text)
    {
        dataRawMessage = rawmessage;
        dataText = text;
    }

    QString getText() { return dataText;}

private:
    QString dataText;
};

// Client code - print message and update UI
void Client::messageReceived(Message *message)
{
    if(message)
    {
        MessageJoin *messagejoin;
        MessagePart *messagepart;
        MessageNotice *messagenotice;
        if((messagejoin = dynamic_cast<MessageJoin *>(message)) != 0)
        {
            qDebug() << messagejoin->getUser() << " joined " << messagejoin->getChannel();
            // Update UI: Add user
        }
        else if((messagenotice = dynamic_cast<MessageNotice *>(message)) != 0)
        {
            qDebug() << messagenotice->getText();
            // Update UI: Display message
        }
        else
        {
            qDebug() << "Cannot cast message object";
        }
        delete message; // Message was allocated in the library and is not used anymore
    }
}
4

3 に答える 3

2

訪問者のパターンはうまく適合する可能性があります。

class Message
{
public:
    QString virtual getRawMessage() { return dataRawMessage; }

    virtual void accept(Client& visitor) = 0;

protected:
    QString dataRawMessage;
};

// Join class - cointains the name of the joined user and the channel
class MessageJoin : public Message
{
public:
    MessageJoin(const QString &rawmessage, const QString &channel, const QString &user)
    {
        dataRawMessage = rawmessage;
        dataChannel = channel;
        dataUser = user;
    }

    QString getChannel() { return dataChannel; }
    QString getUser(){ return dataUser; }

    void accept(Client& visitor) override
    {
          visitor.visit(*this);
    }

private:
    QString dataChannel;
    QString dataUser;
};

// Notice class - contains a notification message
class MessageNotice : public Message
{
public:
    MessageNotice(const QString &rawmessage, const QString &text)
    {
        dataRawMessage = rawmessage;
        dataText = text;
    }

    QString getText() { return dataText;}

    void accept(Client& visitor) override
    {
          visitor.visit(*this);
    }

private:
    QString dataText;
};

void Client::visit(MessageJoin& msg)
{
    qDebug() << msg.getUser() << " joined " << msg.getChannel();
    // Update UI: Add user
}

void Client::visit(MessageNotice& msg)
{
    qDebug() << msg.getText();
    // Update UI: Display message
}

// Client code - print message and update UI
void Client::messageReceived(Message *message)
{
    if(message)
    {
        message->visit(this);
        delete message; // Message was allocated in the library and is not used anymore
    }
}
于 2013-07-09T11:56:37.203 に答える
2

これは式の問題と非常によく似ており、新しいメッセージとそれらを処理する新しい方法を追加する場合、キャストを回避する方法はありません。ただし、実行時に必要な要素をより見やすくラップすることは、それほど難しいことではありません。を使用して、メッセージ タイプから対応するハンドラへのマップを作成するだけtypeidです。


#include <functional>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>

typedef std::function<void(Message *)> handler_t;

typedef std::unordered_map<
    std::type_index,
    handler_t> handlers_map_t;

template <class T, class HandlerType>
handler_t make_handler(HandlerType handler)
{
    return [=] (Message *message) { handler(static_cast<T *>(message)); };
}

template <class T, class HandlerType>
void register_handler(
    handlers_map_t &handlers_map,
    HandlerType handler)
{
    handlers_map[typeid(T)] = make_handler<T>(handler);
}

void handle(handlers_map_t const &handlers_map, Base *message)
{
    handlers_map_t::const_iterator i = handlers_map.find(typeid(*message));
    if (i != handlers_map.end())
    {
        (i->second)(message);
    }
    else
    {
        qDebug() << "Cannot handle message object";
    }
}

次に、特定のメッセージ タイプのハンドラを登録します。


handlers_map_t handlers_map;

register_handler<MessageJoin>(
    handlers_map,
    [] (MessageJoin  *message)
    {
        qDebug() << message->getUser() << " joined " << message->getChannel();
        // Update UI: Add user
    });

register_handler<MessageNotice>(
    handlers_map,
    [] (MessageNotice *message)
    {
        qDebug() << message->getText();
        // Update UI: Display message
    });

そして、メッセージを処理できるようになりました:


// simple test
Message* messages[] =
{
    new MessageJoin(...),
    new MessageNotice(...),
    new MessageNotice(...),
    new MessagePart(...),
};

for (auto m: messages)
{
    handle(handlers_map, m);
    delete m;
}

確かに、単一のメッセージに対して複数のハンドラーを持つことができるように、QT またはブーストシグナル/スロットを使用して、ハンドラーを再利用可能なクラスにラップするなど、いくつかの改善を加えたいと思うかもしれませんが、コアのアイデアは同じです。

于 2013-07-09T13:32:03.307 に答える