6 Stimmen

Worauf achten Sie bei einer Abhängigkeit, um festzustellen, ob sie eine injizierte Abhängigkeit sein sollte?

Ich habe Schwierigkeiten herauszufinden, wann eine Abhängigkeit injiziert werden sollte. Lassen Sie uns einfach mit einem einfachen Beispiel aus meinem Projekt arbeiten:

class CompanyDetailProvider : ICompanyDetailProvider {
    private readonly FilePathProvider provider;
    public CompanyDetailProvider(FilePathProvider provider) {
        this.provider = provider;
    }

    public IEnumerable<CompanyDetail> GetCompanyDetailsForDate(DateTime date) {
        string path = this.provider.GetCompanyDetailFilePathForDate(date);
        var factory = new DataReaderFactory();
        Func<IDataReader> sourceProvider = () => factory.CreateReader(
            DataFileType.FlatFile, 
            path
        );
        var hydrator = new Hydrator<CompanyDetail>(sourceProvider);
        return hydrator;
    }
}

(Keine Produktionsqualität!)

ICompanyDetailProvider ist verantwortlich für die Bereitstellung von Instanzen von CompanyDetail s für die Verbraucher. Die konkrete Umsetzung CompanyDetailProvider tut dies durch Hydratisierung von Instanzen von CompanyDetail aus einer Datei mit einer Hydrator<T> die Reflexion verwendet, um Instanzen von T aus einem IDataReader . Eindeutig CompanyDetailProvider ist abhängig von DataReaderFactory (die Instanzen von OleDbDataReader mit einem Pfad zu einer Datei) und Hydrator . Sollten diese Abhängigkeiten injiziert werden? Ist es richtig, Folgendes zu injizieren FilePathProvider ? Welche Eigenschaften prüfe ich, um zu entscheiden, ob sie injiziert werden sollten?

2voto

Bryan Watts Punkte 43539

Ich bewerte die Verwendungszwecke von Abhängigkeiten durch das Objektiv Absicht/Mechanismus: Kommuniziert dieser Code klar seine Absicht, oder muss ich das aus einem Haufen von Implementierungsdetails herauslesen?

Wenn der Code tatsächlich wie eine Anhäufung von Implementierungsdetails aussieht, bestimme ich die Ein- und Ausgänge und erstelle eine völlig neue Abhängigkeit, die die warum hinter all den wie . Ich schiebe dann die Komplexität in die neue Abhängigkeit und mache den ursprünglichen Code einfacher und klarer.

Wenn ich den Code in dieser Frage lese, sehe ich deutlich den Abruf eines Dateipfads auf der Grundlage eines Datums, gefolgt von einer undurchsichtigen Reihe von Anweisungen, die das Ziel, eine Entität eines bestimmten Typs an einem bestimmten Pfad zu lesen, nicht klar vermitteln. Ich kann mich durcharbeiten, aber das bringt mich aus dem Konzept.

Ich schlage vor, dass Sie den Abstraktionsgrad der zweiten Hälfte der Berechnung erhöhen, nachdem Sie den Weg gefunden haben. Ich würde damit beginnen, eine Abhängigkeit zu definieren, die die Ein- und Ausgänge des Codes implementiert:

public interface IEntityReader
{
    IEnumerable<T> ReadEntities<T>(string path);
}

Schreiben Sie dann die ursprüngliche Klasse unter Verwendung dieser absichtsoffenbarenden Schnittstelle neu:

public sealed class CompanyDetailProvider : ICompanyDetailProvider
{
    private readonly IFilePathProvider _filePathProvider;
    private readonly IEntityReader _entityReader;

    public CompanyDetailProvider(IFilePathProvider filePathProvider, IEntityReader entityReader)
    {
        _filePathProvider = filePathProvider;
        _entityReader = entityReader;
    }

    public IEnumerable<CompanyDetail> GetCompanyDetailsForDate(DateTime date)
    {
        var path = _filePathProvider.GetCompanyDetailsFilePathForDate(date);

        return _entityReader.ReadEntities<CompanyDetail>(path);
    }
}

Jetzt können Sie die blutigen Details, die für sich genommen recht stimmig sind, in den Sandkasten stellen:

public sealed class EntityReader : IEntityReader
{
    private readonly IDataReaderFactory _dataReaderFactory;

    public EntityReader(IDataReaderFactory dataReaderFactory)
    {
        _dataReaderFactory = dataReaderFactory;
    }

    public IEnumerable<T> ReadEntities<T>(string path)
    {
        Func<IDataReader> sourceProvider =
            () => _dataReaderFactory.CreateReader(DataFileType.FlatFile, path);

        return new Hydrator<T>(sourceProvider);
    }
}

Wie in diesem Beispiel gezeigt, sollten Sie die Data-Reader-Factory abstrahieren und den Hydrator direkt instanziieren. Der Unterschied ist, dass EntityReader verwendet die Fabrik des Datenlesers, während sie nur erstellt den Hydrator. Er ist eigentlich gar nicht von der Instanz abhängig, sondern dient als Hydratorfabrik.

1voto

Chris Marisic Punkte 31583

Wie man feststellt, ob eine Klasse Dependency Injection verwenden sollte


Ist für diese Klasse eine externe Abhängigkeit erforderlich?

Wenn ja, injizieren.

Wenn nein, besteht keine Abhängigkeit.

Um die Frage "Ist es richtig, FilePathProvider zu injizieren?" zu beantworten: Ja, es ist richtig.

Edit : Zur Verdeutlichung: Eine externe Abhängigkeit liegt vor, wenn Sie eine nicht verwandte, aber abhängige Klasse aufrufen, insbesondere wenn es sich um eine physische Ressource handelt, wie z.B. das Lesen von Dateipfaden von der Festplatte, aber dies impliziert auch jede Art von Dienst oder Modellklasse, die eine von der Kernfunktionalität der Klasse unabhängige Logik ausführt.

In der Regel ist dies bei jedem Anruf bei dem neuen Betreiber zu vermuten. Unter den meisten Umständen sollten Sie alle Verwendungen des new-Operators refaktorisieren, wenn er mit einer anderen Klasse als einem Datenübertragungsobjekt zu tun hat. Wenn es sich bei der Klasse um eine interne Klasse handelt, kann eine new-Anweisung in Ordnung sein, wenn sie die Komplexität reduziert, wie z. B. die new DataReaderFactory(), die jedoch auch ein sehr guter Kandidat für Konstruktorinjektion zu sein scheint.

1voto

Chris Conway Punkte 15867

Ich neige dazu, auf der liberaleren Seite der Injektion von Abhängigkeiten zu sein, so würde ich auf jeden Fall sowohl IDataReader injizieren wollen, um den neuen DataFactoryReader und den Hydrator loszuwerden. Es hält alles mehr lose gekoppelt, die natürlich macht es einfacher zu pflegen.

Ein weiterer Vorteil, der sich sofort bemerkbar macht, ist die bessere Testbarkeit. Sie können Mocks Ihrer IDataReader und Hydrator erstellen, um Ihre Unit-Tests auf die GetCompanyDetailsForDate-Methode zu beschränken, ohne sich Gedanken darüber machen zu müssen, was innerhalb der Datareader und Hydrator passiert.

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