4

GUI を作成するときにロジックからビューを分離するという問題に関して投稿する質問がたくさんあります。
以下は、「謙虚なダイアログ」アプローチを使用して、ラベルとボタンを持つ単純なダイアログに対して行うことの最小限の例です。ボタンを押すと、ラベルにテキストが表示されます。私は快適な C++ と Qt を使用しましたが、他のすべての聴衆が読むことができると思います。
いずれにせよ、言語の選択による副作用の可能性に興味があります (これを導入したいプロジェクトで C++ を使用しています)。

class IView {
public:
    IView(){}
    virtual ~IView(){}

    virtual void showResult(const QString &text)=0;
};

class Presenter {
public:
    Presenter(IView *view){
        m_View = view;
    }
    ~Presenter(){}

    void buttonPressed(){
        QString text;
        // Evaluate text
        m_View->showResult(text);        
    }

private:
    IView *m_View;
}

// Multiple inheritance. Is this OK?
class MyView : public QDialog, public IView {
public:
    MyView(){
        m_Presenter = new Presenter(this);
        m_Button = new QPushbutton(this);
        m_Label = new QLabel(this);

        // Ui event handled inside view but then directly
        // propagated to the Presenter
        connect(m_Button,SIGNAL(clicked()),this,SLOT(buttonPressed()));
    }
    ~MyView(){
        delete m_Presenter;
        // Qt will automatically delete m_Button and m_Label;
    }

    void showResult(const QString &text){
        m_Label->setText(text);
    }

protected slots:
    void buttonPressed(){
        m_Presenter->buttonPressed();
    }

private:
    Presenter *m_Presenter;
    QPushbutton *m_Button;
    QLabel *m_Label;
}

class TestView : public IView {
public:
    TestView(){}
    ~TestView(){}

    void showResult(const QString &text){
        m_LabelText = text;
    }
    QString getResult(){
        return m_LabelText;
    }

private:
    QString m_LabelText;
}

// Test code
TestView view;
Presenter presenter(&view);
presenter.buttonPressed();
EXPECT_EQ(view.getResult(),"Expected Result");

// Procuction code
MyView view;
view.show();

これは、Feathers による Humble dialog の最初の作業に従って得たものです。Fowler の実装から得られるアプローチは、MyView のコンストラクターで Presenter クラスのインスタンスを作成することを避け、代わりにそれをパラメーターとして渡すことです。これにより、製品コードはテスト コードのように見えます。私は個人的にここで紹介するアプローチが好きです。

そう、

  • 多重継承で使用することを意図していますか (MyView クラスの私のコメントを参照してください)?
  • イベントをプレゼンターに直接伝達する必要がありますか、それともそれぞれのプレゼンター アクションを呼び出すビューで処理する必要がありますか?
  • 他に注意点はありますか?
4

3 に答える 3

2

QObjectsで多重継承を行う場合、継承リストの最初のクラスはQObjectから派生したクラスである必要があります。これは、クラスに信号とスロットを追加する場合にのみ厳密に必要ですが、とにかく良い習慣です。したがって、クラス宣言は次のようになります。

クラスMyView:public IView、public QDialog {

代わりにこれになる必要があります:

クラスMyView:public QDialog、public IView {

繰り返しになりますが、これは「MyV​​iew」にスロットまたはシグナルを追加した場合にのみあなたを噛みます。

それ以外は、ダイアログには膨大な量のやり過ぎがありますが、これは素晴らしい実装だと思います。:)

QtでFowlerのMVPを使用していますが、問題なく動作しています。物事はよりテスト可能です(nUnitスタイル)が、それはもう少し複雑なIMOです。

于 2009-11-01T05:48:04.743 に答える
1

私は通常、C# Winforms の UI に同じパターンを使用します。

ここでは実際に多重継承を行っているわけではありません。継承元のクラスの 1 つは単なる空のインターフェイスです。ここでの唯一の問題は、C++ がクラスとインターフェースの違いを認識していないことです。

このようにビュー内にプレゼンターを作成しても問題ないと思います。これは最もテストしやすい設計ではありませんが、謙虚なダイアログを使用している場合はテストするものが何もないため、おそらくビューをテストするつもりはありません。または、テスト目的でプレゼンターを作成する代わりに、プレゼンターを注入する 2 番目のコンストラクターを追加することで、「貧弱な DI」を実行することもできます。

C# では通常、プレゼンターをまったく知らず、プレゼンターを呼び出す代わりにイベントをスローするだけのビューがあります。これにより、デカップリングが追加されますが、ほとんどの状況ではやり過ぎになる可能性があります。

全体として、これは適切な実装です。UI を備えた C++ アプリを作成する必要がある場合は、この投稿を確認します。

于 2008-10-27T10:45:53.377 に答える
1

私には大丈夫に見えます。しかし、IView-Interface では QString を使用しません。可能であれば、プレゼンテーションに依存しないタイプを使用してください。そうすれば、プログラム ロジックやテストに影響を与えることなく、GUI ツールキットを変更できます。QString と std::string または char* の間で変換するのが本当に面倒な場合にのみ、QString を保持してください (わかりません...)。

于 2008-10-27T11:38:10.383 に答える