6

わかりました、皆さん、たとえば、前にお話ししたこのフォームがあります。DockPanel には DockContent が 1 つだけあります

その時以来、私が行った編集の1つは、ユーザーが左ペインのプロジェクトをクリックしたときに表示される各プロジェクトの単語です。左ペインの 1 つのトライアドを簡単に作成できました。

プロジェクト プレゼンター、プロジェクト ビュー、プロジェクト モデルがあります。以下は、それぞれのインターフェイスです。

interface IProjectsModel
{
    void AttachPresenter(IProjectsModelObserver observer);
    System.Collections.Generic.List<Project> projects { get; }
    Project selectedProject { get; set; }
}

public interface IProjectsViewObserver
{
    void UserChangedSelectedProject(Project project);
}
public interface IProjectsModelObserver
{
    void SelectedProjectChanged(Project project);
}

public interface IProjectsView : IView
{
    List<Project> projects { set; }
    Project project { set; }

    void AttachPresenter(IProjectsViewObserver presenter);
}

そのため、現在、右側のペインに新しい MVP トライアドをもう 1 つ作成することを考えています。しかし、これは主な問題ではありません。私が実行した主な問題は、上記で説明した MVP トライアド間の通信プロセスをどのように作成できるかということです。

この状況では、モデル コーディネーターをプロジェクトに導入する必要があるという Web 上の記事を読みました。

みんな私の質問は次のとおりです。

  1. トライアドを 1 つではなく 2 つ使用するのは正しいですか?
  2. 2 つのトライアドを相互に調整するにはどうすればよいですか?
  3. 提案/提案/オファー/アドバイス/ヒントなど、私にとって役立つと思われる他の多くのものは非常に高く評価されます!

よろしくお願いします。ご清聴ありがとうございました!

4

4 に答える 4

2

プレゼンター間で調整するために、ユーザーが別のプロジェクトを選択したときに、MainFormクラスにテキストを実装IProjectsViewObserverして右側のドック ペインに設定することができます。例えば:

class MainForm : IProjectsViewObserver
{
    void UserChangedSelectedProject(Project project)
    {
          rightDockPane.setText(project.getText());
    }
}

この動作をテストしたい場合は、別のクラスを作成できます。

class DockPaneProjectUpdater : IProjectsViewObserver

ただし、この場合はコードが非常に単純であるため、これは不要です。

匿名メソッドを使用してこれを行う簡単な方法を調べることができますが、私はそこに役立つ十分な C# を知りません。

トライアドを 1 つではなく 2 つ使用するのは正しいですか?

はい、ドックとペインは別個のコンポーネントであるため、別個の MVP トライアドが必要になる可能性があります。コードを再利用可能でテスト可能にすることで、MVP トライアドの粒度 (小ささ) を決定できます。

2 つのトライアドを相互に調整するにはどうすればよいですか?

サブプレゼンターを接続し、トライアド間の動作を調整できるウィンドウレベルのプレゼンターを作成します。window-presenter を にしてIProjectsViewObserver、 に作用させることができますDockContent。または、動作をモジュール化/単体テストしたい場合は、トライアド間通信用に別のプレゼンター クラス (多くの場合匿名クラス) を作成できます。

提案/提案/オファー/アドバイス/ヒントなど、私にとって役立つと思われる他の多くのものは非常に高く評価されます!

MVP とpresenter-firstに関するウィキペディアとオンラインの記事を読んでください。MVP の最大の利点は、テスト容易性と動作 (プレゼンター) のモジュール性であるため、コードを再利用できる場合は、単体テスト(多くの場合、モック/DI フレームワークを使用) と動作を MVP トライアドにリファクタリングすることで、それらを利用していることを確認してください。

MVP パターンを効果的に適用する方法を理解するには時間がかかるので、しばらくお待ちください。

于 2012-05-11T20:17:07.727 に答える
1

ギャレットのアドバイスに従ってください。お互いを認識し、それに応じて行動できるマスタープレゼンターによってサブプレゼンターを作成します。最も簡単な方法は、両方のペインのサブプレゼンターにプロパティを作成することです。

あなたはJavaの世界から来ていますか?:) C#には、オブザーバーパターンの独自の実装があります:events。ご覧ください。Javaのような追加のインターフェースはもう必要ありません。

更新:書き始めたとき、気が変わった。私の意見では、プレゼンター間でコミュニケーションをとる最良の方法は、共有モデルを使用することです。

class SelectedProjectChangedEventArgs : EventArgs
{
    public Project SelectedProject {get;set;}
}

class Project
{

}

interface IReadOnlyModel
{
    Project SelectedProject {get;} 
    event EventHandler<SelectedProjectChangedEventArgs> SelectedProjectChanged;
}

interface IWritableModel
{
    Project SelectedProject {get;set;} 
    IList<Project> Projects {get;}
}

