2

MVP パターン、特にすべてのクラスのインスタンスを作成する場所に問題があります。現在、これらはすべて program.cs ファイルに作成されています。これは機能しますが、これは設計が不十分であることを知りました。これがどのように構成されるべきかについて誰かが私にいくつかの指針を与えることができれば幸いです。

internal static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    private static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        var browser = new BrowserWindow();
        var helper = new MainPresenterHelper();
        var userInterface = new MainForm();
        var entity = new UserInputEntity();

        var readerWriter = new ReaderWriter();
        var manager = new IOManager(readerWriter);
        var verif = new VerificationManager(manager);
        var entityVerification = new EntityVerification(verif);

        var logger = new Logger(entity, readerWriter, true);
        var verifyRow = new VerifyRow(entity, logger);
        var verification = new VerificationOfDataTypes(entity, logger, verifyRow, new Status(), readerWriter);

        var verify = new CsvFileVerification(entityVerification, verification, logger);

        var cts = new CancellationTokenSource();
        var source = new CancellationTokenSourceWrapper(cts);

        var presenter = new MainPresenter(userInterface, browser, helper, entity, verify, source);

        Application.Run(userInterface);
    }
}
4

1 に答える 1

6

「なぜこの投稿はこんなに長いのか!?」それは、私がC#のたまり場でハンスや他の人たちとチャットしていて、彼が何を学びたいのかを考えていたからです。


ハンス、

WinFormsでMVPを使用することは完全に実行可能であり、古いWindows CE 6デバイス( winformスタイル)アプリで完全に使用でき、完全なCQRSサイクルも含まれています。ともかく。

これからリストするものはあなたのプログラムには必要ありませんが、開発者としては非常に役立つと思います。(私の意見では)本質的に学ぶ必要があるのは、オブジェクトの存続期間と依存関係の階層です。これらのものはおそらく固有名を持っていますが、うまくいけばそれは十分に説明的です。

したがって、アプリケーションの起動時に効果的に実行しているのは、すべてをインスタンス化することです。あなたのプログラムがそれらすべてを使用しているのは本当かもしれませんが、本当にこれらすべてをこのように1つの場所でインスタンス化する必要がありますか?他に何もないとしても、それはこのクラスにあまりにも多くの責任を負わせています。アプリケーションを実行するとき、最初に実行したいのは、私が想定しているUIを表示することです。したがって、理想的には、この最初の方法に存在する必要があるのはそれだけです。

インスタンス化したさまざまなオブジェクトを次のオブジェクトに順番に渡していることがわかります。これは良いスタートです。つまり、基本的に、依存関係ツリーが1行ずつ入力済みになっていることを意味します。ただし、これを続行する前に、依存関係を再考する必要があります。基本的に、あなたが目指しているのは、このクラスが動作するために必要なものです。それ以上考えないでください(つまり、このクラスにXが必要な場合、XにはYが必要なので、Yを取得する必要があります)。各クラスの依存関係の最初のレイヤーを見つけたいだけです。

私のアドバイスは、現在行っているのとは異なり、依存関係コンテナーに入れてコンストラクターインジェクションを使用することです。通常、このプロセスは、実際の実装からオブジェクトのアクションとプロパティを抽象化することから開始します。

すなわち

public interface IDoStuff
{
    string AProperty { get; set; }

    bool SomeMethod(int anArgument);
}

public class TheImplementation : IDoStuff
{
    public string AProperty { get; set; }

    public bool SomeMethod(int anArgument)
    {
        return false;
    }

    public void AnotherMethod()
    {
        this.AProperty = string.Empty
    }
}

したがって、一見すると、これらすべてのポイントは何であるか疑問に思うかもしれません。確かに、これはプログラムを不必要に複雑にするだけです。重要なのは、消費者から実装の詳細を抽象化することです。

目指す代わりに:

public class MyConsumer
{
    private readonly TheImplementation myDependency;

    public MyConsumer(TheImplementation myDependency)
    {
        this.myDependency = myDependency
    }

    public void ExposedMethod()
    {
        this.myDependency.SomeMethod(14)
    }
 }

インターフェースのみを参照する消費者を対象としています。

public class MyConsumer
{
    private readonly IDoStuff myDependency;

    public MyConsumer(IDoStuff myDependency)
    {
        this.myDependency = myDependency
    }

    public void ExposedMethod()
    {
        this.myDependency.SomeMethod(14)
    }
 }

これがあなたに与えるのは柔軟性です!つまり、コンシューマーに触れることなく、実装に変更を加えたり、実装を完全に交換したりすることができます。

また、これらのインターフェイスの実装を偽のバージョン(モックと呼ばれる)と交換できるため、テスト駆動設計にも適しています。これにより、アプリケーションのコンポーネント(コンシューマー)を完全に分離してテストできます。テストが高速化され、テストが向上します。データベースクエリを引き起こすプレゼンターをテストしたり、IIS Expressを起動して、将来のクラスで必要になる可能性のあるWCFサービスを実行したりする必要はありません。

その後、依存性注入は非常に簡単になります。アプリケーションのスタートアップがコンポジションルートになり、実際の実装のインターフェイスへのバインドを処理します。

バインディングが完了すると、これまでに使用したすべての依存関係コンテナー(Ninjectが私の個人的なお気に入りであり、Unityがそれに続きます)は、依存関係ツリーを自動的に検出し、ルートノードを要求するだけでオブジェクトグラフ全体をインスタンス化できました。話が多すぎてコードが少なすぎる=)例を次に示します。

[STAThread]
private static void Main()
{
    // assuming ninject
    IKernel kernel = new StandardKernel();

    // some people hate self binds, but you may find this easier than 
    // creating interfaces for all your existing classes
    kernel.Bind<BrowserWindow>().ToSelf(); 
    kernel.Bind<MainPresenterHelper>().ToSelf();
    kernel.Bind<MainForm>().ToSelf();
    kernel.Bind<UserInputEntity>().ToSelf();

    // this is where we use the splitting implementation from interface
    kernel.Bind<IReaderWriter>().To<ReaderWriter>();
    kernel.Bind<IIOManager>().To<IOManager>();
    kernel.Bind<IVerificationManager>().To<VerificationManager>();
    // .... etc

    //If you do them all correctly, you can simply have the following line

    Application.Run(kernel.Get<MainForm>());
}

これがあなたを助けるのに役立つことを願っていますか?そうでない場合は、まあ、これを書くのに何年もかかりました... =)

于 2012-10-11T12:00:09.263 に答える