44,90 €
Schnittstellen-Know-how für die Programmierung - Das Standardwerk zur API-Programmierung - Effektive APIs entwerfen - APIs für professionelle Projekte entwickeln Application Programming Interfaces (APIs) sind allgegenwärtig, denn Softwareentwickler benutzen sie nicht nur ständig, sondern entwerfen sie häufig auch. Dieses Buch bietet erstmals eine umfassende Anleitung für das vielfältige Thema API-Design. Neben theoretischen Konzepten werden zahlreiche praktische Hinweise und Programmbeispiele für Java-APIs gegeben. Remote APIs in Form von RESTful HTTP, GraphQL und Messaging, die für moderne Webanwendungen und andere verteilte Systeme enorm wichtig sind, spielen in diesem Buch ebenfalls eine zentrale Rolle. Aus dem Inhalt: - Entwurf leicht benutzbarer APIs - Kompatibilität und Versionierung - Objektkollaboration, Fluent APIs und Thread-Sicherheit - Dokumentation - Skalierbarkeit, Caching - API-Management Nach der erfolgreichen Lektüre dieses Buches kennen Sie die Grundlagen von APIs und sind in der Lage, objektorientierte APIs für Softwarekomponenten und Remote APIs für verteilte Systeme zu entwerfen. In der dritten Auflage sind u. a. der API-first-Ansatz und Sicherheitsthemen wie Authentifizierung, API-Keys, Distributed Denial of Service (DDos) und Injection-Angriffe hinzugekommen.
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Seitenzahl: 479
Veröffentlichungsjahr: 2025
Kai Spichale verantwortet die strategische und technische Weiterentwicklung digitaler Plattformen und leitet ein Team in einem internationalen IT-Unternehmen. Dabei trägt er die Verantwortung für Plattformen, die auf einer Kombination aus Cloud-Lösungen und maßgeschneiderten Diensten basieren. Nach seinem Studium am Hasso-Plattner-Institut sammelte er umfangreiche Erfahrungen als Softwarearchitekt und IT-Berater. Sein technologischer Fokus liegt auf modernen Architekturansätzen, API-Design und Datenbanktechnologien. Kai Spichale lebt mit seiner Familie in Berlin.
Kai Spichale
Praxishandbuch für Java- undWebservice-Entwickler
3., überarbeitete und erweiterte Auflage
Kai Spichale
Lektorat: Sandra Bollenbacher, Alissa Melitzer
Buchmanagement: Julia Griebel
Copy-Editing: Annette Schwarz, Ditzingen
Satz: inpunkt[w]o, Wilnsdorf, www.inpunktwo.de
Herstellung: Stefanie Weidner
Umschlaggestaltung: Eva Hepper, Silke Braun
Bibliografische Information der Deutschen Nationalbibliothek
Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.
ISBN:
978-3-98889-034-4
978-3-98890-232-0
ePub
978-3-98890-233-7
3., überarbeitete und erweiterte Auflage 2025
Copyright © 2025 dpunkt.verlag GmbH
Wieblinger Weg 17
69123 Heidelberg
E-Mail: [email protected]
Schreiben Sie uns:
Falls Sie Anregungen, Wünsche und Kommentare haben, lassen Sie es uns wissen: [email protected].
Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Verlags urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen.
Weiter darf der Inhalt nicht zur Entwicklung, zum Training oder zur Anreicherung von KI-Systemen, insbesondere generativen KI-Systemen, verwendet werden. Die Nutzung für Text- und Data Mining ist untersagt. Es wird darauf hingewiesen, dass die im Buch verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen.
Alle Angaben und Programme in diesem Buch wurden mit größter Sorgfalt kontrolliert. Weder Autor noch Verlag können jedoch für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieses Buches stehen.
Für Marion und Rudolf
Copyright und Urheberrechte:
Die durch die dpunkt.verlag GmbH vertriebenen digitalen Inhalte sind urheberrechtlich geschützt. Der Nutzer verpflichtet sich, die Urheberrechte anzuerkennen und einzuhalten. Es werden keine Urheber-, Nutzungs- und sonstigen Schutzrechte an den Inhalten auf den Nutzer übertragen. Der Nutzer ist nur berechtigt, den abgerufenen Inhalt zu eigenen Zwecken zu nutzen. Er ist nicht berechtigt, den Inhalt im Internet, in Intranets, in Extranets oder sonst wie Dritten zur Verwertung zur Verfügung zu stellen. Eine öffentliche Wiedergabe oder sonstige Weiterveröffentlichung und eine gewerbliche Vervielfältigung der Inhalte wird ausdrücklich ausgeschlossen. Der Nutzer darf Urheberrechtsvermerke, Markenzeichen und andere Rechtsvorbehalte im abgerufenen Inhalt nicht entfernen.
Es gibt bereits sehr gute Bücher über Softwarearchitektur und Design. Warum dann noch dieses Buch über den Entwurf von Application Programming Interfaces, kurz APIs? Weil meiner Meinung nach der Bedarf dafür existiert! Denn API-Design unterscheidet sich von den klassischen objektorientierten Design-Heuristiken, die mit einem internen Datenmodell beginnen und versuchen, Applikationen im Hinblick auf Wartbarkeit und Flexibilität zu optimieren. Im Gegensatz dazu nimmt API-Design die Perspektive der Benutzer, d. h. anderer Entwickler, ein und versucht, die Benutzung von Komponenten oder Diensten durch gutes API-Design für diese Entwickler möglichst einfach zu machen. API-Design ist deswegen nicht nur wichtig für Open-Source-Frameworks, auch unternehmensinterne Softwarekomponenten können davon profitieren. Ein anderer Grund für dieses Buch ist die schnell wachsende Anzahl intern und extern eingesetzter Web-APIs. Deswegen finden Sie in diesem Buch Techniken und Best Practices für Java-, Web- und Messaging-APIs.
Warum ist API-Design wichtig?
APIs gibt es in unterschiedlichsten Formen und Größen: Sie reichen von der Betriebssystem-API POSIX über die Web-API der Plattform X bis hin zur API der Java-Klassenbibliothek und der Web-API des Cloud-Speicherdienstes Amazon S3.
APIs sind der Kleber, der unsere digitale Welt zusammenhält.
Als Softwareentwickler arbeiten wir ständig mit APIs – aber wir nutzen sie nicht nur, wir entwerfen sie auch. Denn wenn Sie Softwareentwickler sind, sind Sie zwangsläufig auch API-Designer. Warum? Eine gute Softwarearchitektur ist in Module strukturiert, und jedes dieser Module benötigt eine API, über die es angesprochen werden kann. Die Qualität dieser APIs entscheidet maßgeblich darüber, wie leicht oder schwierig die Integration der Module ist.
Nützliche Module werden häufig wiederverwendet, weshalb ihre APIs nicht beliebig verändert werden sollten. Wenn eine API beispielsweise von drei anderen Applikationen genutzt wird, gibt es mindestens drei gute Gründe, bei Änderungen auf Kompatibilität zu achten.
APIs können zu den wertvollsten Assets eines Unternehmens gehören.
Durch die Bereitstellung von APIs können Kunden oder Partner einen Dienst nahtlos integrieren. Oft wird daher die API selbst als das eigentliche Produkt betrachtet. Kunden investieren erhebliche Ressourcen, sowohl Zeit als auch Geld, in die Implementierung einer API. Die Kosten für den Wechsel zu einer anderen API sind in der Regel hoch, was zu einer starken Kundenbindung führt. Ein Beispiel dafür ist die Web-API des Kurznachrichtendienstes X, die von Zehntausenden registrierten Anwendungen genutzt wird.
APIs können zu den größten Verbindlichkeiten eines Unternehmens gehören.
APIs können nicht nur wertvoll für Unternehmen sein, sie können auch eine Last darstellen. Eine schlechte API oder eine schlechte Dokumentation kann unzählige Supportanfragen zur Folge haben. Aber eine schlechte API kann nicht mal eben verändert werden. Aus diesem Grund erfahren Sie in diesem Buch, was alles beim API-Design zu beachten ist.
Eine Frage der Perspektive
Zweifellos gibt es Softwareentwickler, die APIs korrekt entwickeln, sonst gäbe es nicht so viele gute Applikationen, Frameworks und Webservices. Doch es scheint so, als ob die Prinzipien des API-Designs häufig nur unbewusst durch Erfahrung erlernt werden. Softwareentwickler folgen Regeln, ohne sich dessen bewusst zu sein oder deren zugrunde liegende Motive zu kennen.
Beim API-Design geht es um Kommunikation zwischen Entwicklern.
APIs werden nicht für Computer geschrieben, sondern für Menschen. Was bedeutet das? Wir schreiben Software nur in ganz wenigen Ausnahmen in Isolation. Vielmehr baut unsere Software auf existierenden Komponenten und Services auf, deren APIs wir auswählen und verstehen müssen. Weil diese Komponenten und Services von anderen Entwicklern geschrieben wurden, ergibt sich ein Kommunikationsproblem. Denn in den wenigsten Fällen können uns diese Entwickler persönlich erklären, wie die API funktioniert. Daher muss die API selbst der primäre Kommunikationskanal sein. Das kann nur funktionieren, wenn eine API klar, einfach und gut dokumentiert oder sogar selbsterklärend ist.
Können Sie voraussetzen, dass die Benutzer Ihrer API diese bis ins letzte Detail verstehen? Das Gegenteil ist oftmals der Fall: Entwickler müssen ihren Job möglichst schnell erledigen. Da bleibt keine Zeit, alles über eine API in Erfahrung zu bringen, bevor man sie benutzt. Entwickler rufen eine Methode der API auf und schauen, was passiert. Wenn das gewünschte Verhalten eintritt, sind sie fertig, andernfalls probieren sie etwas anderes. Entwickler fangen häufig mit Erfahrung an und das Verständnis folgt später, in manchen Fällen nie [Tulach 2008]. Dieses Prinzip der Ahnungslosigkeit ist sogar gewünscht. Denn darum geht es ja gerade bei Modularisierung, Wiederverwendung und dem Geheimnisprinzip. Aus diesem Grund muss eine API intuitiv verständlich sein. Sie sollte keine Überraschungen bereithalten und es Entwicklern schwer machen, sie falsch zu benutzen.
Die Perspektive ist beim API-Design entscheidend. APIs sollten aus der Perspektive ihrer potenziellen Benutzer entworfen werden.
Wer sind die Benutzer? Was wollen sie mit der API machen? Welche Technologien benutzen sie? Das sind Fragen, die API-Designer beantworten müssen, um eine erfolgreiche und gute API zu entwerfen.
Zielgruppe und Voraussetzungen
Dieses Buch richtet sich an Softwareentwickler und -architekten, die APIs für Frameworks, Bibliotheken oder andere Softwarekomponenten entwickeln. Aber prinzipiell ist das in diesem Buch vorgestellte API-Design für jeden Entwickler interessant, der Code schreibt, der von anderen Entwicklern wiederverwendet wird. Zu Beginn des Buches werden allgemeine Konzepte, Qualitätsmerkmale und Vorteile des API-Designs beschrieben. Dann folgen praktische Tipps und Best Practices für Java-Softwarekomponenten. Eine Übertragung auf andere Programmiersprachen ist durchaus möglich, muss aber durch den Leser erfolgen.
Das Buch richtet sich auch an Softwareentwickler und -architekten, die Web-APIs entwickeln und dafür REST und HTTP einsetzen. Für mobile Applikationen, IoT-Szenarien, zur Integration von Microservices etc. eignen sich auch Messaging-APIs, die ebenfalls in diesem Buch betrachtet werden. Praktische Erfahrungen mit diesen Technologien sind sicherlich von Vorteil, aber keine zwingende Voraussetzung, denn alle Konzepte und Technologien werden erklärt.
Struktur des Buches
In diesem Buch werden Sie sowohl allgemeine Konzepte als auch konkrete Techniken und Best Practices für unterschiedliche APIs und Protokolle kennenlernen. Aus diesem Grund ist das Buch in vier Teile gegliedert, die wiederum aus mehreren Kapiteln bestehen.
Teil I: Grundlagen
Der erste Teil des Buches umfasst wichtige Grundlagen und besteht aus den Kapitel 1 bis Kapitel 3:
Kapitel 1
beginnt mit einem Überblick über die Geschichte der APIs. In diesem Einstieg werden Zweck, Funktion und Bedeutung von APIs beschrieben.
Kapitel 2
stellt die Qualitätsmerkmale vor, die beim API-Design berücksichtigt werden sollten. Diese Merkmale sind die Voraussetzung für alle weiteren Designtechniken in diesem Buch.
Kapitel 3
beschreibt das allgemeine Vorgehen beim Entwurf von APIs. Für den Entwurf werden Beispiele eingesetzt, die zeigen, wie die API von Clients in verschiedenen Szenarien benutzt werden soll.
Teil II: Java-APIs
Nach der allgemeinen Einführung geht es im zweiten Teil um objektorientierte Java-APIs:
Kapitel 4
beschreibt die vielfältigen Ausprägungen von objektorientierten APIs, die man in Bibliotheken, Frameworks und anderen Softwarekomponenten findet.
Kapitel 5
stellt grundlegende Techniken und Best Practices für Java-APIs vor. Hierzu zählen codenahe Themen wie Command/Query Separation, Design für Vererbung, Verwendung von Interfaces und Exception Handling.
Kapitel 6
betrachtet Techniken, die größeren Einfluss auf die Architektur der Anwendung haben, aber doch zum API-Design zählen.
Kapitel 7
führt in das Thema Kompatibilität ein. In diesem Zusammenhang werden theoretische Grundlagen und praktische Techniken vorgestellt.
Teil III: Remote-APIs
An dieser Stelle verlassen wir die Welt der Java-APIs und kommen zu den Remote-APIs, die primär zur Integration unterschiedlicher Systeme eingesetzt werden. Konkret geht es im dritten Teil um RESTful HTTP, SOAP-Webservices und Messaging-APIs:
Kapitel 8
führt in den Architekturstil REST ein und beschreibt dessen Grundprinzipien anhand von RESTful HTTP.
Kapitel 9
behandelt Techniken für Web-APIs und deckt dabei Themen wie Ressourcendesign, Medientypen und Fehlerbehandlung ab. Zudem wird das Thema API-Sicherheit erläutert. Abschließend gibt das Kapitel eine Einführung in GraphQL, OData und gRPC.
Kapitel 10
behandelt Designtechniken für SOAP-Webservices. Dabei werden Granularität, Message Exchange Patterns, Datentypen und Versionierung beschrieben.
Kapitel 11
stellt Messaging als weitere wichtige Alternative zur Integration und Aufrufverarbeitung vor. Neben einem umfassenden Anwendungsbeispiel werden verschiedene Protokolle und Produkte gezeigt.
Teil IV: Übergreifende Themen
Der abschließende vierte Teil des Buches behandelt Querschnittsthemen wie Dokumentation, Skalierbarkeit und API-Management:
Kapitel 12
bietet Empfehlungen zur Dokumentation von APIs. Das Kapitel wird mit der Vorstellung hilfreicher Dokumentationswerkzeuge vervollständigt.
Kapitel 13
behandelt das Thema Caching, das insbesondere für die Performance von RESTful HTTP sehr wichtig sein kann.
Kapitel 14
stellt die Grundlagen skalierbarer Systeme vor. In diesem Zusammenhang werden das CAP-Theorem, statuslose Kommunikation, Load Balancing und verschiedene Architekturvarianten diskutiert.
Kapitel 15
diskutiert Consumer-Driven Contracts, »One size fits all«-APIs und andere Architekturthemen.
Kapitel 16
stellt zu guter Letzt das Thema API-Management vor. Architektur und Werkzeuge für das Veröffentlichen, Dokumentieren und Managen von APIs werden beschrieben.
Pfade durch das Buch
Falls Sie dieses Werk nicht von Anfang bis Ende lesen wollen, können Sie seine Einteilung nutzen und verschiedene Pfade hindurch wählen. Wenn Sie beispielsweise hauptsächlich am Design von Web-APIs auf Basis von RESTful HTTP interessiert sind, können Sie nach den Kapiteln 1 bis 3 direkt mit den Kapiteln 8, 9 und 11 bis 15 fortfahren. Falls Sie hingegen an Java-APIs interessiert sind, können Sie nach den Kapiteln 1 bis 7 zu Kapitel 12 springen.
Danksagung
Bei der Arbeit an diesem Buch durfte ich von vielen kritischen Diskussionen und wertvollen Anmerkungen profitieren. Mein besonderer Dank gilt Eberhard Wolff, Stefan Tilkov, Dirk Ludwig, Ulf Fildebrandt, Ivo Walther und Stefanie Elste für ihre Unterstützung.
Ebenso danke ich dem gesamten Team des dpunkt.verlags, insbesondere meinen Lektoren René Schönfeldt und Sandra Bollenbacher, für die stets professionelle und angenehme Zusammenarbeit.
Den größten Dank möchte ich jedoch meiner Frau Ileana aussprechen, die mich von Beginn an bei diesem Buchprojekt begleitet und unterstützt hat.
Für die zweite Auflage des Buches bin ich besonders dankbar für die zahlreichen Anregungen und Verbesserungsvorschläge von Anton Schönfeld, Prof. Dr. Dominik Gruntz und Matthias Müller.
Danke!
Teil IGrundlagen
1Application Programming Interfaces – eine Einführung
2Qualitätsmerkmale
3Allgemeines Vorgehen beim API-Design
Teil IIJava-APIs
4Ausprägungen
5Grundlagen für Java-APIs
6Fortgeschrittene Techniken für Java-APIs
7Kompatibilität von Java-APIs
Teil IIIRemote-APIs
8Grundlagen RESTful HTTP
9Techniken für Web-APIs
10SOAP-Webservices
11Messaging
Teil IVÜbergreifende Themen
12Dokumentation
13Caching
14Skalierbarkeit
15Erweiterte Architekturthemen
16API-Management
Anhang
ALiteraturverzeichnis
Index
Teil IGrundlagen
1Application Programming Interfaces – eine Einführung
1.1Eine kurze Geschichte der APIs
1.2Web-APIs ab dem Jahr 2000
1.3API-Definition
1.4Vorteile einer API
1.5Nachteile einer API
1.6API als Produkt
1.7Welche Strategien verfolgen Unternehmen mit APIs?
1.8API-first-Ansatz
1.9Zusammenfassung
2Qualitätsmerkmale
2.1Allgemeine Qualitätsmerkmale
2.2Benutzbarkeit
2.2.1Konsistent
2.2.2Intuitiv verständlich
2.2.3Dokumentiert
2.2.4Einprägsam und leicht zu lernen
2.2.5Lesbaren Code fördernd
2.2.6Schwer falsch zu benutzen
2.2.7Minimal
2.2.8Stabil
2.2.9Einfach erweiterbar
2.3Konnaszenz
2.4Zusammenfassung
3Allgemeines Vorgehen beim API-Design
3.1Überblick
3.2Heuristiken und Trade-offs
3.3Anforderungen herausarbeiten
3.4Wenn Use Cases nicht ausreichen
3.5Entwurf mit Szenarien und Codebeispielen
3.6Spezifikation erstellen
3.7Reviews und Feedback
3.8Wiederverwendung
3.9Zusammenfassung
Teil IIJava-APIs
4Ausprägungen
4.1Implizite Objekt-API
4.2Utility-Bibliothek
4.3Service
4.4Framework
4.5Eine Frage der Priorität
4.6Zusammenfassung
5Grundlagen für Java-APIs
5.1Auswahl passender Namen
5.1.1Klassennamen
5.1.2Methodennamen
5.1.3Parameternamen
5.1.4Ubiquitäre Sprache
5.1.5Fazit
5.2Effektiver Einsatz von Typen
5.2.1Semantischen Vertrag minimieren
5.2.2Semantische Verletzung der Datenkapselung vermeiden
5.2.3Werden Namen überschätzt?
5.2.4Fazit
5.3Techniken für Objektkollaboration
5.3.1Tell, Don’t Ask
5.3.2Command/Query Separation
5.3.3Law of Demeter
5.3.4Platzierung von Methoden
5.3.5Fazit
5.4Jigsaw-Module
5.5Minimale Sichtbarkeit
5.5.1Jigsaw-Module
5.5.2Packages
5.5.3Klassen
5.5.4Methoden
5.5.5Felder
5.5.6Fazit
5.6Optionale Hilfsmethoden
5.6.1Komfort
5.6.2Utility-Klassen
5.6.3Fazit
5.7Optionale Rückgabewerte
5.7.1Ad-hoc-Fehlerbehandlung
5.7.2Null-Objekte
5.7.3Verwendung der Klasse java.util.Optional
5.7.4Fazit
5.8Exceptions
5.8.1Ausnahmesituationen
5.8.2Checked Exception versus Unchecked Exception
5.8.3Passende Abstraktionen
5.8.4Dokumentation von Exceptions
5.8.5Vermeidung von Exceptions
5.8.6Fazit
5.9Objekterzeugung
5.9.1Erzeugungsmuster der GoF
5.9.2Statische Factory-Methode
5.9.3Builder mit Fluent Interface
5.9.4Praktische Anwendung der Erzeugungsmuster
5.9.5Fazit
5.10Vererbung
5.10.1Ansätze zum Einsatz von Vererbung
5.10.2Stolperfallen bei Vererbung
5.10.3Bedeutung für API-Design
5.10.4Fazit
5.11Interfaces
5.11.1Typen nachrüsten
5.11.2Unterstützung für nicht triviale Interfaces
5.11.3Markierungsschnittstellen
5.11.4Funktionale Interfaces
5.11.5Fazit
5.12Zusammenfassung
6Fortgeschrittene Techniken für Java-APIs
6.1Fluent Interface
6.1.1DSL-Grammatik
6.1.2Schachteln versus Verketten
6.1.3Fluent Interface von jOOQ
6.1.4Ist der Aufwand gerechtfertigt?
6.1.5Fazit
6.2Template-Methoden
6.2.1API versus SPI
6.2.2Erweiterbare Parameter
6.2.3Fazit
6.3Callbacks
6.3.1Synchrone Callbacks
6.3.2Asynchrone Callbacks
6.3.3Fazit
6.4Annotationen
6.4.1Auswertung zum Kompilierzeitpunkt
6.4.2Auswertung zur Laufzeit
6.4.3Fazit
6.5Wrapper-Interfaces
6.5.1Proxy
6.5.2Adapter
6.5.3Fassade
6.5.4Fazit
6.6Immutability
6.6.1Wiederverwendung
6.6.2Threadsicherheit
6.6.3Einfachheit
6.6.4Umsetzung
6.6.5Automatische Überprüfung mit dem Mutability Detector
6.6.6Codegenerierung mit Immutables
6.6.7Fazit
6.7Threadsichere APIs
6.7.1Vorteile
6.7.2Nachteile
6.7.3Was bedeutet Threadsicherheit?
6.7.4Fazit
6.8Zusammenfassung
7Kompatibilität von Java-APIs
7.1Kompatibilitätsstufen
7.1.1Codekompatibilität
7.1.2Binäre Kompatibilität
7.1.3Funktionale Kompatibilität
7.2Verwandtschaftsbeziehungen
7.3Design by Contract
7.4Codeänderungen
7.4.1Package-Änderungen
7.4.2Interface-Änderungen
7.4.3Klassenänderungen
7.4.4Spezialisierung von Rückgabetypen
7.4.5Generalisierung von Parametertypen
7.4.6Generics
7.4.7Ausnahmen
7.4.8Statische Methoden und Konstanten
7.5Praktische Techniken für API-Änderungen
7.6Test Compatibility Kit
7.7Zusammenfassung
Teil IIIRemote-APIs
8Grundlagen RESTful HTTP
8.1REST versus HTTP
8.2REST-Grundprinzipien
8.3Ressourcen – die zentralen Bausteine
8.4HTTP-Methoden
8.5HATEOAS
8.6Zusammenfassung
9Techniken für Web-APIs
9.1Anwendungsbeispiel: Onlineshop
9.2URI-Design
9.3Medientypen
9.4Fehlerbehandlung
9.5Versionierung
9.5.1Daten- und Sprachversionierung
9.5.2Kompatibilität und Perspektive
9.5.3Versionsidentifikation
9.6API-Sicherheit
9.6.1API-Sicherheitsmechanismen
9.6.2Authentifizierung
9.6.3API-Keys
9.6.4Distributed Denial of Service (DDoS)
9.6.5Injection-Angriff
9.7Partielle Rückgaben
9.8GraphQL
9.9OData
9.10gRPC
9.11Zusammenfassung
10SOAP-Webservices
10.1SOAP-Grundlagen
10.2WSDL-Grundlagen
10.3Entwurfsansätze und -muster
10.4Versionierung
10.5SOAP versus REST
10.6Zusammenfassung
11Messaging
11.1Routenplanung für Lkw-Transporte (Teil 1)
11.2Message Broker
11.3Produkte
11.4Standards und Protokolle
11.5Routenplanung für Lkw-Transporte (Teil 2)
11.6Transaktionen und garantierte Nachrichtenzustellung
11.7Asynchrone Verarbeitung und REST
11.8Push Notifications
11.9Zusammenfassung
Teil IVÜbergreifende Themen
12Dokumentation
12.1Motivation
12.2Zielgruppen unterscheiden
12.3Allgemeiner Aufbau
12.4Beispiele
12.5Dokumentation von Java-APIs
12.6Dokumentation von Web-APIs
12.7Zusammenfassung
13Caching
13.1Anwendungsfälle
13.2Performancevorteil
13.3Verdrängungsstrategien
13.4Cache-Strategien für Schreibzugriffe
13.5Cache-Topologien für Webanwendungen
13.6HTTP-Caching
13.7Zusammenfassung
14Skalierbarkeit
14.1Anwendungsfall
14.2Grundlagen
14.3Load Balancing
14.4Statuslose Kommunikation
14.5Skalierung von Datenbanken
14.6Skalierung von Messaging-Systemen
14.7Architekturvarianten
14.8Zusammenfassung
15Erweiterte Architekturthemen
15.1Consumer-Driven Contracts
15.2Backends for Frontends
15.3Vernachlässigte Frontend-Architektur
15.4Netflix-APIs
15.5Zusammenfassung
16API-Management
16.1Überblick
16.2Funktionen einer API-Management-Plattform
16.3API-Management-Architektur
16.4Open-Source-Gateways
16.5Zusammenfassung
Anhang
ALiteraturverzeichnis
Index
APIs sind keine neue Erfindung; ihre grundlegenden Prinzipien wurden bereits Mitte des 20. Jahrhunderts erkannt und seither in verschiedenen Formen genutzt. Ab der Jahrtausendwende nahmen Web-APIs an Bedeutung zu, und es entstand eine florierende API-Industrie, die Bereiche wie E-Commerce, soziale Medien, Cloud Computing und mobile Anwendungen prägt. In diesem Kapitel befassen wir uns zunächst mit der Entstehung der ersten APIs, definieren den Begriff und erläutern die Vorteile von APIs.
Das Konzept einer Subroutinen-Bibliothek wird erstmalig 1948 von Herman Goldstine und John von Neumann beschrieben [Goldstine & von Neumann 1948]. Demnach ist die Idee, dass die meisten Programme allgemeine Operationen wiederverwenden, um den Umfang von neuem Code und Fehlern zu reduzieren, nach Informatikermaßstäben schon sehr alt. Für die Idee der Subroutinen-Bibliotheken wurde Maurice Vincent Wilkes 1967 sogar mit dem Turing Award ausgezeichnet.
Wilkes und sein Team bauten den EDSAC-Röhrencomputer, der erstmals die Von-Neumann-Architektur implementierte und gespeicherte Programme ausführte. Wilkes damaliger Ph.D.-Student David Wheeler entwickelte für EDSAC ein detailliertes Schema zum Einsatz von Subroutinen. Während Goldstine und von Neumann vorsahen, das gesamte Programm in den Speicher zu laden und die Adressen vor Ausführung mit einer speziellen Routine zu ändern, entwickelte Wheeler eine Reihe von Initiierungsbefehlen, die zuerst ausgeführt wurden, um ein Programm von Lochkarten einzulesen und ohne weitere manuelle Eingriffe auszuführen. Die Initiierungsbefehle von Wheeler waren eine Art Boot Loader für die Programme auf den Lochkarten. Die Programme wurden in Assembler geschrieben, sodass die Benutzer des Computers nie mit dem Binärcode des Computers zu tun hatten.
Der technische Bericht »The preparation of programs for an electronic digital computer«, den das Team 1951 veröffentlichte, war ein Standardwerk der Programmierung, bis Jahre später die ersten höheren Programmiersprachen folgten. Wheeler veröffentlichte 1952 auf nur zwei Seiten folgende grundlegende Konzepte [Wheeler 1952]:
Subroutinen
Subroutinen-Bibliotheken
Bedeutung von Dokumentation für Subroutinen-Bibliotheken
Geheimnisprinzip
Trade-off zwischen Generalität und Performance
Funktionen höherer Ordnung
Debugger
Routinen zur Interpretierung von Pseudocode
In diesem Dokument schreibt Wheeler, dass die Vorbereitungen für eine Subroutinen-Bibliothek größer sind als ihre eigentliche Programmierung. Außerdem betont er die Bedeutung von Dokumentation von Subroutinen-Bibliotheken. Im abschließenden Fazit nennt er einfache Benutzung, Korrektheit und akkurate Dokumentation als Hauptziele bei der Konstruktion von Bibliotheken. Komplexität sollte vor Benutzern verborgen bleiben.
Obwohl Wheeler schon die Prinzipien der späteren APIs erkannte, unterschied er nicht zwischen API und Implementierung, denn es gab zu diesem Zeitpunkt nur eine Maschinenarchitektur und keine alternativen Implementierungen der Bibliotheken. Erst als die Bibliotheken wegen neuerer Hardware oder wegen besserer Algorithmen neu implementiert wurden und man existierende Programme portieren wollte, gab es Gründe, zwischen API und Implementierung zu unterscheiden.
1968 erschien erstmalig der Begriff »Application Programming Interface« [Cotton & Greatorex 1968]. API und Implementierung werden konzeptionell voneinander getrennt, um Implementierungen austauschen zu können, ohne dass Clients davon betroffen sind.
Mehr als ein halbes Jahrhundert nach Wheelers Pionierarbeit sind seine Aussagen immer noch gültig. Lediglich die Begriffe haben sich verändert. So schreibt Joshua Bloch [Bloch 2006], dass es einfach sein sollte, eine API korrekt zu benutzen, und dass es schwer sein sollte, eine API falsch zu benutzen. Egal wie gut eine API ist, ohne gute Dokumentation wird sie nicht benutzt.
Zur Jahrtausendwende begann die Suche nach innovativen Lösungen, um Produkte mehrerer E-Commerce-Webseiten miteinander zu verbinden. Web-APIs auf Basis der existierenden HTTP-Infrastruktur schienen das richtige Werkzeug für diese Aufgabe zu sein:
Erste XML-APIs von Salesforce.com
Im Februar 2000 startete
Salesforce.com
offiziell eine webbasierte Sales Force Automation für Unternehmen. Dieser Internetdienst setzte von Anfang an XML-APIs ein.
Salesforce.com
reagierte damit auf den Kundenbedarf, Informationen zwischen verschiedenen Geschäftsanwendungen austauschen zu wollen.
Pionierarbeit von eBay
Im November 2000, also nur sieben Monate nach
Salesforce.com
, ging die eBay-API zusammen mit dem eBay Developers Program live. Die eBay-API war eine Reaktion des Unternehmens auf die wachsende Anzahl an Applikationen, die bereits die eBay-Webseite benutzten. Die API sollte die Integration mit diesen und zukünftigen Applikationen vereinheitlichen. eBay kann deswegen als führender Pionier der Web-APIs und Webservices angesehen werden.
Soziale Medien
Neben diesen E-Commerce-Plattformen spielten auch soziale Medien eine wichtige Rolle in der Geschichte der Web-APIs. 2003 startete del.icio.us, ein Bookmarking-Dienst zum Speichern, Teilen und Auffinden von Bookmarks für Webseiten. Mit einem leicht verständlichen URL-Schema
1
konnte man eine Liste mit Bookmarks für ein Schlüsselwort abrufen. Diese API war nahtlos in die Webseite integriert. Del.icio.us war eine der ersten Webseiten, die HTML zusammen mit maschinenlesbaren Inhalten wie RSS und XML anbot.
Web 2.0
2004 startete Flickr sein Webportal zum Hochladen, Kommentieren und Teilen von Bildern und kurzen Videos. Die Einführung einer RESTful API half Flickr, schnell populär für Blogger und Benutzer sozialer Medien zu werden. Flickr etablierte für Anwendungsentwickler zur Benutzung der API ein Self-Service. Neben seiner technischen Funktion wurde die API ein wichtiger Faktor für die weitere Geschäftsentwicklung. Die moderne Plattform von Flickr zählte zu den typischen Vertretern des »Web 2.0«.
Facebook REST-API
Die Entwicklungsplattform und API von Facebook ist seit 2006 verfügbar. Seitdem ist es Softwareentwicklern möglich, auf Facebook-Freunde, Fotos und Profilinformationen zuzugreifen. Die RESTAPI war ein Vorteil von Facebook gegenüber Konkurrenten wie MySpace.
Plattform X
Im selben Jahr führte X seine API ein, basierend auf REST mit Unterstützung für JSON und XML. Ähnlich wie eBay reagierte X damit auf die wachsende Zahl von Applikationen. Die X-API wird von unzähligen Desktop-Clients, mobilen Anwendungen und sogar Geschäftsanwendungen genutzt.
Google Maps API
Ebenfalls 2006 startete Google seine Google Maps API für die zahllosen Entwickler, die Google Maps in ihre Anwendungen integrieren wollten. Dies war die Geburtsstunde der Mashups, die neue Inhalte durch die Kombination bereits bestehender Inhalte erzeugen. Hierfür nutzen Mashups offene APIs, die von anderen Webanwendungen zur Verfügung gestellt werden.
API-Serviceprovider
Die Liste bekannter Web-APIs ließe sich leicht fortsetzen. Wichtig sind ebenfalls die API-Serviceprovider wie Mashery. Dies war 2006 der erste Anbieter einer Infrastruktur zur Entwicklung, Veröffentlichung und Verwaltung von APIs, die es externen Entwicklern ermöglicht, Inhalte anderer Unternehmen für ihre Produkte zu nutzen.
Public Cloud PaaS
In diesem Zeitraum begann außerdem die Ära des Public Cloud Computing Platform as a Service durch die Veröffentlichung der Amazon Web Services (AWS). Amazon startete mit dem Cloud-Speicher Amazon S3 und legte mit Amazon EC2, einem Webservice für die Bereitstellung von skalierbarer Rechenkapazität, nach. Beide bieten eine Web-API. PaaS sollte ein wichtiger Motor der API-Industrie werden.
Mobile Apps
Foursquare startete 2009 einen standortgebundenen Dienst für mobile Geräte, mit dem Benutzer interessante Orte einer Stadt finden können. 2011 folgte die offizielle API von Instagram. APIs entwickelten sich vom Antreiber für E-Commerce-Anwendungen, soziale Medien und Cloud Computing zum Lieferanten von Ressourcen und Funktionen für mobile Geräte.
Versionierung
Viele APIs haben eine lange Lebensdauer und werden daher versioniert. Die X-API v1, die 2006 online ging, wurde beispielsweise erst 2013 eingestellt. Zuvor gab es bereits eine neuere Version der API, sodass Entwickler ausreichend Zeit hatten, ihre Anwendungen anzupassen. Einige APIs haben praktisch eine unbegrenzte Lebensdauer. In der Java-Standardbibliothek wurden lange Zeit keine Elemente entfernt, sondern lediglich als »deprecated« gekennzeichnet. Besonders im Zuge der Einführung von Java 9 und des Modul-Systems hat Oracle jedoch veraltete APIs entfernt. Auch in Java 11 wurden beispielsweise Java-Applets und das CORBA-Modell gestrichen, um die Java Development Kit (JDK) API schlanker und effizienter zu gestalten.
Internet der Dinge
Das nächste große Kapitel der API-Geschichte trägt den Titel »Internet der Dinge« (Internet of Things – IoT). Hinter diesem Trend steckt die Idee, unterschiedlichste intelligente Geräte zu vernetzen, um Menschen bei ihren Tätigkeiten zu unterstützen. Das Spektrum dieser intelligenten Geräte reicht von Kühlschränken bis zu Autos. Mithilfe verschiedener APIs kann man auf die IoT-Einheiten zugreifen. Dies geschieht häufig drahtlos per Wi-Fi, BLE (Bluetooth Low Energy) oder NFC (Near Field Communication). Beispiele sind ThingSpeak, eine Open-Source-Lösung, mit der Entwickler unter Zuhilfenahme von Webtechnologien mit Geräten interagieren können, und OGC SensorThings API, ein Standard zum einheitlichen Zugriff auf IoT-Einheiten, Daten und Applikationen über das Web.
Microservices und Cloud-Architekturen
Microservices und Cloud-Architekturen haben sich zwischen 2010 und 2020 als feste Bestandteile der IT-Landschaft etabliert. Das Konzept der Microservices entstand um 2010 und wurde durch Unternehmen wie Netflix und Amazon populär gemacht. Diese setzten auf kleine, unabhängige Dienste, um ihre Anwendungen effizient bereitzustellen. In den darauffolgenden Jahren gewannen Microservices zunehmend an Bedeutung. Viele Unternehmen übernahmen diese Architektur, da sie mehr Agilität, Skalierbarkeit und eine einfachere Wartung ihrer Software ermöglichten. Zeitgleich wurden Cloud-Architekturen von zahlreichen Organisationen adaptiert, um Kosten zu senken und die Bereitstellung von Anwendungen zu beschleunigen. Insgesamt haben Microservices und Cloud-Architekturen den Softwareentwicklungsprozess grundlegend verändert. APIs spielen dabei eine Schlüsselrolle, da sie die Interoperabilität und Modularität in diesen modernen Architekturen maßgeblich unterstützen.
Eine API ist eine Schnittstelle, die es zwei Softwarekomponenten innerhalb einer oder mehrerer Anwendungen oder Systeme ermöglicht, miteinander zu kommunizieren und Daten auszutauschen, ohne dass die interne Funktionsweise der jeweiligen Komponenten offengelegt wird. Eine API kann als eine Art »Vertrag« betrachtet werden, der festlegt, welche Funktionen oder Daten eine Softwarekomponente bereitstellt und wie andere Softwarekomponenten diese anfordern oder nutzen können. Dieser »Vertrag« umfasst sowohl die technische Definition der Schnittstellenfunktionen als auch eine Abstraktion der zugrunde liegenden Problemlösung. Er legt fest, wie Nutzer mit den Komponenten interagieren sollten, die eine Lösung für das betreffende Problem implementieren [Reddy 2011].
Zwei wesentliche Aspekte einer API lassen sich unterscheiden:
Technische Definition: Diese beschreibt die genauen Anforderungen und Spezifikationen für Eingaben, Ausgaben und Methoden, damit eine Anwendung erfolgreich mit einer anderen interagieren kann. Dazu gehören oft festgelegte Datenformate, Authentifizierungsmechanismen und Endpunkte. Eine API erfordert in der Regel eine detaillierte Dokumentation dieser technischen Spezifikationen, um ihre Nutzung zu ermöglichen.
Abstraktionsebene: APIs kapseln im Sinne des Geheimnisprinzips die Komplexität des zugrunde liegenden Systems und stellen nach außen hin nur die benötigten Funktionen bereit. Das bedeutet, dass Entwickler nicht verstehen müssen, wie der Code hinter der API implementiert ist, um sie nutzen zu können. Diese Abstraktion erleichtert die Integration und Nutzung unterschiedlicher Systemkomponenten.
Allgemeine API-Definition
Joshua Bloch definiert APIs folgendermaßen: »Eine API spezifiziert die Operationen sowie die Ein- und Ausgaben einer Softwarekomponente. Ihr Hauptzweck besteht darin, eine Menge an Funktionen unabhängig von ihrer Implementierung zu definieren, sodass die Implementierung variieren kann, ohne die Nutzer der Komponente zu beeinträchtigen« [Bloch 2014].
API-Typen in diesem Buch
Diese allgemeine Definition verdeutlicht, dass »API« nur der Oberbegriff für viele unterschiedliche API-Spielarten ist. Konkret unterscheidet dieses Buch zwischen Programmiersprachen-APIs und Remote-APIs:
Programmiersprachen-APIs werden beispielsweise von Bibliotheken angeboten und sind sprach- und plattformabhängig. Als Vertreter der Programmiersprachen-APIs behandelt das Buch objektorientierte Java-APIs.
Auf der Seite der Remote-APIs bietet das Buch RESTful HTTP, SOAP-Webservices und Messaging-APIs. Diese APIs sind durch Protokolle wie HTTP sprach- und plattformunabhängig. Diese Eigenschaften erfüllen ebenfalls SOAP-Webservices, die mit und ohne HTTP (z. B. über eine Message Queue) genutzt werden können. Messaging-APIs bieten asynchrone Kommunikation auf Protokollen wie AMQP (Advanced Message Queuing Protocol) oder MQTT (Message Queue Telemetry Transport). Darüber hinaus gibt es auch Remote Procedure Calls (RPCs) und dateibasierte APIs für Konfigurationen und asynchronen Informationsaustausch.
Abb. 1–1 Es gibt verschiedene API-Typen. In diesem Buch werden objektorientierte Java-APIs, Web-APIs und Messaging-APIs behandelt.
Service Provider Interface
Auch Frameworks haben eine API, über die sie benutzt und erweitert werden können. Ein Service Provider Interface (SPI) ist eine API, die dazu bestimmt ist, von einem Benutzer erweitert oder implementiert zu werden. Auf diese Weise kann eine Applikation oder ein Framework Erweiterungspunkte bereitstellen. Generell ist für Programmiersprachen-APIs das Thema Vererbung wichtig.
API versus Protokoll
Häufig werden APIs und Protokolle im gleichen Kontext verwendet. Dennoch können und sollten die Begriffe voneinander getrennt werden.
Eine objektorientierte API kann ein Protokoll kapseln. Ein Beispiel dazu ist im Java-Umfeld RMI (Remote Method Invocation). Eine Implementierung dieser Java-API nutzt intern das Protokoll JRMP (Java Remote Method Protocol) für entfernte Aufrufe zwischen Objekten. Ein anderes Beispiel ist JMS (Java Messaging Service). Diese Java-API für Message Queues wird von einem JMS-Provider (z. B. ActiveMQ) mithilfe eines zugrunde liegenden Protokolls (z. B. AMQP) umgesetzt. Eine API-Implementierung kann demzufolge ein Protokoll kapseln bzw. es implementieren. Umgekehrt gilt das nicht: Ein Protokoll kann keine API kapseln oder implementieren.
HTTP ist ein Protokoll, das ist unstrittig, aber HTTP allein stellt noch keine API dar. Eine API kann man jedoch als eine Menge von HTTP-Requests und -Responses inklusive der Struktur der verwendeten Nachrichten definieren. Häufig spricht man in diesem Fall von einer Web-API oder allgemein von einer Remote-API.
Ein anderes Beispiel sind WebSockets, weil hier Protokoll und API konsequent voneinander getrennt sind. Das WebSocket-Protokoll ist in RFC 6455 [Fette & Melnikov 2011] standardisiert und spezifiziert u. a. das Öffnen und Schließen von Verbindungen mit Handshake. Die WebSocket-API [Hickson 2011] definiert u. a. ein WebSocket-Interface mit den Methoden send und close. Mithilfe der API können Webseiten das WebSocket-Protokoll für die Zwei-Wege-Kommunikation mit einem entfernten Host nutzen. Moderne Webbrowser exponieren die API und nutzen das Protokoll zur Kommunikation mit entfernten Servern, die das Protokoll möglicherweise hinter einer serverseitigen API anbinden.
In der zuvor betrachteten kurzen Geschichte der APIs wurden bereits einige Vorteile beschrieben. Ein wichtiger Vorteil, der sich durch die Trennung zwischen API und Implementierung ergibt, ist die Änderbarkeit oder Austauschbarkeit der Implementierung. Solange der Vertrag der API eingehalten wird, müssen Benutzer ihren Code nicht anpassen. Aus diesem Ansatz ergeben sich mehrere Vorteile:
Stabilität durch lose Kopplung
Angenommen, Benutzer einer Softwarekomponente wären direkt von den Implementierungsdetails der Softwarekomponente abhängig, dann wäre der Code des Benutzers instabil, weil dieser schon bei kleinen Änderungen der Softwarekomponente angepasst werden müsste. Diese starke Kopplung zwischen einer Softwarekomponente und ihren Benutzern kann durch eine API minimiert werden. Falls beispielsweise ein Webservice seine Ein-Server-Lösung durch eine Lösung mit verteilter Architektur ersetzen muss, weil die Benutzeranzahl wächst und Performanceprobleme auftreten, sollte die Änderung keine Auswirkungen auf bestehende Benutzer haben, obwohl die neue Lösung auf einer völlig anderen Architektur basiert.
Portabilität
Beispielsweise kann ein in ANSI C geschriebenes Programm auf verschiedenen Computerarchitekturen und Betriebssystemen ausgeführt werden, sofern eine konforme C-Implementierung vorhanden ist. Als Beispiel könnte man auch die Java Runtime Environment (JRE) nennen, denn sie bietet für Java-Programme eine einheitliche API-Implementierung für verschiedene Betriebssysteme.
Komplexitätsreduktion durch Modularisierung
Eine API bietet eine geeignete Abstraktion und versteckt die Komplexität der Implementierung. Diese nicht notwendige Kenntnis des API-Benutzers über Implementierungsdetails folgt dem Geheimnisprinzip und hilft, die Komplexität großer Anwendungen zu beherrschen. Die Modularisierung hat wiederum Vorteile für Arbeitsteilung und Entwicklungskosten.
Softwarewiederverwendung und Integration
Eine API wird nicht nur entworfen, um Implementierungsdetails zu verbergen, sondern um Funktionen einer Softwarekomponente anderen Entwicklern möglichst einfach zur Verfügung zu stellen. Aus diesem Grund sollte eine API für einfache Wiederverwendung und Integration optimiert werden. Mit RESTful HTTP kann beispielsweise eine einheitliche Schnittstelle für unterschiedliche Web-APIs realisiert werden.
Grundvoraussetzung für die genannten Vorteile ist gutes API-Design. Allerdings ist es gar nicht so einfach, gute APIs zu entwerfen. Das Entwerfen schlechter APIs geht vergleichsweise leicht [Henning 2007]. Eine gute API erkennt man sofort, sobald man sie verwendet: Ihre Benutzung macht Spaß, und es gibt kaum Reibungsverluste, weil sie intuitiv benutzbar und gut dokumentiert ist.
Die Konsequenzen schlechten API-Designs sind vielfältig und schwerwiegend: Schlechte APIs sind schwer zu benutzen, und in manchen Fällen muss zusätzlicher Clientcode geschrieben werden, der Programme größer, komplizierter und schwerer wartbar macht. Entwickler brauchen mehr Zeit, um schlechte APIs zu verstehen und zu benutzen. Schlechte APIs führen deswegen zu erhöhten Entwicklungskosten oder zur völligen Ablehnung von Softwarekomponenten, falls Entwickler zwischen mehreren Alternativen wählen können.
Interoperabilität
Eine API hat sicherlich nicht nur Vorteile, sondern auch Nachteile. Erwähnenswert ist die fehlende Interoperabilität von Programmiersprachen-APIs, denn eine Java-API kann beispielsweise nicht von einer Go-Applikation konsumiert werden. Die Lösung liegt allerdings auf der Hand: Die gewünschte Interoperabilität bieten Remote-APIs auf Basis von Protokollen wie HTTP und AMQP, weil diese von unterschiedlichen Plattformen und Programmiersprachen genutzt werden können.
Änderbarkeit
Ein anderer Nachteil, der im Alltag Kopfschmerzen bereiten kann, ist die eingeschränkte Änderbarkeit von APIs, denn die mit den API-Benutzern geschlossenen API-Verträge dürfen nicht gebrochen werden – oder etwa doch?
Um das Problem besser erläutern zu können, sollte man zwischen interner und veröffentlichter API unterscheiden. Letztere hat Benutzer, die Sie nicht kennen oder die Sie nicht kontrollieren. In diesem Fall dürfen Sie keine Änderungen machen, die den bestehenden API-Vertrag brechen. Für die internen APIs ist die Situation anders: Wenn Sie zum Beispiel Ihre Codebasis in Module mit öffentlichen und privaten Teilen strukturieren, erfolgt die Kommunikation der Module untereinander über deren öffentliche Teile, also über deren APIs. In diesem Fall können Sie die APIs ändern und von Refactoring profitieren, weil Sie den von den APIs abhängigen Code kontrollieren. Generell sollten Sie so wenig wie möglich veröffentlichen, um Änderungen machen zu können.
Die bisher genannten Funktionen sind hauptsächlich technischer Art. Es gibt jedoch auch wirtschaftliche Funktionen, die wir hier nicht vergessen dürfen. Prinzipiell kann man in diesem Zusammenhang zwischen zwei Unternehmenstypen unterscheiden:
Das Unternehmen ist die API.
Für Unternehmen wie Yelp, Twilio oder SendGrid ist die API das Hauptprodukt. Das Bereitstellen einer nützlichen und leicht verwendbaren API ist ihre Geschäftsgrundlage.
Die API als zusätzlicher Kanal
Für andere Unternehmen wie FedEx, Walmart und Best Buy steht die API weniger im Mittelpunkt. Viele Offline-Unternehmen nutzen APIs, um ihren Markt zu vergrößern.
Web-APIs können für Unternehmen von strategischer Bedeutung sein. Einige dieser Strategien sind hier aufgezählt:
Mobile Strategie
Unternehmen wie Facebook können nicht für jedes mobile Gerät einen dedizierten Client bauen. Stattdessen setzen sie eine neutrale API ein, die ausfallsicher und skalierbar ist.
Benutzung der Plattform antreiben
X konnte die Benutzung seiner Plattform durch eine besonders gute API steigern. Denn diese API war die Grundvoraussetzung für die vielen mobilen Apps, mit denen X auf unzähligen Geräten überall und jederzeit genutzt werden kann.
Investition in neue Geschäftszweige
Eine API-Strategie kann der Anfang eines neuen Geschäftszweigs sein und Wachstum zur Folge haben. Auch Best Buy startete eine API zur Steigerung seines Onlinehandels. Schnell entstanden Apps zum Preisvergleich, zum Stöbern im Warenkatalog und zur Verbreitung von Angeboten.
Integration mit Partnern
APIs sind wichtig zur Vernetzung mit Partnern und Zulieferern. Beispielsweise verfolgt FedEx eine API-Strategie, durch die unzählige FedEx-kompatible Applikationen entstanden sind.
Integration innerhalb eines Unternehmens
APIs dienen nicht nur zur Integration von Fremdsystemen, sondern auch innerhalb eines Unternehmens sind sie zur Integration von Systemen wichtig.
API-last
Vielleicht hatten Sie in der Praxis schon einmal mit einer Legacy-Anwendung zu tun, bei der APIs nachträglich hinzugefügt wurden. In diesem Fall könnte man von einem »API-last«-Ansatz sprechen. Nachträglich hinzugefügte APIs basieren oft auf vorhandenen Backend-Logiken und Datenmodellen, was ihre Flexibilität und Wiederverwendbarkeit einschränkt, da sie eng mit der bestehenden Architektur verknüpft sind. Eine nachträgliche API-Integration kann eingeschränkt sein, weil das zugrunde liegende System nicht darauf ausgelegt ist, über APIs zu kommunizieren. Das Backend kann möglicherweise nicht alle erforderlichen Daten effizient bereitstellen, was die Funktionalität und das Design der API einschränken könnte.
API-first
Um diese Nachteile zu vermeiden, hat sich der API-first-Ansatz etabliert. Die Grundidee ist wie folgt: Im Softwareentwicklungsprozess werden APIs als zentrale Bausteine der Software betrachtet und entsprechend priorisiert. APIs sind nicht nur Middleware, Klebstoff oder schlimmer noch ein nachträglicher Einfall, sondern bewusste Strategie.
Nimmt man den Begriff »API-first« wörtlich, so entwickeln Teams, die diesem Ansatz folgen, erst ihre APIs, bevor sie sich anderen Aspekten wie der Persistenz oder der Geschäftslogik im Backend widmen. Auf diese Weise entstehen Anwendungen, die auf internen und externen Diensten basieren, die über APIs bereitgestellt werden [Postman API-first].
Die Reihenfolge ist jedoch nicht zwingend entscheidend, sondern vielmehr eine Folge der Priorisierung von Entwicklung und Einsatz der APIs. API-first-Organisationen verstehen genau, dass Anwendungen als Verknüpfung interner und externer Dienste über APIs aufgebaut werden. Sie erkennen, dass APIs festlegen, wie digitale Ressourcen und Funktionen des Unternehmens internen und externen Nutzern zugänglich gemacht werden, um Geschäftsanwendungen bedarfsgerecht zu unterstützen.
API-first-Entwicklungsmodell einführen
Damit eine Organisation ein API-first-Entwicklungsmodell einführen kann, muss sie APIs priorisieren, die Rolle öffentlicher, privater und Partner-APIs in Organisationen erkennen und den API-Lebenszyklus und die Tools verstehen, die erforderlich sind, um API-first zu werden [Postman API-first].
Priorisierung von APIs
API-first-Organisationen stellen die APIs, die ihre Anwendungen unterstützen, in den Mittelpunkt und fokussieren sich auf den Mehrwert, den diese APIs für das Unternehmen schaffen können, anstatt lediglich eine Anwendung zu entwickeln und die API nachträglich hinzuzufügen. Dieser vorausschauende Ansatz ermöglicht es, die Anwendung über die API in verschiedenen Bereichen des Unternehmens für vielfältige Zwecke einzusetzen. Außerdem erkennen API-first-Organisationen, dass APIs gewartet und verbessert werden müssen, und bilden Teams, die dies unterstützen.
Tatsächlich sind die meisten APIs, die Entwickler verwenden, intern. Laut einer repräsentativen Umfrage von Postman sind 58 % der von den Befragten verwendeten APIs ausschließlich für interne Zwecke, 27 % für Partner und 15 % der APIs öffentlich im Web erreichbar
[Postman API-first]
.
Ein klar definierter API-Lebenszyklus ist entscheidend, um den Betrieb auf einer API-Plattform optimal zu gestalten und Hunderte oder sogar Tausende von APIs über verschiedene Teams hinweg effizient zu verwalten. Ein gemeinsames Verständnis des API-Lebenszyklus und ein einheitliches Vokabular für dessen Beschreibung unterstützen Ihre Teams dabei, abgestimmt zu arbeiten und APIs mit höherer Produktivität, Qualität und Governance zu entwickeln – genau die Voraussetzungen, die Ihr Unternehmen zukunftsfähig machen.
API-Plattformen sind Softwaresysteme, die integrierte Tools und Prozesse bieten, um Entwicklern und Nutzern das Erstellen, Verwalten, Veröffentlichen und Verwenden von APIs zu ermöglichen. Mehr Informationen finden Sie in
Kapitel 16
.
In diesem Kapitel haben Sie einen Überblick über die Geschichte der APIs bekommen. Hier sind die wichtigsten Etappen kurz zusammengefasst:
Die ersten Subroutinen-Bibliotheken gab es 1949/1950 für den Supercomputer EDSAC.
Der Begriff »API« wurde 1968 erstmals erwähnt.
Ab 2000 entstehen erste Web-APIs für E-Commerce. Daraufhin entwickelt sich eine ganze API-Industrie für soziale Medien, Cloud Computing, mobile Applikationen und schließlich das Internet der Dinge.
In diesem Kapitel wurden ebenfalls wichtige Vorteile von APIs vorgestellt:
Man kann konzeptionell zwischen einer API und ihrer Implementierung unterscheiden. Eine API bildet den Vertrag und die Beschreibung einer Softwarekomponente.
Eine gute API ist für einfache Wiederverwendung und Integration optimiert.
API-first-Organisationen rücken die APIs, die ihre Anwendungen unterstützen, in den Mittelpunkt. Der Fokus liegt dabei auf dem Mehrwert, den diese APIs für das Unternehmen schaffen, anstatt lediglich eine Anwendung zu entwickeln und die API nachträglich hinzuzufügen.
Das nächste Kapitel geht der Frage nach, was eine gute API ausmacht. Dazu werden Sie verschiedene Qualitätsmerkmale kennenlernen.
Nachdem Sie im vorherigen Kapitel die Prinzipien und den Zweck von APIs kennengelernt haben, geht es in diesem Kapitel weiter mit den allgemeinen Qualitätsmerkmalen. Diese Merkmale sind das Ziel der Best Practices und Design-Heuristiken in diesem Buch.
Um die Qualität eines Produktes oder einer Applikation bewerten zu können, gibt es viele Qualitätsmodelle, von denen sich insbesondere DIN/ISO 9126 in der Praxis durchgesetzt hat. Die darin definierten Qualitätsziele gelten für Software im Allgemeinen und damit auch für APIs. Ein Ziel ist beispielsweise die Richtigkeit der geforderten Funktionalität. Zweifellos ist das ein wichtiges Ziel, doch welche Ziele kann man für APIs besonders hervorheben?
Benutzbarkeit
APIs sollen für andere Entwickler leicht verständlich, erlernbar und benutzbar sein. Gute Benutzbarkeit ist ein zentrales Ziel beim API-Design, 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
.
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 folgenden Abschnitten vorgestellt.
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 nun das Problem? Das Problem liegt darin, 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();
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-Methoden
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 withName, withId oder withProperty ändern das aufgerufene Objekt nicht, sondern erzeugen ein neues Objekt mit den angegebenen Werten. Das Präfix »with« wird beispielsweise von der Bibliothek Joda-Time genutzt.
Methoden der Java-Collections
Ein anschauliches Beispiel für die Bedeutung konsistenter Terminologie in APIs sind die Collections der Java-Standardbibliothek. Hier wurden starke Begriffe wie »add«, »contains« und »remove« etabliert, die einheitlich in den Interfaces List und Set vorkommen. Im Gegensatz dazu weicht das Interface von Map jedoch ab. Warum ist das so? Wäre ein einheitliches »add« in allen drei Interfaces nicht sinnvoller 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
putAll
contains
contains
containsKey, containsValue
containsAll
containsAll
–
remove
remove
remove
removeAll
removeAll
–
Die unterschiedliche Benennung der Methoden zum Hinzufügen von Elementen in den Schnittstellen List, Set und Map – add bei List und Set, put bei Map – spiegelt die unterschiedlichen Konzepte und Verwendungszwecke dieser Datenstrukturen wider.
Mit add wird einem List- oder Set-Objekt einfach ein Element hinzugefügt. Diese Operation fügt ein neues Element in die Collection ein, ohne dabei eine explizite Zuordnung vorzunehmen.
Die Methode put in einer Map ist komplexer, da sie zwei Objekte – einen Schlüssel und einen Wert – miteinander verknüpft und diese als Paar speichert. Der Ausdruck »put« (dt. »setzen«, »stellen« oder »legen«) signalisiert das Einfügen oder Ersetzen eines Werts für einen bestimmten Schlüssel.
Diese Unterscheidung in den Methodennamen sorgt für mehr Klarheit und vermeidet Missverständnisse in Bezug auf die Funktionsweise der jeweiligen Datenstruktur.
Fehlen von removeAll in Map
Es mag auf den ersten Blick seltsam erscheinen, dass Map keine Methode removeAll hat, die das Gegenstück zu putAll wäre. Allerdings ist das Verhalten von removeAll in einer Map potenziell mehrdeutig. Soll es alle Einträge entfernen, deren Schlüssel in der übergebenen Collection enthalten sind? Oder sollen die Einträge entfernt werden, deren Werte in der Collection vorkommen?
Diese Mehrdeutigkeit könnte zu Verwirrung führen, und daher verzichtet Java auf eine generelle removeAll-Methode für Map. Stattdessen bietet die API spezifische Methoden:
// Entfernen von Einträgen anhand übereinstimmender Schlüssel
map.keySet().removeAll(someCollection);
// Entfernen von Einträgen anhand übereinstimmender Werte
map.values().removeAll(someCollection);
// Selektives Entfernen mit beliebiger Bedingung
map.entrySet().removeIf(entry ->
conditionBasedOnKeyOrValue(entry));
Konsistenz in der Benennung
In Map finden wir ein weiteres Beispiel für die Bedeutung konsistenter Begriffe: Während Map über entrySet und keySet verfügt, gibt es kein valueSet. Stattdessen heißt diese Methode korrekt values, da eine Menge (Set) der Definition nach keine doppelten Elemente enthält, während Werte in einer Map durchaus doppelt vorkommen können.
Synonyme vermeiden
Es ist entscheidend, starke Begriffe in einer API zu etablieren und diese konsequent wiederzuverwenden. Innerhalb einer API sollten Synonyme wie »delete« und »remove« nicht vermischt werden. Entscheiden Sie sich für einen Begriff und halten Sie ihn durchgängig ein. Verwenden Sie Begriffe, die für die Benutzer der API vertraut sind. Beispielsweise wäre »erase« zu ungewohnt; ein Java-Entwickler würde in Dateioperationen eher nach »create« und »delete« suchen, während bei Datenbankoperationen »insert« und »remove« gängiger wären. Für Builder sind Methoden wie »withX« oder »addY« üblich, um Objekte schrittweise zu erstellen. Je nach Kontext können auch Methoden mit dem Präfix »plus« verwendet werden, wie etwa date.plusDays(3), um eine neue Instanz mit aktualisierten Werten zu erzeugen.
Intuitive APIs
Eine API ist intuitiv, wenn Entwickler ihren Clientcode auch ohne Dokumentation verstehen können. Dies gelingt nur durch sprechende Bezeichner und das Wiederverwenden bekannter Begriffe aus anderen gängigen APIs. Es ist daher ratsam, etablierte Terminologie aus bekannten Bibliotheken und Frameworks zu übernehmen, um das Verständnis zu erleichtern.
Eine API sollte möglichst einfach zu benutzen sein. Gute Dokumentation ist für dieses Ziel unverzichtbar. Neben Erklärungen für einzelne Klassen, Methoden und Parameter sollten auch Beispiele in der Dokumentation vorhanden sein. Entwickler können durch Beispiele schnell eine API lernen und benutzen. Im Idealfall findet ein Entwickler ein passendes Beispiel, das mit wenigen Änderungen direkt wiederverwendet werden kann. Die Beispiele der Dokumentation zeigen, wie die API korrekt verwendet werden soll.
Gute Dokumentation kann zum Erfolg einer Technologie beitragen. Das Spring Framework hat beispielsweise eine sehr gute Dokumentation mit vielen sinnvollen Beispielen und Erklärungen. Dies war sicherlich ein Grund für die hohe Akzeptanz des Frameworks.
Wie leicht oder schwer es ist, eine API zu lernen, hängt von vielen unterschiedlichen Faktoren ab. Eine konsistente, intuitiv verständliche und dokumentierte API ist sicherlich einfacher zu lernen als eine inkonsistente, unverständliche und undokumentierte. Die Anzahl der von einer API verwendeten Konzepte, die Wahl der Bezeichner und das individuelle Vorwissen der Benutzer haben ebenfalls großen Einfluss auf die Lernkurve.
APIs sind nur mit Mühe zu erlernen, wenn die Einstiegshürden sehr hoch gelegt werden. Dies ist dann der Fall, wenn viel Code für erste kleine Ergebnisse geschrieben werden muss. Nichts kann einen Benutzer mit Anfängerkenntnissen mehr einschüchtern. Das Webframework Vaadin bietet deswegen auf seiner Website ein Beispiel2 mit geringer Einstiegshürde und »sichtbaren« Ergebnissen:
Das Beispiel zeigt die Verwendung von Widgets – eine Besonderheit für Webframeworks. Dieses Beispiel hat den Vorteil, dass mit nur etwa 10 Zeilen Code ein erstes sichtbares Ergebnis entsteht. Das Beispiel kann man für weitere Experimente nutzen, um das Framework auszuprobieren.
APIs haben enormen Einfluss auf die Lesbarkeit des Clientcodes. Schauen wir uns dazu folgendes Beispiel an:
assertTrue(car.getExtras().contains(airconditioning));
assertEquals(2, car.getExtras().size());
Das Beispiel ist ein Auszug aus einem Unit-Test. Die beiden Assertions prüfen, ob das Fahrzeug car eine Klimaanlage und insgesamt zwei Extras hat. Alternativ könnte der Unit-Test auch mit dem FEST-Assert-Framework geschrieben werden:
assertThat(car.getExtras())
.hasSize(2)
.contains(airconditioning);
Dank des Fluent Interface, dessen Methodenketten zur Validierung des Testergebnisses stärker an eine natürliche Sprache angelehnt sind, ist der Code des zweiten Beispiels etwas verständlicher. Ein Fluent Interface ist eine Domain Specific Language (DSL), die durch die Anpassung an die Anforderungen ihrer Domäne viel ausdrucksstärker als eine universelle Programmiersprache ist. In Abschnitt 6.1 finden Sie weitere Informationen zu diesem Thema.
Bessere Lesbarkeit und Wartbarkeit von Unit-Tests waren die Entwurfsziele des FEST-Assert-Frameworks. In diesem Zusammenhang könnte man noch viele andere Bibliotheken mit gleichem Zweck nennen: Das Spock Framework beispielsweise bietet eine kleine DSL zur übersichtlichen Strukturierung von Tests.
Ein Beispiel aus einem ganz anderen Aufgabengebiet ist die JPA Criteria API. Diese API dient zur Konstruktion von typsicheren Datenbankabfragen. Mit dem folgenden Java-Code wird eine Query gebaut und ausgeführt, um alle Order-Objekte mit mehr als einer Position zu selektieren:
Übersichtlicher wird die Abfrage mit QueryDSL. Diese Bibliothek bietet ein Fluent Interface, mit dem verständliche Pfadausdrücke formuliert werden können.
Entwickler verbringen mehr Zeit mit dem Lesen als mit dem Schreiben von Quellcode. Daher kann deren Produktivität durch gut lesbaren Quellcode bzw. eine leicht verständliche API verbessert werden. Wie können APIs zu lesbarem Code führen?
Gute Namenskonventionen sind wichtig, denn sie unterstützen das Lesen und Erfassen des Quellcodes. Gut lesbarer Code enthält auch weniger Fehler, denn Fehler fallen dann schneller auf.
Die zuvor beschriebenen Eigenschaften Konsistenz und intuitive Verständlichkeit haben ebenfalls einen großen Einfluss auf die Lesbarkeit des Clientcodes.
Auch ein einheitliches Abstraktionsniveau verbessert die Lesbarkeit von Code. Das bedeutet, dass eine API beispielsweise nicht Persistenzfunktionen mit Geschäftslogik mischen sollte. Das sind Aufgaben unterschiedlicher Abstraktionsniveaus. Wenn diese vermischt werden, entsteht unnötig komplexer Clientcode. Die gewählten Abstraktionen der API sollten passend für die zukünftigen Benutzer ausgewählt werden.
APIs sollten Hilfsmethoden bieten, sodass der Clientcode kurz und verständlich bleibt. Ein Client sollte nichts tun müssen, was ihm die API abnehmen kann.
Eine API sollte nicht nur einfach zu benutzen, sie sollte sogar schwer falsch zu benutzen sein. Daher sollte man nicht offensichtliche Seiteneffekte vermeiden und Fehler zeitnah mit hilfreichen Fehlermeldungen anzeigen. Benutzer sollten nicht gezwungen sein, die Methoden einer API in einer fest definierten Reihenfolge aufzurufen.
Unerwartetes Verhalten
Die ursprüngliche Datums- und Zeit-API von Java sieht auf den ersten Blick einfach und intuitiv aus. Doch schon bei einfachen Beispielen stolpert man über ein Verhalten, das man vermutlich nicht erwartet. Was das bedeutet, wollen wir uns an einem Beispiel anschauen:
Die ursprüngliche Datums- und Zeit-API von Java lädt geradezu dazu ein, Fehler zu machen. Den 20. Januar 1983 würde man vermutlich so definieren wollen:
Leider enthält diese Codezeile gleich zwei Fehler. Denn die Zeitrechnung dieser API beginnt unerwarteterweise im Jahre 1900. Außerdem sind die Monate beginnend mit 0 durchnummeriert. Die Tage werden beginnend mit 1 angegeben. Deswegen muss der 20. Januar 1983 folgendermaßen erzeugt werden:
Im nächsten Schritt geben wir zusätzlich noch die Uhrzeit 10:17 mit der Zeitzone von Bukarest an. Die Uhrzeit soll schließlich in einen formatierten String umgewandelt werden. Weil die Klasse Date keine Zeitzonen unterstützt, müssen wir ein Calendar-Objekt erzeugen. Die erwartete Ausgabe ist »20.01.1983 10:17 +0200«.
Auch hier verstecken sich mehrere Fehler: Der Konstruktor der Klasse GregorianCalendar akzeptiert eine Zeitzone, aber kein Date-Objekt. Der Calendar kann nicht von SimpleDateFormat formatiert werden. Auch SimpleDateFormat muss die Zeitzone übergeben werden. Durch Angabe der Zeitzone wird die Uhrzeit verändert. Der korrigierte Clientcode sieht so aus:
Aufgrund dieser Fallstricke entstand in der Java-Community die Bibliothek Joda-Time. Der Clientcode könnte folgendermaßen aussehen:
Ein anderes nicht unbedingt intuitives Feature ist die Möglichkeit, mehr als 60 Sekunden, mehr als 24 Stunden usw. bei der Erzeugung eines Date-Objektes anzugeben. Statt einer Fehlermeldung wird der Überhang korrekt berechnet. Durch eine Angabe von beispielsweise 25 Stunden wird der nächste Tag 1 Uhr ausgewählt. Dieses Verhalten ist nicht offensichtlich und könnte deswegen zu Fehlern führen.
Im Zweifel weglassen!
Eine API sollte prinzipiell so klein wie möglich sein, weil einmal hinzugefügte Elemente nachträglich nicht mehr entfernt werden können. Außerdem sind größere APIs auch komplexer. Dies hat Auswirkungen auf Verständlichkeit und Wartbarkeit der API. Ein ganz anderer Punkt ist der Implementierungsaufwand: Je größer die API, desto aufwendiger ihre Implementierung. Deswegen sollten beispielsweise zusätzliche Hilfsmethoden nur mit Bedacht hinzugefügt werden. Andererseits können Hilfsmethoden sehr nützlich sein. Überhaupt sollte ein Client nichts tun müssen, was eine API übernehmen kann.
Daher braucht man einen Kompromiss, wie ihn die Entwickler der Java-Collection-API gefunden haben: Mit den Methoden addAll und removeAll im Interface java.util.List können mit einem Aufruf mehrere Objekte zu einer Liste hinzugefügt bzw. entfernt werden. Diese Methoden sind optional, weil man Objekte auch einzeln mit add und remove hinzufügen bzw. entfernen kann. Trotzdem ist das Vorhandensein dieser Hilfsmethoden im Interface java.util.List nachvollziehbar und akzeptabel. Diese Hilfsmethoden werden sehr häufig verwendet und passen gut zum Rest des Interface. Andere Hilfsmethoden wie beispielsweise removeAllEven oder removeAllOdd, die alle Objekte mit gerader bzw. ungerader Positionsnummer aus einer Liste entfernen, wären für nur wenige spezielle Anwendungsfälle hilfreich und gehören deswegen nicht in die API.
Die Ruby-Standardbibliothek hat diverse Methoden mehrfach, weil man so im Clientcode besser ausdrücken kann, was man tut. Die Anzahl der Elemente eines Arrays kann z. B. mit length, count und size abgefragt werden. Das muss man nicht ermöglichen, aber es ist ein guter Stil, wenn man ihn konsistent anwendet.
Weniger ist manchmal mehr
Schweizer Messer sind bekannt für ihre zahlreichen Werkzeuge. Neben einer Klinge bieten sie z. B. eine Holzsäge, einen Korkenzieher, eine Schere, eine Metallfeile oder eine Pinzette. Manche dieser Werkzeuge werden kaum oder vielleicht nie benutzt. Ein gewöhnlicher Schraubenzieher mit einem vergleichsweise einfachen Design ist ebenfalls vielseitig einsetzbar: Man kann beispielsweise eine Farbdose mit ihm öffnen, falls der Deckel klemmt. Man kann mit ihm die Farbe umrühren, ein Loch in etwas machen, etwas hinter dem Schrank hervorholen, das man mit der Hand nicht erreichen kann, und man kann sogar Schrauben festdrehen.
Auch eine kleine, einfache API kann vielseitig einsetzbar sein. Es muss nicht für jeden Sonderfall eine spezielle Funktion, die am Ende kaum jemand nutzen wird, eingebaut werden. Dennoch sind Schweizer Messer sehr nützlich.
Stabilität ist eine entscheidende Eigenschaft von APIs. Stellen Sie sich vor, Sie entwickeln einen Tarifrechner, der sich als großer Erfolg erweist und in zahlreiche Kundensysteme integriert werden soll. Diese Integrationen werden von verschiedenen Teams durchgeführt und sind oft kostspielig, da Altsysteme nur mit erheblichem Aufwand angepasst werden können. Nun gibt es eine neue Anforderung aus der Fachabteilung: Die komplexen Berechnungsregeln des Tarifrechners müssen erweitert werden.
Sollte sich die API oder das Verhalten der Schnittstelle dabei ändern, könnten Integrationsprobleme mit den Altsystemen entstehen. Daher muss jede Änderung sorgfältig daraufhin geprüft werden, ob sie negative Auswirkungen auf bestehende Nutzer hat. Außerdem sollte überlegt werden, wie diese Änderungen angemessen kommuniziert werden können.
In Kapitel 7 werden wir genauer untersuchen, welche Änderungen rückwärtskompatibel sind. Wenn Änderungen nicht kompatibel sind, muss unter Umständen eine neue Version der API bereitgestellt werden.
Auch bei der Einführung einer neuen API-Version bleibt Stabilität von großer Bedeutung. Ziel sollte es sein, die Migration für bestehende Clients so einfach wie möglich zu gestalten.
Eine weitere Eigenschaft von APIs ist Änderbarkeit, ein zentrales Qualitätsmerkmal für Softwareprodukte. Man versteht darunter den erforderlichen Aufwand zur Durchführung vorgegebener Änderungen für Korrekturen, Verbesserungen oder Anpassungen an neue Anforderungen. Diese Eigenschaft ist kein Widerspruch zur zuvor genannten Stabilität, denn gemeint ist Folgendes:
Bei der Erweiterung einer API sollte der Änderungsaufwand für existierende Clients berücksichtigt werden. Im nächsten Abschnitt wird aus diesem Grund die Metrik Konnaszenz vorgestellt.
Im Idealfall ist die veränderte API kompatibel und der Clientcode muss überhaupt nicht angepasst werden.