0

GUI を実装するとき、私はしばしばジレンマに遭遇します。いくつかの子ウィジェットを持つメイン ウィンドウを持つ単純なウィジェット階層があるとします。子ウィジェットはイベント (たとえば、ユーザー入力から発生) を処理する必要がありますか、それとも処理を親ウィジェットに委譲する必要がありますか? 簡単な例を次に示します。閉じるボタンがクリックされたときにウィンドウを閉じる必要があります。窓を閉めるのは誰?ウィンドウはボタン イベントに反応して閉じますか? または、ボタンはウィンドウの Close メソッドを呼び出しますか? 私が使用しているフレームワークは、子ウィジェットが親ウィジェットにイベントを発生させることが好ましい方法であることを強く示唆しており、親ウィジェットは適切に処理する必要があるため、ウィンドウは次のように閉じます (C++ 風の擬似コード):

void ChildWidget::OnClick()
{
    GetParentWidget()->OnEvent(CreateClickEvent(*this));
}

void ParentWidget::OnEvent(Event& _event)
{
    if (_event.Type() == EventTypeClick && _event.Id() == "close_button")
    {
        Close();
    }
    else if ...
        ...
}

しかし、それは単一責任の原則を破り、親ウィジェットが過大な作業を行うことにつながるのではないかと心配しています。代わりに、次のように、イベントを処理するために必要なオブジェクトを子ウィジェットに与えることができます。

class CloseButtonWidget : public Widget
{
public:
    CloseButtonWidget(Widget& widget_to_close)
    : WidgetToClose_(&widget_to_close)
    {}

    void OnClick()
    {
        WidgetToClose->Close();
    }       

private:
    Widget* WidgetToClose_;
};

私が見ることができる欠点は、多くの場合、そのような自己処理ウィジェット用の新しいクラスを作成する必要があることですが、とにかく子ウィジェット用の新しいクラスを作成する必要があることが多いため、多くの場合、それはそれほど問題ではありません.

私が考えることができる別の方法は、子ウィジェットに次のようなコールバックを与えることです:

auto close_widget = new Widget();
// Suppose parent_widget is in scope
auto handler = CreateHandler(EventTypeClick, [parent_widget](){ parent_widget->Close(); }); 
close_widget->SetHandler(handler);

問題は、子ウィジェットからのイベントを処理するのに最適な場所はどこかということです。上記の方法の長所と短所は何ですか?たぶん、これを行うための他のより良い方法がありますか?「親ウィジェットがすべてを処理する」シナリオの長所を聞くことに特に興味があります。私が使用しているフレームワークの好みの方法と癖を維持しながら、より「哲学的な」方法で質問に対処する回答が必要であることを明確にしたいと思います(より良い設計とは何かなど)重要。

4

1 に答える 1

0

IMPO 最初の方法はボトルネックです。OS/フレームワークは、「フェッチ」プロセスを実行する必要がないように、ウィジェット固有のイベントを提供するために多くの仕事をします。そして、単一のイベント ハンドラーを介してすべてのイベントを処理しますか? ばかげた時間の無駄のように聞こえます。

一連のイベント (または一連のウィジェットから発生した同じイベント) を同じ方法で処理する場合は、同じイベント ハンドラーを使用します。

たとえば、戦艦のようなゲーム: ボタンのグリッドがあり、グリッドのすべてのボタンのクリック イベントを同じ方法で処理します (1 つのボタンと他のボタンの唯一の違いはその座標です)。

すべてのウィジェット イベント (または同じウィジェットの異なるイベント) で異なる操作を行う必要がある場合は、ウィジェット固有のイベントを独自のハンドラーで処理します。

お気づきのように、特定のハンドラーの問題はその実装にあります。コードを重複させます。しかし、Java を使用していないと考えてください。1行の操作のみを記述するために20行に収まる擬似関数デリゲート(別名内部クラス)ではなく、ファーストクラスの関数(生の関数ポインターまたはファンクターを介して)があります。あなたは C++ を使用しており、テンプレート、ラムダ、ファンクター、およびその他の便利なツールを使用して、単純で明確な 1 行のコードだけで特定のハンドラーを記述できます :)

ご覧のとおり、可能な限り/単純な場合は常に、3 番目の方法を使用することを好みます。

于 2013-06-12T16:25:44.827 に答える