2017-07-07 12 views
0

だから、私は依存性注入についての特定のヒントとその使い方を探しています。DIコンストラクタ注入ニートネス

基本的に私はNinject(およびNinject MVCパッケージ)を使用するMVC Webサイトを持っています。ですから、MVCページを作成するときに、コントローラでコンストラクタインジェクションを使用します。これは大丈夫ですが、IMOは少し醜いですが、私が気に入らない主なことは、注入されたリポジトリをすべて他のクラスに渡さなければならないことです.6リポジトリ8リポジトリに渡す必要があるようです静的メソッドまたはオブジェクトコンストラクタ。

私のページの中には、ほとんどすべてのリポジトリで作業しなければならないことは言うまでもありません。そのため、コントローラのコンストラクターは膨大なものになります。

私のコードをあまりにも乱雑にしない他のオプションはありますか?私は本当に、単一の「設定」オブジェクトとしてそれらを渡すことはしません。問題を別のコード行に移動するだけです。

また、コンソール/デスクトップアプリケーションでも同じクラスライブラリを使用します。私はクラスライブラリでDependencyResolver.Currentを使用する考えが好きですが、誰もがこれがアンチパターンであり、コンストラクタインジェクションを使用すべきだと誰もが言います。

多分MyProjectDIContextクラスがあります。このクラスにはコントローラのコンストラクタで注入された型を移入し、必要に応じてすべてのメソッドにコンテキストを渡すことができますか?

私は答えのビットを探しました。私はよく合うものを見つけることができません。

+0

他のクラスをninjectで登録してから、これらのクラスにレポジトリを直接注入することはできませんか? – BoeseB

+0

私がDependencyResolverを使わないで覚えているのであれば、 "Turtles all the way down"という問題があるので、これを行うことはできません。 DIクラスがコントローラのコンストラクタに注入された場合、解決されたクラスを使用したいクラスに渡す必要があります。いくつかのケースでは、私はそれを正確に行っていますが、それはいくつかのより複雑なプロセスの議論の大規模なリストにつながります。 DependencyResolverはそれを回避しますが、リゾルバクラスに縛られているように見えます。 – Dwiea

+0

私はまだコンソール/デスクトップアプリケーションでDIを把握しようとしています。そのため、DIがどのようにうまく機能しているか正確にはわかりません。例えば、私は "new MyClass()"に行き、それが適切な注入されたクラスを通過すると期待することはできませんか? – Dwiea

答えて

0

私はこれを行う方法を見つけたようですが、臭いのようには見えません!

たとえば、MVCを使用している場合、コンポジットを介してリポジトリを取得し、それが依存関係を解決する唯一の方法であると考えていました。

再利用可能なコードのセクションをIoCフレンドリなインターフェイスとその実装に移動した後、コントローラのコンストラクタで参照IMyInterfaceを作成すると、実装クラスのデフォルトコンストラクタが実行され、他のIoCクラスそこから解決されたリポジトリとして。

これは一般的な知識かもしれませんが、それは事をもっときれいにし、私が持っていた問題を解決します。


コントローラ

public IDefaultTemplateManager DefaultTemplateManager { get; set; } 

public DefaultController(IDefaultTemplateManager defaultTemplateManager) { 
    this.DefaultTemplateManager = defaultTemplateManager; 
} 

public ActionResult MyAction(FormCollection collection) { 
    DefaultTemplateManager.Process("MyKeyHere"); 
    View(); 
} 

IDefaultTemplateManager

public interface IDefaultTemplateManager { 
    ProcessResponse Process(string UniqueKey, DefaultTemplateManagerEditMode DatabaseName, string DefaultTemplateName); 
} 

DefaultTemplateManager

public class DefaultTemplateManager : IDefaultTemplateManager { 

     protected IRepository<MyEntity1> MyEntityRepo1 { get; set; } 
     protected IRepository<MyEntity2> MyEntityRepo2 { get; set; } 
     protected IRepository<MyEntity3> MyEntityRepo3 { get; set; } 
     protected IRepository<MyEntity4> MyEntityRepo4 { get; set; } 
     protected IRepository<MyEntity5> MyEntityRepo5 { get; set; } 
     protected IRepository<MyEntity6> MyEntityRepo6 { get; set; } 

     public DefaultTemplateManager(IRepository<MyEntity1> MyEntityRepository1, IRepository<MyEntity2> MyEntityRepository2, IRepository<MyEntity3> MyEntityRepository3, IRepository<MyEntity4> MyEntityRepository4, IRepository<MyEntity5> MyEntityRepository5, IRepository<MyEntity6> MyEntityRepository6,) { 
      this.MyEntityRepo1 = MyEntityRepository1; 
      this.MyEntityRepo2 = MyEntityRepository2; 
      this.MyEntityRepo3 = MyEntityRepository3; 
      this.MyEntityRepo4 = MyEntityRepository4; 
      this.MyEntityRepo5 = MyEntityRepository5; 
      this.MyEntityRepo6 = MyEntityRepository6; 
     } 

