Ich habe einen Freund, der gerade erst in die .NET-Entwicklung einsteigt, nachdem er sich jahrelang in Java entwickelt hat, und nachdem ich mir einen Teil seines Codes angesehen habe, stelle ich fest, dass er ziemlich oft Folgendes tut:
IDictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();
Er deklariert das Wörterbuch eher als Schnittstelle als als Klasse. Normalerweise würde ich Folgendes tun:
Dictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();
Ich würde die IDictionary-Schnittstelle nur verwenden, wenn sie benötigt wird (z. B. um das Wörterbuch an eine Methode zu übergeben, die eine IDictionary-Schnittstelle akzeptiert).
Meine Frage ist: Gibt es irgendwelche Vorteile für seine Art, Dinge zu tun? Ist dies eine gängige Praxis in Java?
IDictionary
(Dictionary
undConcurrentDictionary
) weisen sehr unterschiedliche Leistungsmerkmale auf (z. B.Count
ist bei ersteren sehr schnell und bei letzteren sehr langsam). Daher würde ich empfehlen, die konkrete Klasse (und nichtIDictionary
) in C # anzugeben, sofern dies möglich ist, damit der Verbraucher weiß, wie er das Wörterbuch performant verwendet.IDictionary
hat einige Leistung "Macken" - nimaara.com/2016/03/06/beware-of-the-idictionary-tkey-tvalue .Antworten:
Wenn IDictionary ein "allgemeinerer" Typ als Dictionary ist, ist es sinnvoll, den generischeren Typ zum Deklarieren von Variablen zu verwenden. Auf diese Weise müssen Sie sich nicht so sehr um die der Variablen zugewiesene Implementierungsklasse kümmern, und Sie können den Typ in Zukunft problemlos ändern, ohne viel folgenden Code ändern zu müssen. In Java wird dies beispielsweise häufig als besser angesehen
List<Integer> intList=new LinkedList<Integer>();
als es zu tun ist
LinkedList<Integer> intList=new LinkedList<Integer>();
Auf diese Weise bin ich sicher, dass der folgende Code die Liste als Liste und nicht als LinkedList behandelt, was es in Zukunft einfach macht, LinkedList für Vector oder eine andere Klasse, die List implementiert, auszutauschen. Ich würde sagen, dass dies Java und der guten Programmierung im Allgemeinen gemeinsam ist.
quelle
Diese Praxis ist nicht nur auf Java beschränkt.
Es wird häufig auch in .NET verwendet, wenn Sie die Instanz des Objekts von der von Ihnen verwendeten Klasse entkoppeln möchten. Wenn Sie die Schnittstelle anstelle der Klasse verwenden, können Sie den Sicherungstyp bei Bedarf ändern, ohne den Rest Ihres Codes zu beschädigen.
Sie werden auch feststellen, dass diese Vorgehensweise häufig mit dem Umgang mit IoC-Containern und der Instanziierung mithilfe des Factory-Musters verwendet wird.
quelle
Ihr Freund folgt dem sehr nützlichen Prinzip:
"Abstrakt von Implementierungsdetails"
quelle
Sie sollten immer versuchen, auf die Schnittstelle und nicht auf die konkrete Klasse zu programmieren.
In Java oder einer anderen objektorientierten Programmiersprache.
In der .NET-Welt wird häufig ein verwendet,
I
um anzugeben, dass es sich um eine Schnittstelle handelt, die Sie verwenden. Ich denke, das ist häufiger, weil sie in C # nicht habenimplements
undextends
Vererbung zwischen Klasse und Schnittstelle darauf verweisen.Ich denke, Molke würde tippen
class MyClass:ISome,Other,IMore { }
Und man kann sagen ,
ISome
eineIMore
Schnittstellen sind währendOther
eine KlasseIn Java ist so etwas nicht nötig
class MyClass extends Other implements Some, More { }
Das Konzept gilt weiterhin, Sie sollten versuchen, auf der Schnittstelle zu codieren.
quelle
public class MyClass : BaseClass, IFirstInterface, ISecondInterface
also richtig. Ich habe am Anfang einer Benutzeroberfläche nichts Besonderes. Der Compiler behandelt es nicht anders. Es ist ein Marker für andere Programmierer. Sie könnten Ihre Schnittstellen ohne sie benennen. . . aber andere Programmierer würden dich wahrscheinlich hassen.Für lokale Variablen und private Felder, bei denen es sich bereits um Implementierungsdetails handelt, ist es besser, konkrete Typen als Schnittstellen für die Deklarationen zu verwenden, da die konkreten Klassen eine Leistungssteigerung bieten (der direkte Versand ist schneller als der virtuelle / Schnittstellenversand). Die JIT kann auch Methoden einfacher einbinden, wenn Sie in den Details der lokalen Implementierung nicht unnötig in Schnittstellentypen umwandeln. Wenn eine Instanz eines konkreten Typs von einer Methode zurückgegeben wird, die eine Schnittstelle zurückgibt, erfolgt die Umwandlung automatisch.
quelle
Soweit ich gesehen habe, verwenden Java-Entwickler häufiger Abstraktion (und Entwurfsmuster) als .NET-Entwickler. Dies scheint ein weiteres Beispiel dafür zu sein: Warum die konkrete Klasse deklarieren, wenn er im Wesentlichen nur mit den Schnittstellenmitgliedern arbeitet?
quelle
new IDictionary<string, double>()
würde nicht funktionieren und macht sowieso keinen Sinn.In den meisten Fällen wird der Schnittstellentyp (IDictionary) angezeigt, der verwendet wird, wenn das Mitglied externem Code ausgesetzt ist, unabhängig davon, ob dieser sich außerhalb der Assembly oder nur außerhalb der Klasse befindet. In der Regel verwenden die meisten Entwickler den konkreten Typ intern für eine Klassendefinition, während sie eine gekapselte Eigenschaft mithilfe des Schnittstellentyps verfügbar machen. Auf diese Weise können sie die Funktionen des konkreten Typs nutzen. Wenn sie jedoch den konkreten Typ ändern, muss sich die Schnittstelle der deklarierenden Klasse nicht ändern.
später kann werden:
ohne die Benutzeroberfläche von Widget zu ändern und anderen Code ändern zu müssen, der sie bereits verwendet.
quelle
In der beschriebenen Situation würde fast jeder Java-Entwickler die Schnittstelle verwenden, um die Variable zu deklarieren. Die Art und Weise, wie die Java-Sammlungen verwendet werden, ist wahrscheinlich eines der besten Beispiele:
Map map = new HashMap(); List list = new ArrayList();
Vermutlich wird in vielen Situationen nur eine lose Kopplung erreicht.
quelle
Java-Sammlungen enthalten eine Vielzahl von Implementierungen. Daher ist es für mich viel einfacher, davon Gebrauch zu machen
List<String> myList = new ArrayList<String>();
Wenn ich dann in Zukunft merke, dass "myList" threadsicher sein muss, um diese einzelne Zeile einfach in zu ändern
List<String> myList = new Vector<String>();
Und ändern Sie keine andere Codezeile. Dies schließt auch Getter / Setter ein. Wenn Sie sich zum Beispiel die Anzahl der Implementierungen von Map ansehen, können Sie sich vorstellen, warum dies eine gute Praxis sein könnte. In anderen Sprachen, in denen es nur ein paar Implementierungen für etwas gibt (sorry, kein großer .NET-Typ), aber in Objective-C gibt es wirklich nur NSDictionary und NSMutableDictionary. Es macht also nicht so viel Sinn.
Bearbeiten:
Es ist mir nicht gelungen, einen meiner Schlüsselpunkte zu treffen (nur mit dem Getter / Setter darauf angespielt).
Das Obige ermöglicht Ihnen:
public void setMyList(List<String> myList) { this.myList = myList; }
Und der Client, der diesen Aufruf verwendet, muss sich nicht um die zugrunde liegende Implementierung kümmern. Verwenden eines beliebigen Objekts, das der Listenschnittstelle entspricht.
quelle
Ich komme aus einer Java-Welt und stimme zu, dass das Mantra "Programm zu einer Schnittstelle" in Sie eingearbeitet ist. Indem Sie auf eine Schnittstelle programmieren, nicht auf eine Implementierung, erweitern Sie Ihre Methoden für zukünftige Anforderungen.
quelle
IDictionary
ist eine Schnittstelle undDictionary
ist eine Klasse.Dictionary
GeräteIDictionary
.Dies bedeutet, dass es möglich ist,
Dictionary
mit / nachIDictionary
Instanz auf eine Instanz zu verweisen und die meistenDictionary
Methoden und Eigenschaften über eineIDictionary
Instanz aufzurufen .Dies wird dringend empfohlen, um so viele Schnittstellen wie möglich zu verwenden, da Schnittstellen die Module und Baugruppen der Anwendungen abstrahieren, Polymorphismus ermöglichen, der in vielen Situationen und Fällen sowohl sehr häufig als auch nützlich ist und das Ersetzen eines Moduls durch ein anderes ermöglicht, ohne die anderen Module zu berühren .
Angenommen, der Programmierer hat in der Gegenwart geschrieben:
IDictionary<string> dictionary = new Dictionary<string>();
Und
dictionary
ruft jetzt die Methoden und Eigenschaften von aufDictionary<string>
.In der Zukunft haben die Datenbanken in der Größe gewachsen, und wir erfahren, dass
Dictionary<string>
zu langsam, so dass wir ersetzen wollenDictionary<string>
durchRedBlackTree<string>
, was schneller ist.Alles, was getan werden muss, ist, die obige Anweisung zu ersetzen durch:
IDictionary<string> dictionary = new RedBlackTree<string>();
Wenn dies
RedBlackTree
implementiert wird,IDictionary
wird der gesamte Code der Anwendung erfolgreich kompiliert und Sie haben eine neuere Version Ihrer Anwendung, in der die Anwendung jetzt schneller und effizienter als die vorherige Version ausgeführt wird.Ohne Schnittstellen wäre dieser Austausch schwieriger und würde von den Programmierern und Entwicklern erfordern, mehr Code zu ändern, der möglicherweise fehleranfällig ist.
quelle
Ich habe festgestellt, dass es für lokale Variablen im Allgemeinen nicht wichtig ist, ob Sie die Schnittstelle oder die konkrete Klasse verwenden.
Im Gegensatz zu Klassenmitgliedern oder Methodensignaturen ist der Aufwand für das Refactoring sehr gering, wenn Sie sich entscheiden, Typen zu ändern, und die Variable ist auch außerhalb ihrer Verwendungssite nicht sichtbar. Wenn Sie
var
zum Deklarieren von Einheimischen verwenden, erhalten Sie nicht den Schnittstellentyp, sondern den Klassentyp (es sei denn, Sie wandeln ihn explizit in die Schnittstelle um).Wenn Sie jedoch Methoden, Klassenmitglieder oder Schnittstellen deklarieren, ersparen Sie sich meiner Meinung nach einiges an Kopfschmerzen, wenn Sie den Schnittstellentyp im Voraus verwenden, anstatt die öffentliche API an einen bestimmten Klassentyp zu koppeln.
quelle
Die Verwendung von Schnittstellen bedeutet, dass "Wörterbuch" im folgenden Code eine beliebige Implementierung von IDictionary sein kann.
Dictionary1 dictionary = new Dictionary1(); dictionary.operation1(); // if operation1 is implemented only in Dictionary1() this will fail for every other implementation
Es ist am besten zu sehen, wenn Sie die Konstruktion des Objekts verbergen:
quelle
Ich habe die gleiche Situation mit einem Java-Entwickler angetroffen. Er instanziiert Sammlungen UND Objekte auf die gleiche Weise an ihre Schnittstelle. Zum Beispiel,
IAccount account = new Account();
Eigenschaften werden immer als Schnittstellen abgerufen / festgelegt. Dies führt zu Problemen bei der Serialisierung, die sehr gut erklärt hier
quelle