Sollten Schnittstellennamen mit einem "I" -Präfix beginnen?

73

Ich habe " Clean Code " von Robert Martin gelesen , um hoffentlich ein besserer Programmierer zu werden. Obwohl bisher nichts wirklich bahnbrechend war, habe ich anders darüber nachgedacht, wie ich Anwendungen entwerfe und Code schreibe.

Es gibt einen Teil des Buches, mit dem ich nicht nur nicht einverstanden bin, sondern der für mich keinen Sinn ergibt, insbesondere in Bezug auf die Namenskonventionen für Schnittstellen. Hier ist der Text, der direkt aus dem Buch stammt. Ich habe den Aspekt herausgearbeitet, den ich verwirrend finde, und möchte eine Erläuterung dazu.

Ich ziehe es vor, Schnittstellen schmucklos zu lassen. Das vorangegangene I, wie es in den heutigen Legacy Wads üblich ist, ist bestenfalls eine Ablenkung und im schlimmsten Fall zu viele Informationen. Ich möchte nicht, dass meine Benutzer wissen, dass ich ihnen eine Benutzeroberfläche übergebe .

Vielleicht liegt es daran, dass ich nur ein Student bin, oder vielleicht daran, dass ich noch nie professionell oder teambasiert programmiert habe, aber ich möchte, dass der Benutzer weiß, dass es sich um eine Benutzeroberfläche handelt. Es gibt einen großen Unterschied zwischen der Implementierung einer Schnittstelle und der Erweiterung einer Klasse.

Meine Frage lautet also : "Warum sollten wir die Tatsache verbergen, dass ein Teil des Codes eine Schnittstelle erwartet?"

Bearbeiten

Als Antwort auf eine Antwort:

Wenn Ihr Typ eine Schnittstelle oder eine Klasse ist, ist dies Ihr Geschäft, nicht das Geschäft von jemandem, der Ihren Code verwendet. Sie sollten also keine Details Ihres Codes in diesem Code von Drittanbietern veröffentlichen.

Warum sollte ich nicht die Details darüber "verraten", ob ein bestimmter Typ eine Schnittstelle oder eine Klasse für Code von Drittanbietern ist? Ist es nicht wichtig, dass der Drittanbieter, der meinen Code verwendet, weiß, ob er eine Schnittstelle implementiert oder eine Klasse erweitert? Sind die Unterschiede einfach nicht so wichtig, wie ich sie mir vorstelle?

Charles Sprayberry
quelle
3
Ich stimme Ihrem Punkt zu. Es gibt einen Punkt, an dem zu viele versteckte Informationen nicht sehr hilfreich sind. Selbst wenn Sie diese Richtlinie befolgen, können Sie den Typ dennoch über die IDE oder ein Add-On ermitteln.
NoChance
3
Diese Frage ist im Wesentlichen als die Frage der "ungarischen Notation" bekannt, Sie sollten viele Argumente finden und den Grund, warum die meisten Nicht-MS-Entwickler sie unter diesem Stichwort aufgegeben haben. Die ungarische Schreibweise war für Variablen am weitesten verbreitet, für Typen jedoch im Wesentlichen gleich.
Thiton
2
Die vorherige Bearbeitung des Titels war schrecklich. Bei dieser Frage geht es nicht um die ungarische Notation im Allgemeinen, nur weil darin eine Konvention erwähnt wird, die damit in Verbindung gebracht werden könnte. Die relativen Vorzüge von HN spielen hier keine Rolle. Die Frage bezog sich speziell auf Interfaces vs. Classes und ob die semantischen Unterschiede wichtig / interessant genug sind, um eine spezielle Namenskonvention zu rechtfertigen.
Aaronaught
Betreff to know whether they will be implementing an interface or extending a class: Ja, aber die meisten Benutzer Ihres Codes werden ihn aufrufen, nicht implementieren oder erweitern und es ist ihnen wirklich egal, um welchen Code es sich handelt.
david.pfx
Für das, was es wert ist, bevorzuge ich massiv das "I" -Präfix. Aus dem gleichen Grund verwende ich auch ein "Abstract" -Präfix für abstrakte Klassen. Es macht keinen Unterschied für die Benutzer der Klasse / Schnittstelle, aber es kann einen großen Unterschied für diejenigen bedeuten, die Instanzen davon bereitstellen müssen, und es macht es auch für andere Entwickler, die Ihren Code lesen, viel einfacher. Dies bedeutet, dass sie auf einen Blick sehen können, womit sie es zu tun haben, anstatt die IDE von Fall zu Fall zu Rate ziehen zu müssen, um weitere Informationen zu erhalten. Ich habe gerade mit Angular angefangen und finde es wirklich ärgerlich, dass sie diese Konvention nicht befolgen!
Dan King