     public ProcessResponse Process(string UniqueKey) { 
      /* Do Work */ 
     } 
2

コンストラクタインジェクションの素晴らしい点の1つは、設計と保守性の問題をより明白にすることです。コンストラクタインジェクションを使用すると、クラスが持つ依存性の量を見るのが非常に簡単になりますが、コンストラクタインジェクションなしでは、クラスは依然として同じ数の依存性を持ちますが、離れてしまいます。

あなたが見ている問題は、Single Responsibility Principle(SRP)に違反していることを示すので、Constructor Over-injectionと呼ばれ、デザインの匂いです。 SRPは、あなたのクラスを小さく、焦点を合わせ、そして何よりも保守しやすいように指導します。

私のコードをあまりにも乱雑にしない他のオプションはありますか?

絶対に:小規模なクラスを作成してください。 MVCコントローラは、通常、「顧客」や「注文」などの特定のコンセプトのメソッドをグループ化するために使用すると、膨大なものになります。しかしこれは、コントローラーがどんどん増えていくクラスで、到着する新機能のために変更する必要があることを意味します。これは、Open/closed Principleに違反しています。既存のクラスに触れることなく新しい機能をプラグインできるシステムを用意するよう努めなければならないことを伝えています。

解決策はService Locator anti-patternまたはプロパティインジェクションに戻すのではなく、特定のことを行う小さなクラスを作成することです。コンストラクターインジェクションは、クラスライブラリであっても、依存性注入を適用する主な方法です。

+0

私はあなたが何を意味するかを知っていますが、私はこれまでに聞いてきました。私は実際にそれをやっていく方法についてはあまりよく分かりません:)私は自分のコードの中のいくつかの場所で、検索、ファイルの生成などを扱う 'XManager'タイプのクラスを使用します。作成ウィザードの場合コンストラクタに8つのリポジトリを注入しています。このページでは、最終アクションは336行です。だからあなたの提案は、これを小さな断片に分割し、そのデータを保存するために必要なリポジトリだけを渡すことでしょうか?実際には、そのコードを見て、それは私がとにかくやったはずの何かであるように、最良の例ではありません! – Dwiea

+0

336行ですか?うわー!それは保守性にとって絶対に悪いことです。正確なコードを見ることなく、これをどのように改善すべきか説明するのは本当に難しいです。私が与えた最初のリンクはいくつかのアイデアを与えるかもしれない。 [Aggregate Services](http://blog.ploeh.dk/2010/02/02/RefactoringtoAggregateServices/)について説明します(名前は異なりますが)。これは確かにあなたが試すべきものです。 – Steven

+0

ここに私のファイル生成クラス "MainJSON tmpMain = new MainJSON(tmpClientKey、clientcol、sitecol、configcol、configtemplatecol、configitemdefcol、generatedfilecol);"があります。だから、私はレコードキーを持って、5リポジトリからデータを取ると1リポジトリに書き込みます。 Main.jsonファイルにはこのファイルを作成するために必要なすべてのデータが含まれていますので、これは実際には匂いとみなされますか、またはすべてのデータをオブジェクトにロードして渡す必要がありますか?私はそれが自分の小さな箱に「きちんとしている」のが好きで、Main.jsonファイルが必要なだけで、そのクラスを作成してからProcessToDatabaseメソッドを実行するほうが簡単です。 – Dwiea

0

コントローラが他のクラスのリポジトリをシャッフルしているようです。 Ninjectではなく、あなたにそれらのクラスを提供してみましょう:

public class Controller 
{ 
    public Controller(IDependencyFactory dependency) { } 
} 

public interface IDependencyFactory 
{ 
    IDependency CreateDependency(); 
} 

public interface IDependency 
{ 
} 

public class Dependency : IDependency 
{ 
    public Dependency() { } 
} 

public class Program 
{ 
    public static void Main() 
    { 
     var standardKernel = new StandardKernel(); 
     standardKernel.Bind<IDependencyFactory>().ToFactory(); 
     standardKernel.Bind<IDependency>().To<Dependency>(); 
    } 
} 

あなたは、工場の拡張機能によって処理されIDependencyFactoryの実装を、記述する必要はありません。 Dependency -classは、Ninjectによってその依存関係が挿入されます。

+0

これはどのような問題を解決しますか?これにより、クラスの依存関係の量が倍増し、クラスの複雑さが増します。 – Steven

+0

どういう意味か分かりません。 8つのリポジトリの注入を例えば代わりに1つのRepositoryFactory、依存関係はどのように倍増しますか?すべてのリポジトリに汎用のRepositoryFactoryを追加することもできます。これは制限付きのサービスロケータのようですが、すべてのサービスをコードに公開しているわけではありません。 –

+0

クラスの依存関係は、あなたが挿入するクラスだけでなく、クラスが依存するすべてのものを数えなければなりません。ファクトリの場合、ファクトリによって返される抽象化も依存関係です。したがって、各工場では、2番目の依存関係が発生します。単体テストを開始すると、工場とのやりとりとファクトリで公開されている抽象化の両方をテストする必要があるため、複雑さが増しています。だから一般的に、工場の使用は、クラスを減らすのではなく、クラスの複雑さを増やします。 – Steven

関連する問題