API-Design. Kai SpichaleЧитать онлайн книгу.
deshalb finden Sie in diesem Kapitel weitere Informationen darüber.
Effizienz
Insbesondere für mobile Applikationen ist geringer Akku-Verbrauch und geringes Online-Datenvolumen wichtig. Remote-APIs und eventuell dazugehörige Software Development Kits (SDKs), mit denen die Remote-APIs aufgerufen werden, sollten dies berücksichtigen. Auch Skalierbarkeit kann ein wichtiges Ziel sein, falls Sie beispielsweise davon ausgehen, dass Ihre API in Zukunft immer häufiger aufgerufen wird. Kapitel 14 bietet weitere Informationen zu diesem Thema.
Zuverlässigkeit
Die Reife einer API-Implementierung hängt von der Versagenshäufigkeit durch Fehlerzustände ab. Interessant für API-Designer ist vor allem die Frage, wie man mit Fehlern umgehen soll. Informationen über Exception Handling und zur Fehlerbehandlung von Web-APIs finden Sie in Abschnitt 5.8 und 9.4.
2.2Benutzbarkeit
Wann ist eine API gut benutzbar? Vermutlich kann diese Frage nur mit einer subjektiven Einschätzung beantwortet werden. Dennoch gibt es eine Reihe allgemein akzeptierter Eigenschaften. Weil aber diese Eigenschaften in der Praxis nie vollständig umgesetzt werden können, könnte man auch von Zielen sprechen:
Konsistent
Intuitiv verständlich
Dokumentiert
Einprägsam und leicht zu lernen
Lesbaren Code fördernd
Schwer falsch zu benutzen
Minimal
Stabil
Einfach erweiterbar
Diese Eigenschaften werden in den folgenen Abschnitten vorgestellt.
2.2.1Konsistent
Kohärentes Design mit der Handschrift eines Architekten
»Konsistenz« deckt sich weitestgehend mit »konzeptioneller Integrität«. Dieses Grundprinzip besagt, dass komplexe Systeme ein kohärentes Design mit der Handschrift eines Architekten haben sollten. Dieses Designprinzip stammt von Frederick Brooks, der bereits vor mehreren Jahrzehnten schrieb: »Konzeptionelle Geschlossenheit ist der Dreh- und Angelpunkt für die Qualität eines Produkts [...]« [Brooks 2008]. Er meint damit, dass Entwurfsentscheidungen, wie beispielsweise Namensgebungen und die Verwendung von Mustern für ähnliche Aufgaben, im gesamten System durchgängig angewandt werden sollen. Das folgende Beispiel soll diese Aussage verdeutlichen. Zu sehen sind zwei Listen mit Funktionsnamen [Lacker 2013]:
str_repeat strcmp
str_split strlen
str_word_count strrev
Die Liste auf der linken Seite beginnt mit einem Präfix »str«. Darauf folgen die Funktionsbezeichnungen, wobei die einzelnen Wörter durch Unterstriche voneinander getrennt sind. Die Funktionsnamen auf der rechten Seite sind ähnlich aufgebaut. Der Unterschied ist, dass man hier auf die Unterstriche verzichtet hat. Vermutlich haben Sie schon erkannt, dass es sich hierbei um die Bezeichnungen von Funktionen zur Bearbeitung von Zeichenketten handelt. Beide Namenskonventionen sind in Ordnung. Es ist eine Frage des persönlichen Geschmacks, welche man bevorzugt.
Was ist das Problem? Das Problem ist, dass beide Namenskonventionen zur gleichen PHP-API gehören. Das bedeutet, dass sich Entwickler nicht nur die Namen der Funktionen, sondern auch ihre Namenskonvention merken müssen. Aus diesem Grund sollte eine API unbedingt die (nur eine) Handschrift eines Architekten1 tragen.
Auch im Java Development Kit (JDK) lassen sich leicht Beispiele finden. Das Wort »Zip« wird im selben Package mal mit CamelCase und mal komplett in Großbuchstaben geschrieben:
java.util.zip.GZIPInputStream
java.util.zip.ZipOutputStream
Das Setzen des Textes eines Widgets ist nicht einheitlich im JDK gelöst. Mehrheitlich heißt die Methode setText, aber leider gibt es Abweichungen:
java.awt.TextField.setText();
java.awt.Label.setText();
javax.swing.AbstractButton.setText();
java.awt.Button.setLabel();
java.awt.Frame.setTitle();
2.2.2Intuitiv verständlich
Die zweite wichtige Eigenschaft einer guten API ist intuitive Verständlichkeit. Eine intuitiv verständliche API ist in der Regel auch konsistent und verwendet einheitliche Namenskonventionen. Das bedeutet, dass gleiche Dinge die gleichen Namen haben. Und umgekehrt haben unterschiedliche Dinge auch unterschiedliche Namen. Dadurch ergibt sich eine gewisse Vorhersagbarkeit. Betrachten wir dazu ein weiteres Beispiel:
Ruby-Methoden mit Ausrufezeichen (!)
Ruby-Methoden, die mit einem Ausrufezeichen (!) enden, ändern das Objekt, auf dem sie aufgerufen wurden. Methoden ohne Ausrufezeichen am Namensende erzeugen hingegen eine neue Instanz und lassen das Objekt, auf dem sie aufgerufen wurden, unverändert.
my_string.capitalize
# Funktioniert wie capitalize, erzeugt aber keinen neuen String
my_string.capitalize!
my_string.reverse
# Funktioniert wie reverse, erzeugt aber keinen neuen String
my_string.reverse!
Nachdem Sie die Beispiele für capitalize! und reverse! gesehen haben, können Sie vermutlich das Namenspaar für »downcase« erraten.
Setter- und With-Mehoden
Für Java gibt es ebenfalls derartige Konventionen. Eine Konvention betrifft Setter-Methoden wie setName, setId oder setProperty. Setter-Methoden ändern das aufgerufene Objekt. Methoden wie with-Name, withId oder withProperty ändern das aufgerufene Objekt nicht, sondern erzeugen ein neues Objekt mit den angegebenen Werten. Das Präfix »with« wird beispielsweise von Joda-Time genutzt.
Methoden der Java-Collections
Ein anderes anschauliches Beispiel sind die Collections der Java-Standardbibliothek. Die starken Begriffe add, contains und remove wurden hier etabliert und da, wo es passt, wiederverwendet. Man findet diese Methoden einheitlich in den Interfaces List und Set. Für Map wurde leider ein anderer Name verwendet. Die Methode zum Hinzufügen von Elementen heißt dort put. Zugegeben, diese Methode hat andere Parameter und funktioniert nicht wie add von List, aber auch zwischen List und Set gibt es Unterschiede. Ein einheitliches add in allen drei Interfaces wäre vermutlich besser gewesen.
Die folgende Tabelle zeigt die Interfaces von List, Set und Map im Vergleich:
java.util.List | java.util.Set | java.util.Map |
add | add | put |
addAll | addAll |
|