Asynchronous module definition
Asynchronous module definition (AMD) ist eine JavaScript-Programmierschnittstelle, mit deren Hilfe Module und ihre Abhängigkeiten asynchron geladen werden können. Es bildet dadurch zwei grundlegende Konzepte der Software-Entwicklung, Modularisierung und Wiederverwendung, in der ansonsten funktional und monolithisch aufgebauten JavaScript-Umgebung ab. Die Modularisierung erlaubt die Aufteilung einer Javascript-Anwendung in einzelne Teilkomponenten, welche separat entwickelt und getestet werden können (Teile und Herrsche). Aufgrund von klaren Schnittstellen können AMD-Module in anderen Softwareprojekten wiederverwendet werden.
Die Vorgehensweise basiert weitestgehend auf dem Inversion-of-Control-Entwurfsmuster (IoC Pattern). Das Modul entspricht dabei dem Begriff der Bean in IoC.[1] Dadurch wird es ermöglicht, dass auch JavaScript modular aufgebaut werden kann. Das Resultat sind Module, die vergleichbar mit Java-Klassen sind, und auch genauso durch Vererbung erweitert werden können.[2] Jedes Modul muss dabei – analog zur Java-Klasse – in einer eigenen Datei gespeichert sein.
Vorteile der Modularisierung bestehen besonders im Browser-Umfeld, wo JavaScript besonders häufig genutzt wird. Es werden nur die benötigten Module geladen, nicht jedoch alles, wie es beim synchronen Laden der Fall wäre. Das erhöht einerseits die Performanz des Codes und erleichtert andererseits das Debuggen, insbesondere bei Cross-Domain-Zugriffsproblemen.[3] Weiterhin ermöglicht es eine bessere Wiederverwendbarkeit einzelner Codefragmente, ohne diese per Kopieren und Einfügen oder serverseitige Verkettung (server side concatenation) transportieren zu müssen. Daraus ergibt sich eine Verringerung der Nutzung globaler Variablen auf ein Minimum und reduziert so die durch Namespace Pollution entstehenden Probleme signifikant.
Durch die Vorgabe der Verteilung des Codes auf viele einzelne Dateien ergeben sich jedoch auch Nachteile. Jede Datei muss vom Browser in einem separaten HTTP-Aufruf geladen werden, was bei vielen kleinen Dateien sehr viel Protokoll-Overhead erzeugen kann. Dadurch kann insbesondere bei Verbindungen mit hoher Latenzzeit eine Verzögerung bemerkbar werden.[4] Dieser Overhead beim Abruf von verschiedenen Javascript-Dateien kann allerdings durch die Verwendung eines serverseitigen Bundlings kompensiert werden.[5]
Zur Umsetzung des AMD-Formats existieren verschiedene AMD-Frameworks wie z. B. RequireJS, The Dojo Loader oder curl.js.
Einsatz
Das AMD-Format sieht eine spezielle Moduldefinition bzw. Komponentendefinition vor, welche die Deklaration und das korrekte Laden von Abhängigkeiten ermöglicht. Die Moduldefinition erfolgt somit auf der Basis von Programmierkonventionen. Diese Konvention hat sich bei vielen frei verfügbaren Javascript-Bibliotheken, wie z. B. jQuery oder Socket.IO, durchgesetzt.
define("Name des Moduls", ["Abhängigkeit1", "Abhängigkeit2"], factory);
Der oben stehende Programmcode veranschaulicht die Moduldefinition im AMD-Format. Neben dem Namen des Moduls und den Abhängigkeiten wird eine Factory-Methode definiert, über die das Modul erzeugt wird. Diese Factory-Methode dient dazu, die Komponente zu instanziieren und mögliche Schnittstellen-Objekte zu exportieren. Wie erkenntlich wird, ist für den Einsatz des AMD-Formats eine Javascript-Bibliothek erforderlich, welche die define-Methode anbietet und die verschiedenen Komponenten instanziiert, sowie die Abhängigkeiten injizieren. Im AMD-Umfeld wird hierbei von einem AMD-Loader gesprochen. Die Vorgehensweise entspricht weitestgehend dem Muster der abstrakten Fabrik und der Dependency Injection mit einem IoC-Container. Auf diese Weise können beliebige Komponenten instanziiert und die benötigten Abhängigkeiten können in der entsprechenden Reihenfolge bereitgestellt werden. Die einzelnen Komponenten einer JS-Anwendung können voneinander entkoppelt werden.
define('PieChartModule', ['area', 'graph'],
function ( area, graph ) {
// Beschreibung des Plot-Modules
var plotModuleExport = {
plot: function(width, height, data){
// Einfache Nutzung der Module “graph” und “area”
return graph.drawPie(area.randomGrid(width, height), data);
}
};
return plotModuleExport;
};
);