2016-04-28 13 views
24

Ich untersuche SOLID-Prinzipien und habe eine Frage zum Abhängigkeitsmanagement in Bezug auf Schnittstellen.Kann eine Schnittstelle von einer Klasse abhängig sein?

Ein Beispiel aus dem Buch lese ich (Adaptive-Code über C# von Gary McLean Halle) zeigt eine TradeProcessor Klasse, die die Handelsdaten erhalten wird, verarbeiten und speichern sie in der Datenbank. Die Handelsdaten werden von einer Klasse namens TradeRecord modelliert. Eine TradeParser Klasse verarbeitet die erhaltenen Handelsdaten in eine TradeRecord Instanz (en). Die TradeProcessor Klasse verweist nur auf eine ITradeParser Schnittstelle, so dass sie nicht von der TradeParser Implementierung abhängig ist.

Der Autor hat die Parse Methode (in der ITradeParser Schnittstelle) eine IEnumerable<TradeRecord> Sammlung zurückgeben, die die verarbeiteten Handelsdaten enthält. Bedeutet das nicht, dass ITradeParser nun von der Klasse TradeRecord abhängig ist?

Sollte der Autor nicht etwas wie eine ITradeRecord Schnittstelle gemacht haben und Parse eine Sammlung von ITradeRecord Instanzen zurückgeben? Oder vermisse ich etwas Wichtiges?

Hier ist der Code (die Implementierung von TradeRecord ist irrelevant, so dass es weggelassen):

TradeProcessor.cs

public class TradeProcessor 
{ 
    private readonly ITradeParser tradeParser; 

    public TradeProcessor(ITradeParser tradeParser) 
    { 
     this.tradeParser = tradeParser; 
    } 

    public void ProcessTrades() 
    { 
     IEnumerable<string> tradeData = "Simulated trade data..." 
     var trades = tradeParser.Parse(tradeData); 

     // Do something with the parsed data... 
    } 
} 

ITradeParser.cs

public interface ITradeParser 
{ 
    IEnumerable<TradeRecord> Parse(IEnumerable<string> tradeData); 
} 
+0

Beachten Sie auch: der Autor befürwortet Schnittstellen und ihre Implementierungen in einer separaten Baugruppe (d. H. Das Stairway-Muster) zu halten. Wenn ich diesem Muster folge, müsste "TradeRecord" in beiden Assemblys vorhanden sein, oder ich müsste einen Verweis von der Implementierungsassembly auf die Implementierungsassembly haben (wodurch eine zirkuläre Referenz erstellt wird). –

+1

Tatsächlich ist die Implementierung von TradeRecord relevant. Wenn es sich um eine reine Datenklasse handelt (enthält nur Eigenschaften ohne Logik), dann gibt es keinen Grund, die Schnittstelle dafür zu öffnen. – Evk

+1

Ich denke, es ist, weil TradeRecord nur ein einfaches DTO-Objekt ist. Natürlich können Sie eine Schnittstelle für diese Klasse erstellen, aber würden Sie Schnittstellen für jede andere von Ihnen erstellte Klasse erstellen? Sie können Ihre TradeRecord-Klasse als Vertrag behandeln. – MistyK

Antwort

40

Dies ist eine gute Frage, die in den Kompromiss geht zwischen Reinheit und Praktikabilität.

Ja, von reinem Prinzipal, können Sie sagen, dass ITradeParser.Parse eine Sammlung von ITraceRecord Schnittstellen zurückgeben sollte. Warum binden Sie sich doch an eine bestimmte Implementierung?

Sie können dies jedoch weiter nehmen. Sollten Sie eine IEnumerable<string> akzeptieren? Oder sollten Sie eine Art von ITextContainer haben? I32bitNumeric anstelle von int? Dies ist reductio ad absurdum natürlich, aber es zeigt, dass wir immer irgendwann erreichen einen Punkt, an dem wir arbeiten etwas, ein konkretes Objekt (Nummer, String, TraceRecord, was auch immer), nicht ein Abstraktion.

Dies bringt auch den Punkt hervor, warum wir in erster Linie Schnittstellen verwenden, um Verträge für Logik und Funktionalität zu definieren. Ein ITradeProcessor ist ein Vertrag für eine unbekannte Implementierung, die ersetzt oder aktualisiert werden kann. A TradeRecord ist kein Vertrag für die Implementierung, es ist die Implementierung. Wenn es ein DTO-Objekt ist, was es zu sein scheint, würde es keinen Unterschied zwischen der Schnittstelle und der Implementierung geben, was bedeutet, dass es keinen wirklichen Zweck bei der Definition dieses Vertrags gibt - es ist in der konkreten Klasse enthalten.

+0

Ausgezeichnet erklärt, das macht jetzt völlig Sinn. Kurz gesagt: Würde die "TradeRecord" -Klasse bei der Implementierung des Stariway-Musters in derselben Baugruppe wie die Interfaces leben, so dass die Schnittstellen UND die Implementierungen auf "TradeRecord" verweisen könnten (wie von @Evk in einem Kommentar erwähnt)? –

+1

Ja, ich denke, sie würden beide in derselben Versammlung sitzen. Diese Assembly - normalerweise "Common" oder "Shared" oder "Interfaces" genannt - enthält die öffentlichen, freigegebenen Verträge, die in Ihrem Code verfügbar gemacht und implementiert wurden. Sie können ITradeProcessor nicht konsumieren oder implementieren, ohne TradeRecord zu kennen. –

10

Der Autor hat die Parse-Methode (in der ITradeParser-Schnittstelle) eine IEnumerable-Auflistung zurückgeben, die die verarbeiteten Handelsdaten enthält.

Bedeutet das nicht, dass ITradeParser jetzt von der TradeRecord-Klasse abhängig ist?

Ja, ITradeParser ist jetzt fest mit TradeRecord gekoppelt. Angesichts des eher akademischen Ansatzes dieser Frage kann ich sehen, woher Sie kommen. Aber was ist TradeRecord? Ein Datensatz ist per Definition im Allgemeinen ein einfaches, nicht intelligentes Datenelement (manchmal als POCO, DTO oder Model bezeichnet).

Irgendwann ist der potenzielle Gewinn der Abstraktion weniger wertvoll als die Komplexität, die er verursacht. Dieser Ansatz ist in der Praxis ziemlich üblich - Modelle (wie ich sie bezeichne) sind versiegelte Typen, die durch die Schichten einer Anwendung fließen. Schichten, die auf die Modelle einwirken, werden zu Grenzflächen abstrahiert, so dass jede Schicht getrennt dargestellt und getestet werden kann.

Zum Beispiel kann eine Client-Anwendung eine View-, ViewModel- und Repository-Ebene haben. Jede Schicht kann mit dem konkreten Datensatztyp arbeiten. Aber das ViewModel könnte so verkabelt werden, dass es mit einem verspotteten IRepository arbeitet, das die konkreten Typen mit fest codierten, gespotteten Daten aufbaut. Für ein abstrahiertes IModel gibt es an dieser Stelle keinen Vorteil - es hat nur gerade Daten.

+0

Danke für deine Antwort. Ich wusste, dass ich es wahrscheinlich über überlegte, und das war der Fall. –

+3

Ich würde nicht sagen, dass es übertrieben ist. Es geht vielmehr darum, Theorie in die Praxis umzusetzen und die Stellen in einer Anwendungsebene zu finden, die am meisten von der Abstraktion profitieren. – Jonesopolis