2017-07-07 4 views
0

Okay, also ich bin auf der Suche nach bestimmten Tipps über Dependency Injection und wie man es benutzt.DI Constructor Injection Neatness

Grundsätzlich habe ich eine MVC-Website, die Ninject (und das Ninject MVC-Paket) verwendet. Wenn ich also die MVC-Seite erstelle, verwende ich die Konstruktorinjektion in den Controllern. Das ist in Ordnung, aber IMO ein bisschen "hässlich", aber die Hauptsache, die ich nicht mag, ist es, alle injizierten Repositories in die anderen Klassen zu übergeben, es scheint nur ein bisschen OTT wie 6 Repositories-8 Repositories passieren zu müssen eine statische Methode oder ein Objektkonstruktor.

Nicht zu erwähnen, dass ich auf einigen meiner Seiten an fast jedem Repository arbeiten muss, damit der Controller-Constructor riesig und nicht überschaubar wird.

Gibt es noch andere Optionen, die meinen Code nicht so sehr durcheinander bringen? Ich möchte sie nicht wirklich als einzelne "Einstellungs" -Objekte weitergeben, da dies das Problem nur auf eine andere Codezeile verschiebt.

Ich verwende auch die gleichen Klassenbibliotheken für Konsolen/Desktop-Anwendungen. Ich mag die Idee, DependencyResolver.Current in den Klassenbibliotheken zu verwenden, aber jeder sagt, dies ist ein Anti-Pattern und Konstruktorinjektion sollte verwendet werden.

Haben Sie vielleicht eine MyProjectDIContext-Klasse, die ein Wörterbuch hat, das ich mit den injizierten Typen in den Controller-Konstruktoren auffüllen kann, und dann den Kontext an alle Methoden wie benötigt übergeben?

Ich habe nach dem Antwort-Bit gesucht Ich kann nicht ganz scheinen, etwas zu finden, das gut passt.

+0

Können Sie die anderen Klassen nicht einfach mit ninject registrieren und dann die Repositories direkt in diese Klassen einfügen? – BoeseB

+0

Wenn ich mich richtig erinnere, ohne DependencyResolver zu verwenden, könnte man dies nicht tun, weil das Problem "Turtles ganz unten" auftritt. Wenn also die DI-Klassen in den Konstruktor eines Controllers injiziert werden, muss ich die aufgelösten Klassen in jede Klasse weiterleiten, die sie verwenden möchte. In einigen Fällen mache ich genau das, aber es führt zu einer ganzen massiven Liste von Argumenten für einige kompliziertere Prozesse. Ein DependencyResolver vermeidet es, aber Sie sind dann an die Resolver-Klasse gebunden, die offensichtlich nicht gut ist. – Dwiea

+0

Ich versuche immer noch, DI mit Konsolen-/Desktop-Apps zu finden, daher weiß ich nicht genau, wie DI mit ihnen gut funktioniert. Zum Beispiel kann ich nicht einfach "neue MyClass()" gehen und erwarten, dass es in den entsprechenden injizierten Klassen (oder kann ich?) Passieren? – Dwiea

Antwort

0

Okay, ich habe anscheinend einen Weg gefunden, dies zu tun, der nicht so stinkt!

Ich dachte, wenn Sie zum Beispiel MVC verwenden, würden Sie die Repositories über den Konstruktor einholen und das war die einzige Möglichkeit, die Abhängigkeiten aufzulösen.

Nach einigen Arbeiten, um Abschnitte von wiederverwendbarem Code in IoC-freundliche Interfaces und deren Implementierungen zu verschieben, bemerkte ich, dass beim Erstellen Referenz IMyInterface in einem Controller-Konstruktor der Standardkonstruktor der implementierenden Klasse ausgeführt wird und Sie andere IoC-Klassen ziehen können als aufgelöste Repositories von dort.

Das mag allgemein bekannt sein, aber es macht die Dinge viel besser und löst das Problem, das ich hatte.


Beispiel

Regler

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

Eines der großen Vorteile von Constructor Injection ist, dass es Probleme bei der Konstruktion und Wartung deutlich macht. Wenn Sie die Konstruktorinjektion verwenden, wird es sehr einfach, die Anzahl der Abhängigkeiten einer Klasse zu sehen, während ohne Konstruktorinjektion eine Klasse immer noch die gleiche Anzahl von Abhängigkeiten hat, aber sie sind versteckt.

Das Problem, das Sie sehen, heißt Constructor Over-injection und es ist ein Design-Geruch, weil es anzeigt, dass Sie die Single Responsibility Principle (SRP) verletzen. Die SRP-Guides halten Ihre Klassen klein, fokussiert und vor allem: wartbar.

Gibt es noch andere Optionen, die meinen Code nicht so sehr durcheinander bringen?

