362 Stimmen

Wie kann man den Konstruktor-Wahnsinn der Dependency Injection vermeiden?

Ich stelle fest, dass meine Konstrukteure allmählich wie folgt aussehen:

public MyClass(Container con, SomeClass1 obj1, SomeClass2, obj2.... )

mit ständig wachsender Parameterliste. Da "Container" ist meine Abhängigkeit Injektion Container, warum kann ich nicht einfach dies tun:

public MyClass(Container con)

für jede Klasse? Was sind die Nachteile? Wenn ich das tue, fühlt es sich an, als würde ich ein verherrlichtes statisches Element verwenden. Bitte teilen Sie Ihre Gedanken über IoC und Dependency Injection Wahnsinn.

483voto

Mark Seemann Punkte 216836

Sie haben Recht, wenn Sie den Container als Service Locator verwenden, ist er mehr oder weniger eine glorifizierte statische Fabrik. Aus vielen Gründen Ich halte dies für ein Anti-Muster (siehe auch dieser Auszug aus meinem Buch).

Einer der wunderbaren Vorteile von Constructor Injection ist, dass es Verstöße gegen die Prinzip der einzigen Verantwortung überdeutlich sichtbar.

Wenn das passiert, ist es an der Zeit Umstrukturierung zu Fassadendiensten . Kurz gesagt, schaffen Sie eine neue, mehr grobkörnig Schnittstelle, die die Interaktion zwischen einigen oder allen der feinkörnigen Abhängigkeiten, die Sie derzeit benötigen, verbirgt.

71voto

derivation Punkte 4022

Ich denke nicht, dass Ihre Klassenkonstruktoren einen Verweis auf Ihren IOC-Container haben sollten. Dies stellt eine unnötige Abhängigkeit zwischen Ihrer Klasse und dem Container dar (die Art von Abhängigkeit, die IOC zu vermeiden versucht!).

34voto

kyoryu Punkte 12525

Die Schwierigkeit der Übergabe der Parameter ist nicht das Problem. Das Problem ist, dass Ihre Klasse zu viel tut und stärker aufgegliedert werden sollte.

Dependency Injection kann als Frühwarnung für zu groß werdende Klassen dienen, insbesondere wegen der zunehmenden Belastung durch die Übergabe aller Abhängigkeiten.

5voto

tchelidze Punkte 7596

Problem :

1) Konstruktor mit ständig wachsender Parameterliste.

2) Wenn die Klasse vererbt wird (Bsp: RepositoryBase ), dann ändern Sie den Konstruktor Signatur zu einer Änderung in den abgeleiteten Klassen.

Lösung 1

Pass IoC Container zum Konstrukteur

Warum

  • Keine ständig wachsende Parameterliste mehr
  • Die Signatur des Konstruktors wird einfach

Warum nicht

  • Macht Ihre Klasse eng an den IoC-Container gekoppelt. (Das führt zu Problemen, wenn 1. Sie diese Klasse in anderen Projekten verwenden wollen, in denen Sie einen anderen IoC-Container verwenden. 2. Sie sich entscheiden, den IoC-Container zu wechseln)
  • Das macht Ihren Unterricht weniger anschaulich. (Man kann nicht wirklich einen Klassenkonstruktor betrachten und sagen, was er zum Funktionieren braucht).
  • Klasse kann potenziell auf alle Dienste zugreifen.

Lösung 2

Erstellen Sie eine Klasse, die alle Dienste zusammenfasst, und übergeben Sie sie an den Konstruktor

 public abstract class EFRepositoryBase 
 {
    public class Dependency
    {
        public DbContext DbContext { get; }
        public IAuditFactory AuditFactory { get; }

         public Dependency(
            DbContext dbContext,
            IAuditFactory auditFactory)
        {
            DbContext = dbContext;
            AuditFactory = auditFactory;
        }
    }

    protected readonly DbContext DbContext;        
    protected readonly IJobariaAuditFactory auditFactory;

    protected EFRepositoryBase(Dependency dependency)
    {
        DbContext = dependency.DbContext;
        auditFactory= dependency.JobariaAuditFactory;
    }
  }

Abgeleitete Klasse

  public class ApplicationEfRepository : EFRepositoryBase      
  {
     public new class Dependency : EFRepositoryBase.Dependency
     {
         public IConcreteDependency ConcreteDependency { get; }

         public Dependency(
            DbContext dbContext,
            IAuditFactory auditFactory,
            IConcreteDependency concreteDependency)
        {
            DbContext = dbContext;
            AuditFactory = auditFactory;
            ConcreteDependency = concreteDependency;
        }
     }

      IConcreteDependency _concreteDependency;

      public ApplicationEfRepository(
          Dependency dependency)
          : base(dependency)
      { 
        _concreteDependency = dependency.ConcreteDependency;
      }
   }

Warum

  • Das Hinzufügen einer neuen Abhängigkeit zu einer Klasse hat keine Auswirkungen auf abgeleitete Klassen
  • Die Klasse ist unabhängig vom IoC-Container
  • Die Klasse ist beschreibend (in Bezug auf ihre Abhängigkeiten). Wenn Sie wissen wollen, welche Klasse Sie haben, müssen Sie per Konvention A Hängt davon ab, dass die Informationen in A.Dependency
  • Die Konstruktorsignatur wird einfach

Warum nicht

  • zusätzliche Klasse erstellen müssen
  • die Registrierung von Diensten wird komplex (Sie müssen sich für jede X.Dependency getrennt)
  • Konzeptionell dasselbe wie beim Passieren IoC Container
  • ..

Lösung 2 ist nur ein grober Entwurf, wenn es stichhaltige Argumente dagegen gibt, dann wäre ein beschreibender Kommentar willkommen

4voto

samsur Punkte 276

Ich stieß auf eine ähnliche Frage über Konstruktor-basierte Dependency Injection und wie komplex es war immer in allen Abhängigkeiten übergeben.

Einer der Ansätze, die ich in der Vergangenheit verwendet habe, ist die Verwendung des Anwendungsfassadenmusters mit einer Dienstschicht. Diese würde eine grobe API haben. Wenn dieser Dienst von Repositories abhängt, würde er eine Setter-Injection der privaten Eigenschaften verwenden. Dies erfordert die Erstellung einer abstrakten Fabrik und die Verlagerung der Logik zur Erstellung der Repositories in eine Fabrik.

Ausführlichen Code mit Erläuterungen finden Sie hier

Bewährte Praktiken für IoC in komplexen Dienstschichten

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X