14

私は依存性注入を見ています。利点はわかりますが、それが作成する構文に問題があります。私はこの例を持っています

public class BusinessProducts
{
   IDataContext _dx;

   BusinessProducts(IDataContext dx)
   {
      _dx = dx;
   }

   public List<Product> GetProducts()
   {
    return dx.GetProducts();
   }
}

書きたくないのが問題

BusinessProducts bp = new BusinessProducts(dataContextImplementation);

私は書き続けるだろう

BusinessProducts bp = new BusinessProducts();

最初の選択肢が不自然に感じるからです。製品を取得するために BusinessProduct が「依存」するものを知りたくありません。また、コードが読みにくくなると感じています。

オブジェクトを作成するための元の構文を維持したいが、単体テスト時に依存関係を偽造できるようにしたいので、このアプローチに代わるものはありますか?

私は c# でコーディングしていますが、他の言語からの代替も大歓迎です

4

11 に答える 11

10

コンテキストにファクトリを使用して注入し、提供されたファクトリが null の場合に適切なデフォルトを提供します。これには 2 つの理由があります。まず、データ コンテキストをスコープ オブジェクトの作業単位として使用するため、データ コンテキストを維持するのではなく、必要なときに作成できるようにする必要があります。第二に、私は主にテスト容易性を高めるために DI を使用しており、デカップリングは二次的な考慮事項にすぎません。

したがって、私のビジネス製品クラスは次のようになります。

public class BusinessProducts
{
     private IDataContextFactory DataContextFactory { get; set; }  // my interface

     public BusinessProducts() : this(null) {}

     public BusinessProducts( IDataContextFactory factory )
     {
          this.DataContext = factory ?? new BusinessProductsDataContextFactory();
     }

     public void DoSomething()
     {
          using (DataContext dc = this.DataContextFactory().CreateDataContext())
          {
             ...
          }
     }

これに代わる方法は、ファクトリ プロパティをパブリックに設定可能にし、プロパティを設定して代替ファクトリを注入することです。どちらの方法でも、null コンストラクターを維持したい場合は、デフォルトを提供する必要があります。

于 2008-12-11T12:32:26.703 に答える
6

私は通常、ソリッドインスタンス(またはIoCによって作成されたインスタンス)を使用する空のコンストラクターと、DIを使用するコンストラクターを使用します。すなわち

public class BusinessProducts
{
   IDataContext _dx;

   BusinessProducts()
   {
      _dx = new SolidDataContext();
   }

   BusinessProducts(IDataContext dx)
   {
      _dx = dx;
   }
}

このようにして、DI を使用して単体テスト テストで既定のインスタンスをオーバーライドできます。

于 2008-12-11T12:36:10.207 に答える
6

工場を作ることができます。DI コンテナーは、実行時ではなく、セットアップ時に発生する配線に最適です (これはケースのようです)。ファクトリは、必要なプラグ可能性と、使用する必要がある場所の数に応じて、さまざまな方法で実装できます。

于 2008-12-11T12:18:44.470 に答える
4

あなたの気持ちは有効ですが、見当違いです。

依存性注入パターンは、制御の反転の原則を直接適用したものです。

これは、クラスが消費する他のクラスのインスタンスを制御する代わりに、その関係が反転され、依存関係が提供されることを意味します。

そのため、クラスはコンストラクターの引数またはプロパティを介して依存関係を自然に公開します。この構造に軽蔑を示すことは、パターンを真に理解していないことを意味します。

于 2008-12-11T15:17:41.333 に答える
3

ここには 2 つの異なるケースがあります。

本番コードでは、決して書くことはありません

new BusinessProducts(dataContextImplementation)

通常、依存性注入は完全なオブジェクト階層を作成するためです。これは、依存性注入パターンの「バイラル」な性質であり、サービスの作成を完全に制御する傾向があります。

単体テスト コードでは、通常、これを自分で作成しますが、モック オブジェクトまたは dataContextImplementation のスタブ実装を提供することがよくあります。したがって、通常、後続の依存関係が多数ないオブジェクトを注入します。

于 2008-12-11T12:15:25.287 に答える
1

通常、フレームワーク自体には、オブジェクト ツリー全体を構築するためのロジックがあります。たとえば、代わりに

new SomeObjectO(diContext)

次のようにフレームワークを呼び出します。

DIFramework.GetNew<SomeObjectO>();

また

DIFramework.Get<SomeObject>();

DI とそのプロセスについて学びたい場合は、もう 1 つの興味深いフレームワークとして、Microsoft の Unity および Object Builder プロジェクトを参照してください。

于 2008-12-11T12:28:37.210 に答える
1

こんな感じの貧乏人のDIというテクニックがあります

public class BusinessProducts
{
   IDataContext _dx;

   BusinessProducts() : this(new DataContext()) {}

   BusinessProducts(IDataContext dx)
   {
      _dx = dx;
   }