Absolut: machen Sie kleinere Klassen. MVC-Controller werden in der Regel sehr groß, wenn wir Methoden eines bestimmten Konzepts wie "Kunde" oder "Bestellung" gruppieren. Dies bedeutet jedoch, dass ein Controller eine ständig wachsende Klasse ist, die für jedes neue Feature, das ankommt, geändert werden muss. Dies ist eine Verletzung der Open/closed Principle, die besagt, dass wir uns bemühen sollten, ein System zu haben, in dem wir neue Funktionen hinzufügen können, ohne bestehende Klassen zu berühren.

Die Lösung besteht daher nicht darin, zu Service Locator anti-pattern oder Property Injection zurückzukehren, sondern kleinere Klassen zu erstellen, die eine bestimmte Sache ausführen. Constructor Injection sollte Ihre primäre Methode zur Anwendung von Dependency Injection sein, auch in Klassenbibliotheken.

+0

Während ich weiß, was du meinst, und ich habe es schon einmal gehört. Ich bin nicht ganz sicher, wie ich es in der Praxis machen würde. Lol :) Ich meine an einigen Stellen in meinem Code verwende ich 'XManager' Art von Klassen, die Suchvorgänge, Dateigenerierung usw. handhaben. Im Falle eines Assistenten erstellen ich have gibt 8 Repositories in den Konstruktor ein, auf dieser Seite ist die letzte Aktion 336 Zeilen lang. Also wäre Ihr Vorschlag, dies in kleine Stücke zu teilen und nur das Repository zu übergeben, das benötigt wird, um diese Daten zu speichern? Eigentlich ist es nicht das beste Beispiel, nachdem ich mir diesen Code angeschaut habe, denn ich hätte es sowieso tun sollen! – Dwiea

+0

336 Zeilen lang? Beeindruckend! Das ist absolut schlecht für die Wartbarkeit. Ohne den genauen Code zu sehen, ist es wirklich schwer zu erklären, wie dies verbessert werden sollte. Die erste Verbindung, die ich gab, könnte jedoch einige Ideen geben. Es spricht über [Aggregate Services] (http://blog.ploeh.dk/2010/02/02/RefactoringtoAggregateServices/) (obwohl anders benannt). Dies ist sicherlich etwas, was Sie versuchen sollten. – Steven

+0

Hier ist eine meiner Dateigenerierungsklassen "MainJSON tmpMain = new MainJSON (tmpClientKey, clientcol, sitecol, configcol, configtemplatecol, configitdefdefcol, generatedfilecol);". Also habe ich einen Datensatzschlüssel, 5 Repositories, von denen Daten genommen werden, und 1 Repository, in das ich schreiben kann. Die Main.json-Datei benötigt alle diese Daten, um die Datei zu erstellen, also würde dies tatsächlich als Geruch betrachtet werden oder sollte ich alle Daten in ihre Objekte laden und sie dann weitergeben?Ich mag es 'ordentlich' in seiner eigenen kleinen Box, will eine Main.json-Datei, dann erstellen Sie nur diese Klasse dann Ausführen ProcessToDatabase Methode scheint einfacher. – Dwiea

0

Es scheint, dass Ihr Controller Repositorys zu anderen Klassen mischt. stattdessen lassen Ninject Sie diese Klassen liefern:

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>(); 
    } 
} 

Sie keine Implementierung von IDependencyFactory schreiben müssen, dass durch die Werkserweiterung behandelt. Ihre Dependency -klasse erhält ihre Abhängigkeiten von Ninject.

+0

Welches Problem löst das? Dadurch verdoppeln Sie die Anzahl der Abhängigkeiten, die die Klasse besitzt, und damit erhöhen Sie die Komplexität der Klasse. – Steven

+0

Ich bin mir nicht sicher, wie du meinst. Wenn Sie Ihre Injektion von 8 Repositorys auf z. eine RepositoryFactory stattdessen, wie werden Ihre Abhängigkeiten verdoppelt? Sie könnten sogar eine generische RepositoryFactory für alle Repositories haben. Es ist wie ein eingeschränkter Service-Locator, aber Sie geben nicht alle Services Ihrem Code frei. –

+0

Die Abhängigkeiten einer Klasse sind nicht nur die Klassen, die Sie injizieren, sondern Sie müssen alles zählen, worauf die Klasse angewiesen ist. Im Fall einer Fabrik ist die von der Fabrik zurückgegebene Abstraktion ebenfalls eine Abhängigkeit. Für jede Fabrik erhält man also eine zweite Abhängigkeit. Sie werden diese zusätzliche Komplexität sofort bemerken, wenn Sie mit Komponententests beginnen, da Sie sowohl die Interaktion mit der Factory als auch die Abstraktion, die von der Factory verfügbar gemacht wird, testen müssen. Im Allgemeinen erhöht die Verwendung von Fabriken die Komplexität einer Klasse, anstatt sie zu verringern. – Steven

Verwandte Themen