zur Übersicht
12. Okt 2016

Naming von Interfaces und Implementierungen

Wie ist eure Konvention für Implementierungsklassen? Lieber FormDataValueImpl oder IFormDataValue ?

Eigentlich müsste man froh über so eine Frage sein - immerhin kümmert sich überhaupt jemand um Namenskonventionen … aber irgendwie ist doch beides Murks. Leider sind die oben genannten Muster so üblich geworden, dass meine Kritik wohl wenig Wirkung zeigen wird. Hier folgt sie:


Kritik an Single-Implementation Interfaces

Ganz allgemein sollen diese Namenskonventionen häufig für das Muster “Ein Interface mit genau einer Implementierung” eingesetzt werden. Meiner Ansicht nach ist die Idee ein Interface könnte nur genau eine sinnvolle Implementierung haben schon der falsche Ansatz.

Fast überall wo dieses Muster verwendet wird, finde ich casts vom Interface-Typ auf den Implementierungstyp, z.B.

Interface intrfc = ...;

// other code

InterfaceImpl impl = (InterfaceImpl) intrfc;
impl.doNonInterfaceAction();

Die erstellenden Entwickler wissen meist, dass dies nicht erwünscht ist, aber Drittnutzer behelfen sich doch gerne damit, wenn sie die korrekte Funktion einfach nicht finden.

Synonymartige Verwendung von Interface und Implementierung verstößt gleich gegen mehrere SOLID-Prinzipien. Auf jeden Fall ist die Erzeugung einer zweiten Implementierung dann praktisch nicht mehr ohne Aufräumarbeiten in den abhängigen Implementierungen möglich.

I ist ein syntaktischer Hinweis

Wer vor jedes Interface ein I hängt, gibt dem Leser syntaktisch einen Hinweis, den der Compiler nicht berücksichtigt. Das führt zu einer Redudanz mit dem Schlüsselwort interface, aber auch dazu, dass die Transparenz zwischen Klassen und Interfaces künstlich aufgehoben wird.

Ich empfand es bisher als Gewinn, dass ich ein Objekt benutzen kann ohne wissen zu müssen ob es sich um ein Interface oder eine Klasse handelt. Natürlich muss ich das jetzt nicht bei der Benutzung wissen, aber wenn ich als Entwickler der Klasse entscheide, dass ein Interface besser gewesen wäre, so ist das ohne Renaming nicht mehr möglich.

Abgesehen handelt es sich bei dieser Konvention um eine instabile Regel. Wenn eine API einmal “falsche” Namenskonventionen verwendet und kompatibel bleiben möchte, muss sie diese weiter verwenden. Wenn hingegen die Implementierungsklasse eine Namenskonvention erhält lässt sich eine Umbenennung der selben ohne weiteres machen - wenn man konsequent Interfaces verwendet hat.

Robert C. Martin schreibt in Clean Code eine ähnliche Kritik und gibt der anderen Namenskonvention schweren Herzens den Vorzug.

Impl ist faul

Wer ein interface FormDataValue hat, und sich keine großen Gedanken über die Implementierung macht, nennt seine neue Implementierung FormDataValueImpl . Kein lästiges Nachdenken, keine Zeitverschwendung. Aber gerade beim Naming sollte man eben Nachdenken, um späteren Lesern das Verstehen zu vereinfachen …

Ich habe ja schon erwähnt, dass Robert C. Martin dieser Konvention den Vorzug gibt (auch wenn ich bezweifle, dass er sie je eingesetzt hat). Dennoch findet man in Clean Code viele gute Gründe, warum man davon ebenfalls Abstand halten sollte:

Wenn ich ein Interface habe, dann muss die Implentierung irgendeine spezifische Eigenschaft haben . Und die könnte ich doch dann auch in den Namen einfließen lassen. Impl gibt mir als Benutzer der Klasse nun wirklich gar keine Information über die Vererbungshierarchie hinaus.

Ein weiteres Problem ist aus meiner Sicht, dass die Impl -Konvention inzwischen sehr präsent ist und von vielen Nutzern bereits auf das Muster “Single-Implementation Interface” interpretiert wird. Folge ist, dass die API dann gerne auch mal falsch verwendet wird, oder dass die effizienteren Implementierungen eben nicht verwendet werden.

Besseres OO-Design, bessere Namenskonventionen

Um der Kritik etwas konstruktives zu geben möchte ich einen Gegenvorschlag machen. Mag sein, dass der nicht optimal ist, aber zumindest erfüllt er nicht die Kritikpunkte von oben.

Zunächst würde ich einfach sicher stellen, dass kein Interface genau eine Implementierung hat. Wenn es genau eine Implementierung gibt und das in Zukunft auch so bleiben wird, dann sollte man einfach überlegen ob man das Interface überhaupt benötigt und eventuell zugunsten der Implementierung aufgibt.

Wenn das aus irgendwelchen Gründen nicht geht und man dann ein Interface FormDataValue hat und bisher nur eine Implementierung, dann sollte man einen geeigneten Namen finden. Der Namen sollte die Eigenschaften der Implementierung charakterisieren, z.B.

  • PrimitiveFormDataValue (wenn der enthaltene Datentyp charakteristisch ist) oder
  • WebFormDataValue (wenn der Verwendungskontext charakteristisch ist)
  • DirectFormDataValue (wenn der Wert nicht gecached wird)

Wenn wirklich nichts passen mag, dann haben wir zumindest ein Charakteristikum, was für die Implementierung zutrifft: Es ist die Standardimplementierung auf die zurückgegriffen wird. Somit wäre auch DefaultFormDataValue ein valider Name.

Nun mag der eine oder andere vielleicht Kritik äußern was an einem Präfix Default besser sein soll als an einem Suffix Impl . Nun - da gibt es mehrere Argumente:

  • Logisch kann ein Interface mehrere Implementierungen Impl haben, aber nur einen Default.
  • Technisch ist es aber nicht möglich mehrere Impls zu haben (Namenskollision), technisch wird hingegen erzwungen, dass es genau einen Default gibt (Da es ohnehin nur einen default gibt, hilft uns die Namenskonvention sogar).
  • Default ist robuster für zukünftige Implementierungen. Wenn jetzt eine kompakte Implementierung dazu kommt, dann stünde eine FormDataValueImpl neben einer FormDataValueCompactImpl. Im anderen Fall stünde neben DefaultFormDataValue ein CompactFormDataValue.
  • Default deutet darauf hin, dass es eventuell Non-Default-Implementierungen gibt. Jeder der also in den Implementierungstyp castet wird auf jeden Fall daran erinnert, dass er hier eventuell auf andere Implementierungen prüfen sollte.
  • Es kann ja vorkommen, dass eine neue Implementierung nachträglich die alte ersetzen soll (z.B. sein CompactDataValue ist der neue default). Wenn ich eine Umbenennung von CompactDataValue in DefaultDataValue vornehme weiß jeder was damit intendiert ist, CompactDataValue nach DataValueImpl hingegen ist eher kontraintuitiv.