Antworten:

67

Wenn Sie darüber nachdenken, werden Sie feststellen, dass sich eine Schnittstelle semantisch nicht wesentlich von einer abstrakten Klasse unterscheidet:

  • Beide haben Methoden und / oder Eigenschaften (Verhalten);
  • Weder sollten nicht-private Felder (Daten) haben;
  • Beides kann nicht direkt instanziiert werden.
  • Von einem abzuleiten bedeutet, alle abstrakten Methoden zu implementieren, sofern der abgeleitete Typ nicht auch abstrakt ist.

Tatsächlich sind die wichtigsten Unterschiede zwischen Klassen und Schnittstellen:

  • Schnittstellen können keine privaten Daten enthalten.
  • Schnittstellenmitglieder können keine Zugriffsmodifikatoren haben (alle Mitglieder sind "öffentlich").
  • Eine Klasse kann mehrere Schnittstellen implementieren (im Gegensatz dazu, dass sie im Allgemeinen nur von einer Basisklasse erben kann).

Da sich die einzig besonders aussagekräftigen Unterschiede zwischen Klassen und Interfaces auf (a) private Daten und (b) Typhierarchie beziehen - die für einen Aufrufer nicht den geringsten Unterschied macht - ist es im Allgemeinen nicht erforderlich zu wissen, ob ein Typ ein Interface ist oder eine Klasse. Sie brauchen die visuelle Anzeige auf keinen Fall.

Allerdings gibt es bestimmte Ecke Fälle bewusst zu sein. Insbesondere, wenn Sie Reflektion, Abfangen, dynamische Proxies / Mixins, Bytecode-Weben, Codegenerierung oder alles verwenden, was direkt mit dem Typensystem oder dem Code der Umgebung zu tun hat - dann ist es sehr hilfreich und manchmal notwendig, es gleich zu wissen Fledermaus, ob Sie mit einer Schnittstelle oder einer Klasse zu tun haben. Offensichtlich möchten Sie nicht, dass Ihr Code auf mysteriöse Weise versagt, weil Sie versucht haben, eine Klasse anstelle einer Schnittstelle als Mixin hinzuzufügen.

Für den typischen, einfachen Geschäftslogikcode müssen jedoch die Unterscheidungen zwischen abstrakten Klassen und Schnittstellen nicht angekündigt werden, da sie niemals ins Spiel kommen.

Abgesehen davon tendiere ich dazu, meinen C # -Schnittstellen Iohnehin Präfixe zu setzen, da dies die von Microsoft verwendete und befürwortete .NET-Konvention ist. Und wenn ich einem neuen Entwickler Codierungskonventionen erkläre, ist es weitaus einfacher, nur die Regeln von Microsoft zu verwenden, als zu erklären, warum wir unsere eigenen "speziellen" Regeln haben.

