{ End Bracket }

Perfekter API-Entwurf

James Waletzky

Warum ist es so schwierig, einen guten API-Entwurf (Application Programming Interface) zu erstellen? Schustern Sie einfach ein paar Klassen zusammen, die einige grundlegende Methoden enthalten, und fertig!

Alles schön und gut, wenn es Ihnen nichts ausmacht, dass andere Entwickler Sie verfluchen und auf Ihre API als Antimuster verweisen. Wie können Sie dies vermeiden? Streben Sie beim Erstellen von API-Entwürfen nach Perfektion. Ja, richtig: Perfektion.

Das Entwerfen einer API ähnelt dem Entwerfen einer Benutzeroberfläche. Beide sind Einstiegspunkte in eine Anwendung und das Erste, mit dem ein Benutzer (oder Entwickler) interagiert. Stellen Sie sich Ihr Lieblingselektronikgerät im Vergleich zu den Konkurrenzprodukten vor. Mein absoluter Favorit ist ein digitaler TiVo-Videorecorder. Warum ist die TiVo-Benutzeroberfläche so hervorragend?

  • Die Verwendung ist einfach
  • Missbrauch ist schwierig
  • TiVo konzentriert sich auf ein gutes Produkt
  • Bei der Bedienung passiert nichts Unerwartetes
  • Die Oberfläche ist in mehreren Jahren noch nie abgestürzt
  • Aktionen/Aufgaben sind intuitiv und einfach aufzufinden
  • Die Benutzeroberfläche ist konsistent, ausgefeilt und perfekt (oder so gut wie perfekt)

Dieselben Prinzipien gelten für den API-Entwurf. Betrachten wir eins meiner bevorzugten API-Antimuster aus der COM-Welt: IOleCommandTarget::Exec. Diese API wird in der Regel verwendet, um zu erreichen, dass ein Objekt sowie der Container, in dem es enthalten ist, sich gegenseitig Befehle erteilen.

Eine Beispielimplementierung ist ein COM-Steuerelement, das mit seinem Container kommuniziert, um Menüelemente und Symbolleisten zu ändern. Diese API ist die etwas stärker typisierte Version einer Befehlsschnittstelle, die eine generische Zeichenfolge aufnimmt, die vorschreibt, wie die Implementierung vor sich gehen soll.

Hier ist die API-Signatur von IOleCommandTarget::Exec:

HRESULT Exec( 
  const GUID *pguidCmdGroup,  // Pointer to command group
  DWORD nCmdID,               // Identifier of command to execute
  DWORD nCmdExecOpt,          // Options for executing the command
  VARIANTARG *pvaIn,          // Pointer to input arguments
  VARIANTARG *pvaOut          // Pointer to command output
);

Diese API soll entsprechend der gerade erörterten Prinzipien analysiert werden.

Ist die API intuitiv und feststellbar? Ist dies die erste Stelle, an der ein Entwickler eine Symbolleiste ändern würde? Wahrscheinlich nicht, es sei denn, Sie verfügen über genaue Kenntnisse im Hinblick auf diese API. Selbst eine Suche in der MSDN-Dokumentation würde zu keinem Ergebnis führen.

Ist sie einfach? Die API nimmt fünf Parameter mit generischen Bezeichnungen auf. Eine Dokumentation ist erforderlich, um die Verwendung jedes einzelnen Parameters gründlich zu verstehen. Bei einer einfachen API sind Absicht und Klarheit schon durch den Namen des Objekts (für den Kontext) und den Namen der Methode ersichtlich.

Ist die API stark typisiert und der Missbrauch schwierig? Nein. Die Varianten sind nicht stark typisiert, und die Ausführungsoptionen sind keine Enumerationen, sondern generische DWORDs. Stark typisierte Parameter helfen beim Auffinden von Fehlern zur Kompilierzeit statt zur Laufzeit.

Ist die API zusammenhängend? Eine Implementierung dieser Funktion hat in der Regel viele Verantwortungen, nicht nur eine. Eine gut bezeichnete API für einen bestimmten Zweck (z. B. AddToolbarButton) ist erheblich zusammenhängender. Eine Implementierung der Exec-Methode schaltet wahrscheinlich die Befehls-ID ein und weist auf Grundlage dieser Bedingung unterschiedliche Aktionen auf.

Ist die API frei von Nebenwirkungen? Schwer zu sagen, aber eine Implementierung der API ist aufgrund mangelnder Kohäsion ziemlich unberechenbar.

Ist sie zuverlässig? Das berühmte Entwurfsprinzip, „für Schnittstellen zu entwerfen“, hilft beim Durchsetzen der Testbarkeit an einem Objekt. Diese API kann inhärent getestet werden, doch die Anzahl der zu überprüfenden Testfälle könnte aufgrund mangelnder Kohäsion enorm hoch sein. Die Zuverlässigkeit ist gefährdet.

Ist sie konsistent? Diese API hat einige Konsistenz, da sie ein HRESULT zurückgibt und GUIDs zum Identifizieren von Informationssätzen verwendet. Doch es ist schwierig, die Konsistenz zu beurteilen, ohne zu wissen, wie schlecht der Entwurf der übrigen Schnittstellen auf einem Objekt ist.

Jeder, der die Verwendung von IOleCommandTarget::Exec erlernt, braucht dazu wahrscheinlich Anweisung von einem Experten oder aus einem Buch. Die besten APIs sind einfach und können ohne Dokumentation verwendet werden, obwohl eine Dokumentation erforderlich sein kann, um Details wie die Fehlerbehandlung zu verstehen.

Letztendlich sollten Ihre APIs und die APIs, die Sie überprüfen, so einfach zu verwenden sein wie TiVo. Eine leicht feststellbare, stark typisierte, zusammenhängende, zuverlässige, konsistente und ausgefeilte API ohne Nebenwirkungen macht das Leben Ihrer Entwickler leichter und führt zu erhöhter Zufriedenheit und geringeren Supportkosten. Viele Schnittstellen im Microsoft .NET Framework sind hervorragende Beispiele. Um all diese Entwurfsvorteile zu erzielen, sollten Sie Perfektion anstreben, wenn Sie einen API-Entwurf erstellen oder überprüfen.

James Waletzky ist leitender Softwareentwickler im Consumer Internet and Developer Experience-Team für Windows Mobile. Neben der Leitung eines Produktentwicklungsteams ist James Waletzky die Verbesserung technischer Verfahren bei Microsoft äußerst wichtig.