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?
quelle
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.Antworten:
Wenn Sie darüber nachdenken, werden Sie feststellen, dass sich eine Schnittstelle semantisch nicht wesentlich von einer abstrakten Klasse unterscheidet:
Tatsächlich sind die wichtigsten Unterschiede zwischen Klassen und Schnittstellen:
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
I
ohnehin 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.quelle
I
eine Schnittstelle ausschalten, erhält eine Klasse einen guten Namen für statische Methoden, die der Schnittstelle zugeordnet sind (z. B.Enumerable<T>.Empty
oderComparer<T>.Default
).class Foo
bereits erweitert wurdeclass Bar
, ist nicht sofort ersichtlich, obclass Bar
er erweitert oder implementiert wird. Jetzt müssen Sie in derclass Bar
Kopfzeile nachsehen , ob es in Ordnung ist, Änderungen vorzunehmen, um sieclass Foo
zu erweiternclass 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.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!
quelle
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 ILog
eine Standardimplementierungpublic class Log
.Nun, wenn Sie sich dafür entscheiden, müssen
public interface Log
Sie plötzlich die Klasse Log aufpublic class LogDefault
oder 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.
quelle
Log
das? In die Konsole einloggen? Zum Syslog? Ins Nirgendwo? Die sollten genannt werdenConsoleLog
,SyslogLog
undNullLog
jeweils.Log
ist kein guter Name für eine konkrete Klasse, weil es kein konkreter Name ist.MyClass
es 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ärtMyClass
zuIMyClass
Zuordnungen.)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.
quelle
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 IMyClass
ist unnötige Verdoppelung. Es ist, als würden Sie nicht schreibenpublic 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.quelle
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.
quelle
In meinem (Java-) Code habe ich normalerweise diese Konvention in den APIs, die ich für Aufrufer verfügbar mache:
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.)
quelle
I
Präfixe auf Interfaces. Von Natürlich ist es eine Schnittstelle! Es ist in einer API (oder SPI)!