Aaronaught
quelle
Vielen Dank für diesen Zusammenbruch. +1 für die "sinnvolle Unterscheidung zwischen Klassen und Schnittstellen ..."
Charles Sprayberry
24
+1 für "Abgesehen davon tendiere ich sowieso dazu, meinen C # -Schnittstellen ein Präfix zu setzen, da dies die von Microsoft verwendete und befürwortete .NET-Konvention ist." Das ist Grund genug für mich in C #. Durch Befolgen dieses gemeinsamen Standards ist es wahrscheinlicher, dass andere .NET-Programmierer die Schnittstelle identifizieren.
Robotsushi,
4
Als jemand, der sowohl Java als auch C # schreibt, ist die Aussage "All dies gesagt, ich tendiere dazu, meinen C # -Schnittstellen sowieso ein Präfix zu setzen, weil dies die von Microsoft verwendete und befürwortete .NET-Konvention ist" nicht zu unterschätzen hilft mir, mich zu erinnern, in welcher Sprache ich arbeite
Aidos
3
Ein weiterer Unterschied in .NET (aber nicht in Java) besteht darin, dass in vielen .NET-Sprachen keine Schnittstellen statische Methoden enthalten dürfen. Wenn Sie also Ieine Schnittstelle ausschalten, erhält eine Klasse einen guten Namen für statische Methoden, die der Schnittstelle zugeordnet sind (z. B. Enumerable<T>.Emptyoder Comparer<T>.Default).
Superkatze
Ich habe eine Sorge. Wenn Sie in C ++ einen Header öffnen und feststellen, dass dieser class Foobereits erweitert wurde class Bar, ist nicht sofort ersichtlich, ob class Barer erweitert oder implementiert wird. Jetzt müssen Sie in der class BarKopfzeile nachsehen , ob es in Ordnung ist, Änderungen vorzunehmen, um sie class Foozu erweitern class Baz. Darüber hinaus gibt das Präfix I einen offensichtlichen visuellen Hinweis darauf, ob die "einzelne Basisklasse" verletzt wird oder nicht - so erhalten Sie jedes Mal, wenn Sie einen Header öffnen, eine Plausibilitätsprüfung.
Jsj
14

Konsistenz ist in vielerlei Hinsicht wichtiger als Konvention. Solange Sie in Ihren Benennungsschemata konsistent sind, wird es nicht schwierig sein, mit ihnen zu arbeiten. Präfix-Schnittstellen mit einem I, wenn Sie möchten, oder lassen Sie den Namen einfach schmucklos, es ist mir egal, solange Sie einen Stil auswählen und dabei bleiben!

Jim Nutt
quelle
2
+1 für Konsistenz> Konvention. In C # würden Sie ein I voranstellen und in Java nicht. Unterschiedliche Aufgaben
erfordern
2
+1, während ich persönlich es vorziehen würde, Is nicht auf meinen Schnittstellen zu haben, ist es imho keine Option, mit den in .NET-Projekten verwendeten BCL- und 3rd-Party-Bibliotheken inkonsistent zu sein.
jk.
13

Das Buch ist voller guter Sachen, aber ich würde immer noch "I" zum Namen der Benutzeroberfläche hinzufügen.

Stellen Sie sich vor, Sie haben public interface ILogeine Standardimplementierung public class Log.

Nun, wenn Sie sich dafür entscheiden, müssen public interface LogSie plötzlich die Klasse Log auf public class LogDefaultoder etwas in diesen Zeilen ändern . Du bist von einem zusätzlichen Charakter auf sieben gewechselt - das ist sicherlich verschwenderisch.

Die Grenze zwischen Theorie und Praxis ist oft sehr eng. Theoretisch ist das eine wirklich gute Idee, aber in der Praxis ist es nicht so gut.

