Warum Interfaces anstelle von abstrakten Klassen mit DI verwenden?

8

Ich beginne meine Reise des Lernens über Dependency-Injection, und einer der Gründe, warum ich sah, warum es eine gute Idee ist, DI zu verwenden, war, dass es explizit Ihre Abhängigkeiten spezifiziert, was auch Ihren Code klarer macht.

>

Ich habe auch bemerkt, dass Interfaces reichlich verwendet werden, aber ich möchte wissen, warum wir abstrakte Klassen nicht ausschließlich zum Spezifizieren eines Standardkonstruktors verwenden sollten?

Natürlich kann keine Implementierung in die abstrakte Klasse aufgenommen werden.

Wäre das nicht:

%Vor%

Zeigen Sie deutlicher, was die Abhängigkeit eines FooBase-Objekts mehr ist als:

%Vor%

?

Bitte denken Sie daran, ich bin neu in diesem ganzen Konzept, also sei nett:)

    
ChandlerPelhams 19.07.2012, 21:21
quelle

3 Antworten

5

Ein technischer Grund - das Erzwingen bestimmter Elternklassen in Sprachen ohne Mehrfachvererbung (Java / C #) wird die Freiheit der Implementierung der "Schnittstelle" erheblich einschränken.

Beachten Sie, dass hinter dem Wort "interface" zwei Konzepte versteckt sind und dass es in C # etwas schwieriger zu verstehen ist:

  • "interface" und abstraktes Konzept: gut definierter Satz von Eigenschaften / Methoden zur Interaktion mit einem Objekt; Vertrag, um mit einem Objekt zu arbeiten.
  • "Schnittstelle" als Typ in einer bestimmten Sprache (C # / Java) - eine mögliche Darstellung von Vertrag in einer bestimmten Sprache.

Abstrakte / konkrete Klassen können verwendet werden, um einen Vertrag darzustellen, aber erzwingen Beschränkungen für die Implementierer eines Vertrags in C #.

Einige andere Sprachen (wie C ++) haben keine solche Einschränkung und abstrakte Klassen sind eine gute Option. Andere Sprachen (d. H. "Ducktypen" JavaScript) haben keine solche Klassen- / Schnittstellenunterscheidung, aber Sie können immer noch "Vertrag" mit einem Objekt haben.

Beispiel:

Um mehr Kontext zur Verfügung zu stellen, wo Sie diese Einschränkung selbst in C # treffen sollten: DI wird häufig zusammen mit irgendeiner Form von TDD oder zumindest mit grundlegenden Komponententests verwendet. Sie versuchen also, Code und Tests zu schreiben, die die abstrakte Basisklasse für DI verwenden.

%Vor%

Wenn Sie sich nun dazu entschließen, Tests zu schreiben, haben Sie vielleicht schon Test-Klassen, die Ihnen helfen, Objekte zu mocksen (wenn Sie nicht schon einmal existieren)

%Vor%     
Alexei Levenkov 19.07.2012, 21:26
quelle
5

Interface definiert einen Vertrag. Eine abstrakte Basisklasse definiert ein Verhalten.

Im Wesentlichen können Sie eine einzelne Klasse bereitstellen, die mehrere Schnittstellen implementiert, die dann wiederum in mehrere Klassen injiziert werden können, die Sie aber nur haben eine einzelne abstrakte Basisklasse (zumindest in C #).

Betrachten Sie den Punkt, an dem Sie einen Typ im Container registrieren (am besten den Kompositionswurzel) und berücksichtigen Sie den Punkt, an dem Sie die Abhängigkeit auflösen (den Konstruktor oder eine Eigenschaft).

Dieses SO behandelt einige weitere Aspekte SO auf der Schnittstelle zur Basisklasse

    
Mare Infinitus 19.07.2012 21:31
quelle
1

In .NET haben Sie nur einzelne Vererbung. In Sprachen / Frameworks, in denen dies der Fall ist, bietet die Verwendung einer abstrakten Klasse als Abstraktion das Potential zu "brennt die Basisklasse" .

Dies bedeutet, dass Sie den Implementierer Ihrer Abstraktion zwingen, von einer einzelnen Klasse zu erben, wenn Sie dies erzwingen können, je nachdem, was die Implementierung ist, kann dies zu erheblichen Unannehmlichkeiten führen.

Nehmen wir an, Sie haben Ihre abstrakte Klasse Contract . Wenn jemand eine eigene Base -Klasse hat, die er verwenden möchte, die nur protected -Methoden (für Erben) verfügbar macht.

Da die Methoden protected sind, kann die Kapselung (eine in einem Feld gespeicherte Instanz von Base ) nicht für den Zugriff auf die Methoden in Base für Ihre Abstraktionsimplementierung verwendet werden.

Schlimmer noch, wenn Sie nicht Zugriff auf Base haben, müssen Sie möglicherweise auf einige sehr hässliche Problemumgehungen (Reflection, nämlich) zurückgreifen.

Mit Interfaces geben Sie dem Implementierer die Wahl, wo er erben soll, und schränken seine Optionen nicht ein.

Das typische Muster, das Sie sehen werden, ist, dass Sie immer eine Schnittstelle für Ihren Vertrag bereitstellen und Ihre Kunden gegen die Schnittstelle kodieren. Sie auch stellen eine abstrakte Basisklasse zur Verfügung, die Funktionen zur Verfügung stellt, von denen Personen möglicherweise ableiten, die aber nicht aus abgeleitet werden müssen.

>

Wenn es Ihnen auch möglich ist, diese Funktionalität in Form von etwas bereitzustellen, das leicht gekapselt ist (für die oben beschriebene Bedingung), wäre es sogar noch optimaler (Sie hätten eine abstrakte Klasse, die nur die Instanz, die die Methoden verfügbar macht).

    
casperOne 19.07.2012 21:48
quelle

Tags und Links