Es gibt einen winzigen Unterschied zwischen Java und C #, der hier relevant ist. In Java ist standardmäßig jedes Mitglied virtuell. In C # ist jedes Mitglied standardmäßig versiegelt - mit Ausnahme der Mitglieder der Benutzeroberfläche.
Die damit verbundenen Annahmen beeinflussen die Richtlinie - in Java sollte jeder öffentliche Typ gemäß dem Substitutionsprinzip von Liskov als nicht endgültig angesehen werden [1]. Wenn Sie nur eine Implementierung haben, geben Sie der Klasse einen Namen Parser
. Wenn Sie feststellen, dass Sie mehrere Implementierungen benötigen, ändern Sie die Klasse einfach in eine Schnittstelle mit demselben Namen und benennen die konkrete Implementierung in eine beschreibende Implementierung um.
In C # ist die Hauptannahme, dass, wenn Sie eine Klasse erhalten (Name beginnt nicht mit I
), dies die gewünschte Klasse ist. Wohlgemerkt, dies ist bei weitem nicht 100% genau - ein typisches Gegenbeispiel wären Klassen wie Stream
(die eigentlich eine Schnittstelle oder ein paar Schnittstellen sein sollten), und jeder hat seine eigenen Richtlinien und Hintergründe aus anderen Sprachen. Es gibt auch andere Ausnahmen wie das häufig verwendete Base
Suffix für eine abstrakte Klasse - genau wie bei einer Schnittstelle wissen Sie, dass der Typ polymorph sein soll.
Es gibt auch eine nette Usability-Funktion, wenn Sie den Namen ohne I-Präfix für Funktionen belassen, die sich auf diese Schnittstelle beziehen, ohne die Schnittstelle zu einer abstrakten Klasse machen zu müssen (was aufgrund der fehlenden Klassenmehrfachvererbung in C # schaden würde). Dies wurde von LINQ, das IEnumerable<T>
als Schnittstelle verwendet wird, und Enumerable
als Repository von Methoden, die für diese Schnittstelle gelten, populär gemacht . Dies ist in Java nicht erforderlich, wo Schnittstellen auch Methodenimplementierungen enthalten können.
Letztendlich ist das I
Präfix in der C # -Welt und in der .NET-Welt weit verbreitet (da der weitaus größte Teil des .NET-Codes in C # geschrieben ist, ist es sinnvoll, die C # -Richtlinien für die meisten öffentlichen Schnittstellen zu befolgen). Das bedeutet, dass Sie mit ziemlicher Sicherheit mit Bibliotheken und Code arbeiten werden, die dieser Notation folgen, und es ist sinnvoll, die Tradition zu übernehmen, um unnötige Verwirrung zu vermeiden - es ist nicht so, als würde das Weglassen des Präfixes Ihren Code verbessern :)
Ich gehe davon aus, dass Onkel Bobs Argumentation ungefähr so war:
IBanana
ist der abstrakte Begriff der Banane. Wenn es eine implementierende Klasse geben kann, die keinen besseren Namen als hat Banana
, ist die Abstraktion völlig bedeutungslos, und Sie sollten die Schnittstelle löschen und einfach eine Klasse verwenden. Wenn es einen besseren Namen gibt (etwa LongBanana
oder AppleBanana
), gibt es keinen Grund, ihn nicht Banana
als Namen für die Schnittstelle zu verwenden. Daher I
bedeutet die Verwendung des Präfixes, dass Sie eine nutzlose Abstraktion haben, wodurch der Code ohne Nutzen schwerer zu verstehen ist. Und da Sie beim strengen OOP immer Code gegen Schnittstellen verwenden, ist der einzige Ort, an dem Sie das I
Präfix für einen Typ nicht sehen würden, ein Konstruktor - ziemlich sinnloses Rauschen.
Wenn Sie dies auf Ihre Beispielschnittstelle anwenden IParser
, können Sie deutlich erkennen, dass sich die Abstraktion vollständig im "bedeutungslosen" Bereich befindet. Entweder es ist etwas Bestimmtes über eine konkrete Implementierung eines Parsers ( zum Beispiel JsonParser
, XmlParser
, ...), oder sollten Sie nur eine Klasse. Es gibt keine "Standardimplementierung" (obwohl dies in einigen Umgebungen tatsächlich Sinn macht, insbesondere COM), entweder gibt es eine bestimmte Implementierung, oder Sie möchten eine abstrakte Klasse oder Erweiterungsmethoden für die "Standardeinstellungen". I
Behalten Sie es jedoch in C # bei, es sei denn, Ihre Codebasis lässt das Präfix bereits weg. Machen Sie sich einfach jedes Mal eine mentale Notiz, wenn Sie Code wie sehen class Something: ISomething
- das bedeutet, dass jemand nicht sehr gut darin ist, YAGNI zu folgen und vernünftige Abstraktionen zu erstellen .
[1] - Technisch gesehen wird dies in Liskovs Artikel nicht speziell erwähnt, aber es ist eine der Grundlagen des ursprünglichen OOP-Artikels und in meiner Lektüre von Liskov hat sie dies nicht in Frage gestellt. In einer weniger strengen Interpretation (die von den meisten OOP-Sprachen verwendet wird) bedeutet dies, dass jeder Code, der einen öffentlichen Typ verwendet, der ersetzt werden soll (dh nicht endgültig / versiegelt), mit jeder konformen Implementierung dieses Typs funktionieren muss.