CodeART
quelle
Dies wird auch in der .NET- Dokumentation
levininja
30
"Stellen Sie sich vor, Sie haben eine öffentliche Schnittstelle ILog mit einer öffentlichen Standardimplementierungsklasse Log." - Dann haben Sie einen schlechten Namen für Ihre Standardimplementierung. Was macht Logdas? In die Konsole einloggen? Zum Syslog? Ins Nirgendwo? Die sollten genannt werden ConsoleLog, SyslogLogund NullLogjeweils. Logist kein guter Name für eine konkrete Klasse, weil es kein konkreter Name ist.
Sebastian Redl
7
Persönlich ist dies genau der Grund, warum ich es hasse, ITheClassName zu sehen. Erstellen Sie nur ein Interface ohne Grund? Ist es nur Rauschen über TheClassName? Wenn Ihre Schnittstelle einen Zweck hat, sollte es möglich sein, ihr einen besseren Namen zu geben als ITheClassName
Richard Tingle
Der Name der Klasse wird einmal verwendet (um zu instanziieren), der Name der Schnittstelle wird überall verwendet. Das Hinzufügen eines Buchstabens zur Schnittstelle, um einen Buchstaben auf dem Klassennamen zu speichern, ist eine falsche Wirtschaftlichkeit (von Zeichen).
Sergut
@RichardTingle Aber ich denke die gebräuchliche Verwendung dort ist einfach so, dass MyClasses eine spöttische Variante gibt, oder? Das heißt, wir verwenden häufig Schnittstellen, um öffentliche Methoden auf eine Weise wirklich öffentlich zu machen , die es ermöglicht, sie zu testen. (Nicht sagen , das ist gut oder schlecht, aber zu verstehen , dass die Motivation für Schnittstellen oft einfach ist Klassen zu machen , die Abhängigkeiten sind leicht mockable die 1-zu-1 nicht erklärt MyClasszu IMyClassZuordnungen.)
ruffin
8

Hier geht es nicht darum, die Schnittstelle zu implementieren oder eine Klasse zu erweitern. In diesen Fällen wissen Sie sowieso, was Sie tun.

Wenn jedoch Code von Drittanbietern (beispielsweise ein anderes Modul der Anwendung) Ihre Daten manipuliert, sollte es diesem Code egal sein, ob Sie eine Schnittstelle oder eine Klasse präsentieren.

Das ist der springende Punkt der Abstraktion. Sie präsentieren diesem Drittanbietercode ein Objekt eines bestimmten Typs. Dieser gegebene Typ hat eine Elementfunktion, die Sie aufrufen können. Das ist genug.

Wenn Ihr Typ eine Schnittstelle oder eine Klasse ist, ist dies Ihr Geschäft, nicht das Geschäft von jemandem, der Ihren Code verwendet. Sie sollten also keine Details Ihres Codes an diesen Code eines Drittanbieters weitergeben.

Schnittstellen und Klassen sind übrigens am Ende Referenztypen. Und darauf kommt es an. Dies ist also, was Ihre Namenskonvention hervorheben muss.

deadalnix
quelle
@Mat> Ja, das war ein Fehler von mir und ich habe ihn bearbeitet.
Deadalnix
5

Die meisten Antworten gehen davon aus, dass die Programmiersprache entweder Java oder C # ist, wo es ein Konzept (oder ein Schlüsselwort) für Interfaces / abstrakte Klassen gibt. In diesen Fällen ist in einer anständigen IDE sofort ersichtlich, mit welcher Art von Klasse man sich befasst, und das Schreiben public interface IMyClassist unnötige Verdoppelung. Es ist, als würden Sie nicht schreiben public final FinalMyClass- stellen Sie sich vor, Sie schreiben jedes Schlüsselwort in den Klassennamen.

Meiner Meinung nach ist die Situation in C ++ jedoch etwas anders, da man in die Header-Datei eintauchen und prüfen muss, ob die Klasse über virtuelle Methoden verfügt, um herauszufinden, ob es sich um eine abstrakte Klasse handelt. In diesem Fall sehe ich deutlich ein Argument zum Schreiben class AbstractMyClass.

Meine Antwort wäre also: Es kommt auf die Programmiersprache an.

Aktualisiertes 2016: 2 Jahre C ++ - Erfahrung später Ich würde es jetzt wahrscheinlich für eine schlechte Praxis halten, Klassennamen mit einem Präfix zu versehen Abstract, obwohl ich sagen würde, dass es wahrscheinlich Codebasen gibt, die so komplex sind, dass es möglicherweise Sinn ergibt.

