Название: API-Design
Автор: Kai Spichale
Издательство: Bookwire
Жанр: Математика
isbn: 9783960886037
isbn:
Trennung von Zuständigkeiten (separation of concerns)
Die Trennung von Zuständigkeiten ist ein Softwaredesignprinzip zur Aufteilung der Software in unterscheidbare Sektionen, die jeweils eine Zuständigkeit haben. Diese Aufteilung unterstützt einen modularen Entwurf im Zusammenspiel mit Datenkapselung und dem Geheimnisprinzip. Vorteile dieser Aufteilung sind vereinfachte Entwicklung und Wartung, weil indivduelle Sektionen unabhängig voneinander wiederverwendet oder weiterentwickelt werden können.
APIs nicht nur für Komponenten- und Teamgrenzen
APIs werden häufig zur Integration von Komponenten eingesetzt. Nach dem Gesetz von Conway stimmen diese technischen Schnittstellen häufig mit Team- oder Organisationsgrenzen überein. APIs – insbesondere die auf der hier betrachteten Objektebene – sind jedoch auch innerhalb einer Komponente oder eines Teams zu finden, denn prinzipiell kann API-Design in der gesamten Codebasis eingesetzt werden, um den Code zu verbessern.
public class NoEncapsulationOrInformationHiding {
public static final int STATUS_ENABLED = 0;
public static final int STATUS_DISABLED = 1;
public int currentStatus = STATUS_ENABLED;
}
Dieses Beispiel zeigt, dass der Einsatz einer objektorientieren Sprache nicht automatisch Datenkapselung und das Geheimnisprinzip sicherstellen.
public class EncapsulationWithoutInformationHiding {
public static final int STATUS_ENABLED = 0;
public static final int STATUS_DISABLED = 1;
private int currentStatus = STATUS_ENABLED;
public int getStatus() {
return currentStatus;
}
}
Diese zweite Variante ist insofern besser, weil der interne Zustand des Objektes gekapselt wird. Kein Benutzer dieser Klasse ist in der Lage, unkontrolliert den Wert des Attributes currentStatus zu ändern. Das Geheimnisprinzip wird dennoch nicht befolgt, weil implementierungsspezifische Details über eine Zugriffsmethode (accessor method) exponiert werden.
public class EncapsulationAndInformationHiding {
private static final int STATUS_ENABLED = 0;
private static final int STATUS_DISABLED = 1;
private int currentStatus = STATUS_ENABLED;
private int getStatus() {
return currentStatus;
}
public boolean isEnabled() {
return getStatus() == STATUS_ENABLED;
}
}
Diese Variante nutzt Datenkapselung zur korrekten Umsetzung des Geheimnisprinzips. Benutzer dieser Klasse können den Status abfragen, ohne dass sie erfahren, wie dieser realisiert ist. Auch das Design dieser API hat sich verbessert, sodass die Klasse intuitiver benutzt werden kann.
API-Design fördert Clean Code
Wie das vorherige Beispiel zeigt, kann eine gute API das Schreiben von lesbarem und wartbarem Clientcode unterstützen. Clean Code [Martin 2008] – eine Sammlung verschiedener Maßnahmen zur Verbesserung der Verständlichkeit von Quellcode – verfolgt ähnliche Ziele. Das gleichnamige Buch von Robert C. Martin deckt viele Themen ab, die ebenfalls für gutes API-Design relevant sind:
Auswahl aussagekräftiger Namen
Entwurf von Klassen und Methoden
Fehlerbehandlung
Boundaries
Im Gegensatz zu Clean Code schaut API-Design jedoch nicht in das Innere von Klassen und Methoden. Deswegen enthält dieses Buch auch keine Empfehlung zur Formatierung von Quellcode, zur Benutzung von Instanzvariablen oder Schachtelungstiefe von If-Anweisungen.
4.2Utility-Bibliothek
Auch Utility-Bibliotheken haben eine API. Diese Bibliotheken sind häufig klein und haben keinen globalen Zustand. Globaler Zustand entsteht beispielsweise durch eine Datenbank, eine Konfiguration oder Initialisierung. Charakteristisch für diese APIs sind statische Methoden, wie man sie in Apache Commons Lang findet. Ein passendes Beispiel sind die statischen Methoden der Klasse StringUtils:
chomp
contains
appendIfMissing
equals
Auch Joda-Time, Guava und FEST sind Utility-Bibliotheken. Selbst das JDK kann als Beispiel genannt werden. Typischerweise bieten Utility-Bibliotheken konkrete Klassen zur Benutzung an.
4.3Service
Charakteristisch für APIs dieser Kategorie sind Fassaden und Datentransferobjekte zur Kapselung einer zugrunde liegenden Komponente, sodass man diese Kategorie auch »Component Boundary« nennen könnte. Die API eines Service kapselt das Klassenmodell der Komponente vollständig oder partiell. Bei einer partiellen Kapselung werden Elemente der internen Implementierung der Komponente in der API verwendet, sodass Benutzer ebenfalls davon abhängig werden.
Den überladenen Begriff »Service« finden man im Domain-Driven Design (DDD) nach Eric Evans [Evans 2004] als Bezeichnung für eine Operation, die nicht natürlich einem anderen Domänenobjekt zugeordnet werden kann und deswegen Teil eines Serviceobjektes ist. Diese Bedeutung von »Service« passt nicht zu dieser API-Kategorie, wenngleich die Platzierung von Methoden eine wichtige Aufgabe beim API-Design darstellt.
Domain-Driven Design, Application Services und Onion Architecture
Ein Application Service – ebenfalls ein Baustein im Domain-Driven Design nach Vaughn Vernon [Vernon 2013] – ist ein treffenderes Beispiel. Diese Services bieten Operationen für externe Benutzer und kapseln das zugrunde liegende Domänenmodell. Die Application Services sind ebenfalls ein Bestandteil der Zwiebelarchitektur (Onion Architecture) nach Jeffrey Palermo [Palermo 2008]. Die API eines Application Service dient als Einstiegspunkt in die Domäne und nutzt deren Begriffe und Objekte. Die API sollte nur unveränderliche Objekte oder Datentransferobjekte veröffentlichen, sodass Benutzer der API nicht Zugriff auf die darunterliegende СКАЧАТЬ