   public List<Product> GetProducts()
   {
    return dx.GetProducts();
   }
}

これは実装に結び付くので理想的ではありませんが、分離されたコードへの良い足がかりになります。これは @tvanfosson に似ていますが、より単純です。

私はウィンザーの推薦に二番目です

于 2008-12-11T14:07:32.550 に答える
1

私のコードは Microsoft Unity を参照しますが、すべての DI フレームワークにかなり適用できると確信しています。DI を正しく使用している場合、new BusinessObject(new dataContext) を呼び出す必要はありません。DI 関連付けがすべてを処理します。

Unity によって完全に読み込まれた Model View Presenter Web サイトを実行するために使用するコードを貼り付けるので、私の例は少し長くなります。(完全なソースが必要な場合は、私のブログをチェックして、Assembla SVN サーバーからダウンロードしてください)

コンテナをロードします(私が好むように、または構成を使用してコードにすることができます)

protected void Application_Start(object sender, EventArgs e)
{
    Application.GetContainer()
        // presenters / controllers are per request                 
        .RegisterType<IEmployeeController, EmployeeController>(new ContextLifetimeManager<IEmployeeController>())

        //Data Providers are Per session                
        .RegisterType<IEmployeeDataProvider, EmployeeDataProvider>(new SessionLifetimeManager<IEmployeeDataProvider>())

        //Session Factory is life time
        .RegisterType<INHibernateSessionManager, NHibernateSessionManager>(new ContainerControlledLifetimeManager());
}

カスタム HTTP モジュールは、OnPreRequest 呼び出し中に各ページの Unity BuildUp メソッドを呼び出します。

private static void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
    var handler = HttpContext.Current.Handler;
    HttpContext.Current.Application.GetContainer().BuildUp(handler.GetType(), handler);

    // User Controls are ready to be built up after the page initialization is complete
    var page = HttpContext.Current.Handler as Page;
    if (page != null)
    {
        page.InitComplete += OnPageInitComplete;
    }
}

[Dependency] 属性で装飾されたページ コンテナー プレゼンター

public partial class Employees : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    [Dependency]
    public EmployeePresenter Presenter
    {
        set
        {
            _presenter = value;
            _presenter.View = this;
        }
    }
}

InjectionConstructor メソッドを使用したプレゼンター

public class EmployeePresenter : Presenter<IEmployeeView>
{
    private readonly IEmployeeController _controller;

    [InjectionConstructor]
    }
    public EmployeePresenter(IEmployeeController controller)
    {
        _controller = controller;
}

コントローラーが追従する

public class EmployeeController : IEmployeeController
{
    private readonly IEmployeeDataProvider _provider;

    [InjectionConstructor]
    public EmployeeController(IEmployeeDataProvider DataProvider)
    {
        _provider = DataProvider;
    }
}

プロバイダと同じ

public class EmployeeController : IEmployeeController
{
    private readonly IEmployeeDataProvider _provider;

    [InjectionConstructor]
    public EmployeeController(IEmployeeDataProvider DataProvider)
    {
        _provider = DataProvider;
    }
}

最後に、通常のコンストラクターのみを含むセッション マネージャーです。

public class NHibernateSessionManager : INHibernateSessionManager
{   
    private readonly ISessionFactory _sessionFactory;

    public NHibernateSessionManager()
    {            
        _sessionFactory = GetSessionFactory();
    }
}

ページ リクエストが開始されると、HttpModule によってページ上で BuildUp() メソッドが呼び出されます。次に、Unity は Dependency 属性でマークされたプロパティを確認し、コンテナをチェックして、内部に EmployeePresenter オブジェクトが存在するかどうかを確認します。

コンテナーにはそのようなオブジェクトがないため、EmployeePresenter を作成しようとします。Presenter 内に表示されるクラスを作成するための検査では、IEmployeeController を注入する必要があるコンストラクターが必要です。コンテナには実際にはコントローラーのマネージャーがあるため、ページ要求の開始時に存在しないインスタンスがコンテナーに存在するかどうかを確認し、コントローラーをインスタンス化します。

その後、Unity は、コントローラーが IEmployeeDataProvider を挿入する必要があることを確認し、プロバイダーがセッション マネージャーを挿入する必要があるポイントに最終的に到達するまで、このプロセスを続行します。セッション マネージャーはインジェクションを行う必要がなくなったため、Unity はセッション マネージャーのインスタンスを作成し、指定された ContainerLifeTimeManager のコンテナーに保存し、それをプロバイダーにインジェクトしてそのインスタンスを保存します。ページの EmployeePresenter 依存関係。

于 2008-12-11T14:21:45.423 に答える
1

http://springframework.net/http://structuremap.sourceforge.net/Default.htmは、おそらく .NET ベースの言語で最もよく使用される DI フレームワークであり、どちらも必要なことを実行します。

于 2008-12-11T12:10:37.030 に答える
1

このインスタンスをコンストラクターに注入したくない場合は、互換性のあるお気に入りの .NET 依存性注入フレームワークでCommonServiceLocatorを使用してみてください。これにより、次のようなコードを記述できます。

public class BusinessProducts
{
   IDataContext _dx;

   BusinessProducts()
   {
      _dx = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IDataContext>();
   }

   public List<Product> GetProducts()
   {
    return dx.GetProducts();
   }
}

ただし、これは、依存性注入フレームワークを使用していることを知っているほとんどの人が期待するものではないことに注意してください。依存性注入フレームワークを使用して、すべてのオブジェクトを作成できるようにする方がはるかに一般的だと思います。

BusinessProducts bp = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<BusinessProducts>();

依存性注入フレームワーク パスを回避したい場合は、おそらくファクトリを使用するのが最善の方法です。

于 2008-12-11T12:55:31.527 に答える
0

IoCのウィンザーも見ることができます。

于 2008-12-11T12:16:18.780 に答える