Ela782
quelle
Sind Sie sicher, dass dies die Frage beantwortet? Vielleicht möchten Sie die anderen Antworten lesen und einige Ihrer Punkte klären.
david.pfx
C ++ hat definitiv Schnittstellen, auch wenn es kein Schlüsselwort dafür gibt. Wie nennt man eine Klasse, in der jede Member-Funktion rein virtuell ist und keine Member-Variablen vorhanden sind?
@ david.pfx: Ich denke, ich beantworte genau die Frage "Sollen Schnittstellennamen mit einem" I "-Präfix beginnen?": Das hängt von der Programmiersprache ab. Wenn Sie der Meinung sind, dass meine Ausarbeitung nicht klar genug ist, bin ich gerne bereit, sie zu verbessern - welche Teile sind für Sie nicht klar?
Ela782
2
@ JohnGaughan: Sicher hat es Schnittstellen! Aber mein ganzer Punkt ist , dass es ist über das Schlüsselwort. C ++ hat keine, daher ist es für die IDE / den Benutzer nicht sichtbar, ob er / sie sich mit einer Schnittstelle befasst oder nicht. In Java ist es jedoch sofort sichtbar.
Ela782
1
Lesen Sie die Antwort mit der besten Bewertung und vergleichen Sie Ihre. Sie sind neu in SO, und dies ist eine Gelegenheit zu erfahren, welche Arten von Antworten Stimmen anziehen. So wie es aussieht, wird deins nicht. Mit mehr Arbeit, mehr Erklärung, mehr Wert könnte es sein. Halte durch!
david.pfx
4

Befolgen Sie die Redewendungen der von Ihnen verwendeten Sprache.

Jede Sprache hat ihre eigenen Redewendungen und Kodierungsrichtlinien. Beispielsweise ist es in C # idiomatisch, Schnittstellen mit I zu versehen. In Go ist dies nicht der Fall.

Pete
quelle
+1 Befolgen Sie die Redewendungen der von Ihnen verwendeten Sprache. Offensichtlich ist der Buchautor ein Java-Entwickler. .NET / C #: ILog Java: Log Aber der Nachteil: Wenn ich eine Log-Klasse mag, die die ILog-Schnittstelle implementiert .... MyLog, LogDefault, LogBase, ...? Hässlich, nicht wahr? Hässlicher ist: LogImpl ....: D
hfrmobile
0

In meinem (Java-) Code habe ich normalerweise diese Konvention in den APIs, die ich für Aufrufer verfügbar mache:

  • Funktionalität wird durch Schnittstellen bereitgestellt und niemals durch Klassen.
  • Nichtfunktionale Teile sind Klassen.

Mit "nicht funktionsfähig" meine ich Dinge wie reine Datenstrukturen (wie Klassen, die als Brücken zur XML-Serialisierung oder zum ORM fungieren), Aufzählungen und Ausnahmen, die keine Schnittstellen sein können (Sie können es auch für die reinen Datenklassen tun) , aber es ist eine Menge Arbeit für sehr wenig Gewinn, da diese Klassen nichts tun, außer Daten zu halten.

In Bezug auf Benennungskonventionen werden Schnittstellen in der Regel entweder Akteursubstantiven (z. B. FooBarWatcher) oder Adjektiven (z. B. FooBarWatchable) zugeordnet, und sowohl reine Datenklassen als auch Aufzählungen werden nicht aktiven Substantiven (z. B. FooBarStatus) zugeordnet. Die IDE kann den API-Konsumenten ohne spezielle Namenskonventionen führen. Ausnahmen folgen üblichen Java - Konventionen ( FooBarException, FooBarError, FooBarFault) natürlich.

Ich werde die Schnittstellen auch oft in ein eigenes Paket oder sogar in eine eigene Bibliothek packen, um sicherzustellen, dass ich nicht versucht bin, meine eigenen Regeln zu brechen. (Dies hilft mir auch beim Verwalten des Builds, wenn ich den Code aus externen Quellen wie WSDL-Dokumenten ableite.)

Donal Fellows
quelle
Ich setze auch nie IPräfixe auf Interfaces. Von Natürlich ist es eine Schnittstelle! Es ist in einer API (oder SPI)!
Donal Fellows