web-dev-qa-db-ja.com

UIコードの分離?

私のアプリケーションには、ボタンのクリックやメニューの選択などのユーザーインターフェイスイベントに応答していくつかのアクションを実行するいくつかのイベントハンドラーがあります。これらのイベントハンドラーのコードは、たとえば次のようになります。

void MyDialog::OnCommandLoadFile()
{
    char strFilter[] = { "Text Files (*.txt)|*.txt|All Files (*.*)|*.*||" };

    CFileDialog fileDlg(TRUE, ".txt", NULL, 0, strFilter);

    if( fileDlg.DoModal() == IDOK )
    {
        const std::string path = fileDlg.GetPathName();

        std::vector<std::string> urls;
        int maxNumberOfUrls = settings_->Get<int>(Settings::MaxNumberOfUrls);
        UrlProvider *urlProvider = new FileUrlProvider(path);
        urlProvider->GetUrls(urls, maxNumberOfUrls);

        webMonitoringService_->MonitorUrls(urls);

        DisplayMessage("URLs loaded.");
    }

}

これは私のイベントハンドラーの典型です。私はモノリシックイベントハンドラーをコーディングしませんが、さまざまな部分を「接着」します。通常、UIが参照するビジネスロジック/サービスクラスがいくつかあり、イベントハンドラーでこれらの1つ以上を呼び出して、目的のタスクを実行します。

これはまだあまりにも結合されているのでしょうか?これをコマンドラインアプリに変換したい場合は、UIコードからビットと断片を抽出する必要があります。上記の例では、複合アクションが実行されているため、次のように見えるはずです。

void MyDialog::OnCommandLoadFile()
{
    char strFilter[] = { "Text Files (*.txt)|*.txt|All Files (*.*)|*.*||" };

    CFileDialog fileDlg(TRUE, ".txt", NULL, 0, strFilter);

    if( fileDlg.DoModal() == IDOK )
    {
        const std::string path = fileDlg.GetPathName();

        application->MonitorUrlsFromFile(path);

        DisplayMessage("URLs loaded.");
    }

}

そうですか?上記のコードでは、「アプリケーション」は何でしょうか?コントローラー?これは正しい抽象化ですか? UIハンドラーでいくつかのタスクを実行する必要があるときはいつでも、それは常に1行である必要があります(テキストボックスの内外でデータを取得することは別として)。 )?

さらに、実際に複合アクションを抽象化する必要がある場合、複合抽象内からエラーメッセージをUIに戻す方法を教えてください。例外階層?エラーコード付きの単一例外?

[〜#〜]更新[〜#〜]

私は有望に見えるコマンドパターンを見ています。

更新2

結局、私は受け入れられた回答のHumble Dialog Boxペーパーで説明されているようにコードをリファクタリングすることに決めました。私の理解では、これはモデルビュープレゼンター(MVP)タイプのパターンであり、具体的にはパッシブビューです。リファクタリングは順調に進んでいます。メッセージボックスやファイルオープンダイアログの表示やイベントタイマーの処理などに関して、パターンの完全な適用について理解しているところが多少ありますが、全体としてコードには満足しています。密結合は私を悩ませていました。

私はこの「バンドル」も見つけました: パターンとプラクティスWebクライアント開発者向けガイダンス:モデルビュープレゼンター(MVP)バンドル サンプルMVPコードがあります。現在の実装はデスクトップですが、これはWebプロジェクトですが、MVPの優れている点は、それが部分的に問題ではないことです。私が得たものの1つは、サービスレイヤーオブジェクトをプレゼンタークラス内で新しくするのではなく、プレゼンターに依存性注入することでした。

7
User

GUIをビジネスロジックからより適切に分離する方法を学びたい場合は、Michael Feathersの記事 "The humble dialog box" を読むことをお勧めします。

これに精通していて、さらに詳細が必要な場合は、「MVC」のすべての重要なバリエーションを説明するブログシリーズ「Build Your Own CAB」を以下に示します。

http://codebetter.com/jeremymiller/2007/07/26/the-build-your-own-cab-series-table-of-contents/

編集:あなたのサンプルコードによれば: "MVP"を使用する場合、それは "神オブジェクト" applicationを配置しないことを意味しますMonitorUrlsFromFileを配置しますが、コントローラー(または "プレゼンター ")クラスMyDialogController最初に。プログラムが20個のダイアログを受け取り、それらすべてがハンドラーロジックをクラスapplicationに配置する場合を考えてください。これは、5000行以上のコードを持つクラスになりがちですが、これは明らかに大きすぎます。

MVPアプローチでは、MonitorUrlsFromFileを別のより一般的な場所にリファクタリングするだけで、複数のダイアログからこの関数が必要で、それを再利用したい場合にのみ使用します。それでも、これをapplicationクラスにしません。1つのユーティリティクラスに、URLまたはURL監視用のより良いグループ関数を作成します。

2
Doc Brown

特にMonitorUrlsFromFile()が重複したコードである場合、リファクタリングは問題ありません。そのリファクタリングを正当化するために、「UIからの分離」の感度は必要ありません。それ自体のメリットを正当化することができます。

エラー伝搬スキームは、いくつかの要因に依存します。通常、BackgroundWorkerオブジェクトまたはスレッドなどを使用して、長時間実行されるバックグラウンドタスクからステータスイベントを受信しますが、現在取り組んでいるプロジェクトには、UIに表示されるいくつかのビジネスルール検証があります。私のアプローチはそこで変わるでしょう。

基本的なエラー伝播を処理する最も簡単な方法は、Main()メソッドまたは処理ループを呼び出すものでトップレベルの例外ハンドラーを提供することです。

5
Robert Harvey