Das e4-Programmiermodell
Das e4-Programmiermodell
Die Programmierung von Eclipse-Plug-ins einfacher zu machen, ist eine anspruchsvolle Aufgabe. Nicht deshalb, weil es einfacher nicht mehr geht, sondern weil Vereinfachung auch immer bedeutet, etwas wegzulassen. Dabei gilt es, sorgfältig abzuwägen, was noch zum Kern der Plattform gehören soll und wo die Spezialfälle beginnen, durch die das API über Gebühr komplizierter wird. Zunächst muss man zwischen einem API unterscheiden, das rein innerhalb einer bestimmten Komponente (zum Beispiel innerhalb einer View oder eines Editors) verwendet wird, und einem API, mit dem die Komponente sich in Eclipse integriert. Mit dem Begriff "e4-Programmiermodell" ist nur Letzteres gemeint. Das erste Problem anzugehen, also wie man z. B. die SWT- und JFace-Programmierung vereinfachen kann, ist Ziel anderer Technologien im e4-Projekt, zum Beispiel XWT oder TM.
Bisher ist das API, über das sich eine Komponente in Eclipse integriert, größtenteils so aufgebaut, dass man ausgehend von einem relativ allgemeinen Objekt (die Workbench-Instanz, die Workspace-Instanz oder die IDE-Klasse) die gewünschte Funktionalität finden kann - manchmal etwas versteckt durch Zwischenobjekte wie das aktive Workbench-Fenster oder die aktuelle Perspektive. Dies führt zu zwei Problemen:
- Durch die Verwendung des Singleton-Patterns entsteht eine relativ enge Kopplung zwischen allen Stellen im Code, die dasselbe Objekt referenzieren. Der Kasten "Singleton - Wolf im Schafspelz" erklärt im Detail die Probleme, die das Singleton-Pattern verursacht.
- Im Laufe der Zeit sammelt sich in diesen allgemeinen Objekten immer mehr Funktionalität an, und die ursprünglichen Verantwortlichkeiten der beteiligten Klassen sind nicht mehr klar zu erkennen. So hat zum Beispiel das Interface IWorkbench (Abb. 1) inzwischen 23 Methoden, die verschiedene Services zurückliefern - Tendenz steigend. Diese Services hängen zwar irgendwie logisch mit der Workbench zusammen, können aber kaum als echte Verantwortlichkeiten der Workbench verargumentiert werden. Andere Interfaces, wie z. B. IWorkbenchWindow, IWorkbenchPage etc., stellen weitere Services zur Verfügung.
Abstrakt betrachtet, fungieren diese Interfaces neben ihrer eigentlichen Aufgabe als relativ primitive "Service Broker", über die die verschiedenen Services verfügbar gemacht werden. Primitiv deswegen, weil im Gegensatz zur kanonischen Servicearchitektur (Abb. 2) neue Services dadurch hinzukommen, dass neue Methoden im Interface hinzugefügt werden. Dies sorgt dafür, dass das Interface nur immer größer werden kann und niemand außer der Workbench selbst Services registrieren kann.
Im e4-Programmiermodell hat Code, der eine e4-Komponente wie zum Beispiel eine View oder einen Editor implementiert, weder direkte Abhängigkeiten zu einem Service Broker noch zu Objekten, die den Zustand der Workbench repräsentieren. Die Abhängigkeiten existieren stattdessen nur auf der Ebene einzelner Services. Wie sieht nun konkret eine Klasse aus, die als View eingebunden werden kann? Zum Beispiel wie folgt:
@Inject Composite parent;
@Inject Logger logger;
@Inject void setInput(IResource resource) { ... }
@Inject IMemento persistedState;
@PostConstruct void init() {
// create UI ...
}
}
Der obige Code ist eine vereinfachte Version der Klasse ExifTable aus der e4 "Photo Demo". Die Klasse implementiert eine View, die EXIF-Daten wie zum Beispiel Verschlusszeit und Blendenzahl für die selektierte Bilddatei oder den selektierten Ordner mit Bilddateien anzeigt. Dazu werden verschiedene Objekte benötigt: ein Parent-Composite, um die Tabellen-Widgets erzeugen zu können, ein Logger-Objekt für Debugging-Zwecke, die momentan selektierte Datei oder der momentan selektierte Ordner, und ein IMemento-Objekt zum Persistieren von UI-Zustand wie zum Beispiel Spaltenbreiten in der Tabelle. Wenn eine Instanz der ExifTable-Klasse instanziiert wird, werden diese Objekte vom Dependency-Injection-Mechanismus zur Verfügung gestellt. Dazu werden zunächst die Instanzvariablen gesetzt, danach die Setter-Methoden und schließlich die init-Methode aufgerufen. Im Vergleich zu einer "normalen" View-Implementierung fällt auf:
- Die Klasse erbt nicht von einer Frameworkklasse wie ViewPart. Das macht sie auch in anderen Kontexten verwendbar, zum Beispiel in einem Dialog oder als Teilkomponente eines Editors.
- Services und andere benötigte Objekte, wie zum Beispiel das Parent-Composite, sind mit Java-5-Annotationen gekennzeichnet. Sie werden von einem Dependency-Injection-Mechanismus bereitgestellt - siehe auch den Kasten zum Thema "Dependency Injection". Dadurch gibt es keine Codeabhängigkeiten mehr zu so etwas wie einem Service Broker.
Singleton-Pattern: Wolf im Schafspelz
Gäbe es ein Buch "Design Patterns 2.0", würde das Singleton-Pattern nicht wieder aufgenommen [2]. Aber warum? Das Pattern funktioniert doch und erfüllt seine Aufgabe. Man könnte sich fragen: Was ist denn das Problem am Singleton-Pattern?
Durch die Verwendung von globalem Zustand, also von statischen Variablen, sind die Komponenten, die auf diesen globalen Zustand zugreifen, nicht mehr frei kombinierbar. Singletons sind zwar nicht ganz so "schlimm" wie statische Variablen, die gelesen und geschrieben werden können, aber sie führen trotzdem zu einer engen Kopplung. Die Kopplung besteht nicht nur zwischen dem Bundle, das das Singleton definiert, und dem Bundle, das auf das Singleton zugreift. Vielmehr sind auf einmal alle Bundles, die auf dasselbe Singleton zugreifen, miteinander gekoppelt, weil sie nur auf eine Instanz der Singleton-Klasse zugreifen können.
Konkret führt das nicht unbedingt sofort zu Problemen. Während der Entwicklung von Eclipse 1.0 waren zunächst keine Singletons verwendet worden, etwa für den Zugriff auf den Workspace (ResourcesPlugin.getWorkspace). Das bedeutete, dass der aktuell gemeinte Workspace immer als Argument von Methode zu Methode durchgereicht werden musste. Auf die Dauer wurde das so umständlich, dass die Entwickler sich entschieden, die Referenz zum Workspace als Singleton zur Verfügung zu stellen. Die Konsequenz war zunächst, dass die Entwickler es bei der Weiterentwicklung von Eclipse einfacher hatten. Erst viel später wurden die Nachteile klar: Durch die Verwendung des Workspace-Singletons ist der gesamte existierende Code, der darauf zugreift, auf einen einzigen Workspace beschränkt. Zum Beispiel kann man deswegen den inkrementellen Eclipse-Compiler innerhalb einer Java VM nur einmal laufen lassen, für einen Benutzer. Wenn man Eclipse in ein serverseitiges Backend und ein User Interface auf Clientseite aufteilen wollte, könnte also der Server nicht mehrere Benutzer gleichzeitig unterstützen.
Ähnliche Beispiele gibt es im Eclipse UI: Der Compare-Editor zeigt zwei Versionen einer Datei nebeneinander an. Wenn es sich um eine Java-Datei handelt, würde man gerne alle Features des Java-Editors verwenden können, z. B. Tastenkürzel, das bekannte Kontextmenü oder Navigationsmöglichkeiten per CTRL-Klick oder F3. All dies war sehr lange Zeit im Compare-Editor nicht vorhanden, weil der Java-Editor nur auf "oberster Ebene" funktioniert, also nicht in einen anderen Kontext eingebettet werden kann. Erst in Eclipse 3.5 hat das Compare-Team in Zusammenarbeit mit dem JDT-Team viele der gewünschten Features zum Laufen gebracht, mit relativ hohem Aufwand.
Als Gast kommentieren:
Gastkommentare werden nach redaktioneller Prüfung freigegeben (bitte Policy beachten).
-
Java Magazin - Die aktuelle Ausgabe
Inhalt, Editorial, Quellcodes und Link-Tipps zum aktuellen Java Magazin -
Archiv
-
Digital lesen
-
Java Magazin Abo
-
Eclipse Magazin - Die aktuelle Ausgabe
Inhalt, Editorial, Quellcodes und Link-Tipps zum aktuellen Eclipse Magazin -
Archiv
-
Digital lesen
-
Eclipse Magazin Abo
-
Business Technology - Die aktuelle Ausgabe
Inhalt, Autorenverzeichnis und Editorial zum aktuellen Business Technology -
Archiv
-
Digital lesen
-
Business Technology Abo

Warenkorb
Login
Registrieren
Kommentare
Ihr Kommentar zum Thema