class Model : IReadOnlyModel, IWritableModel
{
    public Project SelectedProject {get;set;} 
    public event EventHandler<SelectedProjectChangedEventArgs> SelectedProjectChanged;
    public IList<Project> Projects {get;set;}
}

class ProjectsListPresenter
{
    readonly IWritableModel _model;

    public ProjectsListPresenter(IWritableModel model)
    {
        _model = model;
    }

    public void ChangeSelectedProject(Project project)
    {
        _model.SelectedProject = project;
    }
}

class ProjectDetailsPresenter
{
    readonly IReadOnlyModel _model;

    public ProjectDetailsPresenter(IReadOnlyModel model)
    {
        _model = model;
        _model.SelectedProjectChanged += ModelSelectedProjectChanged;
    }

    void ModelSelectedProjectChanged(object sender, SelectedProjectChangedEventArgs e)
    {
        //update right pane
    }
}


class WholeFormPresenter
{
    public ProjectDetailsPresenter DetailsPresenter {get;private set;}
    public ProjectsListPresenter ListPresenter {get;private set;}

    public WholeFormPresenter(Model model)
    {
        DetailsPresenter = new ProjectDetailsPresenter(model);
        ListPresenter = new ProjectsListPresenter(model);
    }
}

class WholeForm
{
    ListBox _projectsListControl;
    Panel _detailsPanel;
    public WholeForm()
    {
        var presenter = new WholeFormPresenter(new Model());
        _projectsListControl.Presenter = presenter.ListPresenter;
        _detailsPanel.Presenter = presenter.DetailsPresenter;
    }
}

クラス/インターフェイス名が完全ではないことは承知していますが、アイデアが明確であることを願っています。

于 2012-05-13T17:49:10.717 に答える
1

トライアド間の相互作用の完全に薄く抽象的な方法を実現したい場合は、Mediatorパターンを使用できます。「メッセージ」クラス/構造を作成すると、1人のプレゼンターがそれをサブスクライブし、別のプレゼンターがそれを送信します。

このアプローチはイベントのサブスクリプションに非常に似ていますが、両側のプレゼンターがメッセージの受信者が存在するかどうかさえ知ることができないため、より抽象化されます。1つのプロセスのコンテキストで作業しているため、コールバックデリゲートであっても、メッセージで必要なものをすべて渡すことができます。メディエーターパターンの実装の良い例は、MVVMLightフレームワークにあります。そこではメッセンジャーと呼ばれています。テストを見てみましょう。

個人的には、共通の親や作成者がいないダイアログウィンドウ間で情報を送信する必要がある場合は、メディエーターを使用しました。そのシナリオでは、物事を単純化するのに役立ちました。

于 2012-05-26T15:51:43.010 に答える
0

「リスナー」を登録して新しいイベントを発行できる EventHub という静的クラスを作成しました。私は C# の初心者なので、このクラスを改善する方法があると確信しています。

public enum EventType
{
    // Your event types
}

public static class EventHub
{
    static Dictionary<EventType, List<Delegate>> _eventHandlers;

    // Use this to fire events with input
    public static void Publish( EventType eventType, object data )
    {
        // Fire event handler if found
        if( _eventHandlers.ContainsKey( eventType ) )
        {
            _invokeHandlers( _eventHandlers[ eventType ], data );
        }
    }

    static void _invokeHandlers(List<Delegate> list, object data)
    {
        if( !(list != null && list.Count > 0) )
            return;

        foreach( var handler in list )
            handler.DynamicInvoke( data );
    }

    // Use this to register new "listeners"
    public static void Register( EventType eventType, Delegate handler )
    {
        // Init dictionary
        if( _eventHandlers == null )
            _eventHandlers = new Dictionary<EventType, List<Delegate>>();

        // Add handler
        if( _eventHandlers.ContainsKey( eventType ) )
            _eventHandlers[ eventType ].Add( handler );
        else
            _eventHandlers.Add( eventType, new List<Delegate> { handler });
    }

    // Use this to remove "listeners"
    public static void Dismiss( EventType eventType, Delegate handler )
    {
        // Nothing to remove
        if( _eventHandlers == null || _eventHandlers.Count == 0 )
            return;

        // Remove handler
        if( _eventHandlers.ContainsKey( eventType ) && _eventHandlers[ eventType ].Count > 0 )
            _eventHandlers[ eventType ].Remove( handler );

        // Remove Key if no handlers left
        if( _eventHandlers[ eventType ].Count == 0 )
            _eventHandlers.Remove( eventType );
    }
}

使用法:

void MyHandler(object data) { /*...*/ }
EventHub.Register( EventType.MyType, MyHandler );
//...
EventHub.Publish( EventType.MyType, "I am data, I can be anything" );
于 2016-09-26T08:00:47.910 に答える