4. Implementierung

In diesem Kapitel wird die Umsetzung des Konzeptentwurfs dargestellt. Hierfür werden Werkzeuge und die verwendete Programmiersprache vorgestellt, mit denen das in Kaptitel 3 vorgestellte Programmkonzept realisiert wird.

4.1 Java 

Die Programmiersprache Java wird von Sun Microsystems44 entwickelt. Java ist eine objektorientierte Sprache und plattformunabhängig. Das bedeutete, dass Programme, welche in Java geschrieben wurden, auf allen Rechnerplattformen ausgeführt werden können auf denen eine Implementierung der Java Virtual Maschine existiert (siehe 4.1.1). Java bietet die Möglichkeit, Applets zu erzeugen, welche in Webseiten eingebaut werden können. Die Plattformunabhängigkeit garantiert, dass auch Benutzer von Solaris, Linux oder Mac-Systemen diese nutzen können.

4.1.1 Java Runtime Environment (JRE)

Die Laufzeitumgebung von Java wird Java Runtime Environment genannt. Diese beinhaltet die bereits angesprochene Java Virtuel Maschine (JVM) und eine Programmierschnittstelle (API) mit den Java-Standardbibliotheken. JVM und API müssen aufeinander abgestimmt sein und fungieren dann zusammen als virtueller Computer. Die JVM interpretiert die von einem Compiler (siehe 4.1.2) erzeugen *.class-Bytecode-Dateien. Die neueste Laufzeitumgebung von Java kann auf folgender Seite heruntergeladen werden:
http://java.sun.com/javase/downloads/index.jsp.

4.1.2 Java Development Kit (JDK)

Das Java Development Kit ist das Entwicklungswerkzeug für Java. Es beinhaltet einen Java-Compiler, mit dem sich *.java-Quelldateien zu *.class-Bytecode-Dateien übersetzen lassen, welche dann von der JVM ausgeführt werden können. Darüber hinaus liegen dem JDK weitere Entwicklungswerkzeuge bei, welche bei der der Programmerstellung hilfreich sind (beispielsweise Java Dokumentationswerkzeug oder Java Applet Viewer).

4.1.3 Java Applets

Das fertige Programm soll, wie bereits angesprochen, in Form eines Java-Applets auf einer Webseite lauffähig sein. Im Folgenden sind noch einige Informationen zu Java-Applets aufgeüfuhrt. Applets laufen auf der Client-Seite, was bedeutet, dass der gesamte Bytecode45 zunächst vom Client-Rechner heruntergeladen werden muss und das Programm dann auf diesem Rechner läuft, ohne das weitere Daten mit dem Server ausgetauscht werden müssen. Der Internetbrowser, in dem das Applet ausgeführt wird, muss über eine integrierte JVM verfügen. Das Programm läuft dann in einer abgeschotteten Laufzeitumgebung („Sandbox“), um ein Sicherheitsrisiko zu vermeiden.

4.2 Werkzeuge

Im folgendem werden alle verwendeten Software-Werkzeuge vorgestellt.

4.2.1 Entwicklungsumgebung

Als Entwicklungsumgebung wird Eclipse 3.4.1 46 eingesetzt. Eclipse bietet eine große Unterstützung bei der Implementierung. So verfügt Eclipse über eine automatische Syntax-Hervorhebung und ermöglicht schnelles automatisiertes kompilieren von allen beteiligten Klassen. Ein integrierter Applet-Viewer und das einfache einbinden externer Bibliotheken (siehe Kapitel 4.2.3) vereinfachen die Programmierarbeit.


Abbildung 17: Entwicklungsumgebung Eclipse 47

4.2.2 Visual Editor

Der Visual Editor48 ist als Eclipse-Plugin zur Erstellung graphischer Benutzeroberflächen (GUI – Graphical User Interface) hilfreich. Durch „Drag and Drop“ kann schnell ein Grundgerüst einer GUI entwickelt werden. Die verwendeten graphischen Komponenten (JButton, JSlider, etc.) sind in der Java-Standardbibliothek enthalten.

4.2.3 JFreeChart

Das primäre Ergebnis des World3-03-Modells ist die Erzeugung unterschiedlicher Graphen. Aus diesem Grund wurde auf eine ansprechende visuelle Umsetzung der Ergebnisse Wert gelegt. JFreeChart49 ist eine von Object Refinery Limited zur Verfügung gestellte Diagrammbibliothek und ermöglicht eine Vielzahl unterschiedlicher Darstellungsarten. Bei der Realisierung von diesem Projekt kommt nur das XYLineChart (Liniendiagramm) zur Anwendung. JFreeChart ermöglicht dem Benutzer mit einem Rechtsklick auf die Chartfläche verschiede Aktionen durchzuführen. So ist es möglich, die Beschriftung zu ändern, oder in das Diagramm herein oder heraus zu zoomen. Auch das Speichern als *.png-Bilddatei oder als *.pdf-Datei ist möglich und damit ganz im Sinne einer E-Learing-Implementierung des World3-03-Modells. Die Klassenbibliotheken von JFreeChart müssen von dem Client-Rechner heruntergeladen werden, da sie nicht Teil der Java-Standardbibliothek sind.

4.3 Realisierung

Nachdem das Entwurfskonzept, die Programmiersprache und die verwendeten Werkzeuge vorgestellt wurden, soll nun auf die konkrete Realisierung des Projekts eingegangen werden. Für das Verständnis von Programmstruktur und Programmablauf ist es unvermeidbar etwas  (objektorientierte) Programmiergrundkenntnis vorrauszusetzen. Dagegen werden Java-spezifische Details weitestgehend vermieden.
Zum Vorgehen:
In Kapitel 3.2 wurden die Klassen vorgestellt, welche das Programmfundament darstellen. Im nächsten Kapitel (3.3) wurde auf das Verteilungskonzept des gesamten Programms eingegangen. Im Folgenden wird zunächst auf den grundsätzlichen Programmablauf eingegangen, welcher schließlich die Ergebnisse des World3-03-Modells generiert. Eine wichtigste Aufgabe besteht darin, sich nicht in Abhängigkeitshierarchien zu „verzetteln“ (siehe Kapitel 2.2). Anschließend erfolgt die Erweiterung der Programmstruktur. Die bereits kennengelernten Klassen werden um Attribute (Daten) und Methoden (Funktionen) ergänzt. Abschließend wird noch kurz auf die Konstruktion der graphischen Benutzeroberfläche eingegangen.

4.3.1 Programmablauf

Die untere Ebene enthält die Systemelemente in Form abstrakter Klassen. Die mittlere Systemebene nutzt diese Klassen, indem jedes System Dynamics Element aus World3-03 eine Größen-Klasse erweitert. Die obere Ebene ist für die Visualisierung zuständig. In der Klasse World3_03 der mittleren Ebene werden Instanzen aller in den verschiedenen Paketen enthaltenen Klassen gebildet. Eine recheModell()-Methode in dieser Klasse weist alle erzeugten Objekte darauf hin für den nächsten Zeitschritt den entsprechenden Wert zu berechnen. Jedes Objekt steht mit anderen in Beziehung, bzw. braucht Zugriff auf den letzten Wert derer Objekte mit denen es in Beziehung steht, um selbst den nächsten Wert ausrechnen zu können. Hierzu wird jedem Objekt eine Referenz auf die Objekte übergeben, mit denen es direkt in Beziehung steht.
In Kapitel 2.2 wurde aufgezeigt, dass es praktisch nicht möglich ist eine Berechnungsreihenfolge festzulegen. Aus diesem Grund weist jedes Objekt seine Beziehungsobjekte darauf hin die entsprechenden Werte auszurechnen und zurückzugeben. Um keine Performanzeinbußen zu erhalten, sollen gleiche Werte nicht mehrmals ausgerechnet werden. Deshalb schaut das Objekt, welches einen Wert liefern soll, zuerst nach, ob der entsprechende Wert schon vorhanden ist und gibt gegebenenfalls diesen zurück. Das Programmmuster gleicht einer rekursiven Vorgehensweise, da sich die Methodenaufrufe der einzelnen Objekte ineinander verschachteln. Es sei noch angemerkt, dass Rückkopplungsschleifen im World3-03 Modell teilweise eine Schleifenlänge von über 30 aufweisen, wodurch potenziell50 genauso viele Methodenaufrufe ineinander geschachtelt werden. Im Anhang 2 ist ein UML-Sequenzdiagramm abgebildet, in dem der exemplarische Programmablauf für das einleitend dargestellte Bevölkerungsbeispiel wiedergegeben ist. Der prinzipielle Programmablauf der rechneModell()-Methode des World3-03-Modells, wird in diesem Diagramm deutlich.
Im weiteren Verlauf der Arbeit wird auf die konkrete Realisierung der verschiedenen Ebenen eingegangen, um anschließend die Ergebnisse der Umsetzung vorzustellen.

4.3.2 Realisierung der unteren Systemebene

In Kapitel 3.2 wurde das Klassendiagramm für die unterste Programmebene gezeigt. Dieses Diagramm wird nun um Methoden und Attribute erweitert und im folgendem erläutert. Das erweiterte Diagramm ist im Anhang 3 zu finden.
Größe:
Die Basis-Klasse Größe definiert Namen, Einheit, Beschreibung und eine ID-Nummer und definiert dafür Getter-Methoden, welche den entsprechenden String bzw. int-Wert zurückgeben.
Konstante:
Die Klasse Konstante erweitert die Größen-Klasse um einen Wert, mit einer get- und einer set-Methode.
Zwischengröße:
Die Zwischengrößen-Klasse erbt, wie die Konstanten-Klasse, alle Attribute und Methoden von der Größen-Klasse und erweitert diese zunächst mit einer ArrayList, in welche die Berechnungswerte eingetragen werden. Jede Zwischengröße51 verlinkt auf ein oder kein Lookup-Table. Mit einer setLookup()-Methode wird das lookup-Attribut initialisiert. Die abstrakte Methode berechnung() muss in jeder Klasse vorkommen, welche die Zwischengrößen-Klasse erweitert. In dieser Methode werden die entsprechenden Gleichungen der System Dynamics Objekte des World3-03-Modells vorzufinden sein. Die berechne()-Methode wird von der World3_03-Klasse (auf mittlerer Ebene) für alle Objekte aufgerufen oder von den einzelnen Objekten, welche eine Referenz auf das entsprechende Objekt besitzen. Wenn für den Zeitschritt, bei dem sich das Programm gerade befindet, schon ein Wert vorhanden ist, wird dieser zurückgegeben, ansonsten wird die Berechnungsprozedur (Methode berechnung() des entsprechenden Objekts) aufgerufen und der Wert ausgerechnet und zurückgegeben. ResetWerte() löscht alle Werte der ArrayList, um eine neue Berechnung durchführen zu können. GetWerte() gibt die ArrayList() zurück, um diese in Tabellenform auf oberer Programmebene darstellen zu können. Für die graphische Visualisierung werden die Werte in einer sogenannten XYSeries-Datenstruktur benötigt. Diesen Zweck erfüllt die Methode getXYSeries(). Um Tooltips52 anzeigen zu können, wird die nächste Methode generateToolTip() implementiert. Die letzten vier Methoden repräsentieren die Funktionen, welche von manchen System Dynamics Objekten gebraucht werden. WithLookup() dient dazu, einen Wert X in einen Wert Y der Nachschautabelle umzuwandeln und in die ArrayList einzutragen. In Kapitel 2.4.1 wurden die Glättungsfunktionen smooth, smoothi und smooth3 bereits theoretisch erklärt. Nun erfolgt die praktische Umsetzung. Zum allgemeinen Verständnis wird der Programmcode hierfür in Pseudoform angegeben:

Im Grunde wird genau das umgesetzt, was im theoretischen Teil erklärt wurde. Für den ersten Zeitschritt wird der Initialwert (erster Wert der übergebenen Zwischengröße) festgelegt. In allen weiteren Zeitschritten wird die neue Veränderung ausgerechnet und zu dem letzten Wert kumuliert. Die Veränderung ergibt sich aus dem Mittelwert der bereits geflossenen „Information“ (= letzter Wert) und dem Sollwert (= letzter Wert der übergebenen Zwischengröße) geteilt durch die beabsichtigte Verzögerungszeit.

  


Die smoothi-Methode macht genau das gleiche wie die smooth-Methode, mit der Ausnahme, dass für den ersten Zeitschritt ein definierter Initialwert zurückgegeben wird.

  

Die smooth3-Methode arbeitet mit verschiedenen Hilfsattributen, da auf die Werte der Verzögerungsglieder und auf die resultierenden Veränderungen zugegriffen werden muss. Es ist jeweils nur notwendig, auf den letzten Wert zuzugreifen, weshalb double-Attribute genügen, welche überschrieben werden. Die Logik der Methode ist ansonsten die gleiche wie für die smooth-Methode, mit dem Unterschied, dass die Information durch mehrere Verzögerungsglieder „fließt“.
Zustandgröße:
Auch die Zustandsgrößen-Klasse erbt alle Attribute und Methoden der Größen-Klasse und darüber hinaus die der Zwischengrößen-Klasse. Da Zustandgrößen initialisiert werden müssen, liegen hierfür die passenden Methoden – setInitial() und getInitial() – bereit. Gespeichert wird der Initialwert an erster Stelle der ArrayList, weshalb es auch notwendig is,t die resetWerte()-Methode neu zu definieren, um nicht den Initialwert zu löschen. Für die Berechnung kommt nun die integ()-Methode hinzu. Diese Methode bekommt den letzten Wert der Zustandgröße und eine Änderungsrate übermittelt. Die Änderungsrate wird über die Zeit integriert53 und zum letzten Wert dazu addiert.

Die statische integ()-Prozedur wird benötigt, da auch die Verzögerungsglieder ihre Werte zeitlich integrieren müssen. BerechneNext() wird von der World3_03-Klasse aufgerufen, da bei Zustandgrößen immer der Wert für den nächsten Zeitschritt berechnet werden muss, auf den andere Größen mit der berechne()-Methode dann zugreifen.
Lookup:
Der Konstruktor der Lookup-Klasse muss mit zwei Arrays initialisiert werden, in welchen die Daten der entsprechenden X und Y-Werte liegen. Im Konstruktor wird zugleich ein Test auf Konsistenz der übermittelten Daten durchgeführt (testAufLaengeUndOrdnung()). Nun kann für einen X-Wert ein Y-Wert angefragt werden. Dabei wird linear interpoliert, wenn der X-Wert zwischen zwei Y-Werten liegt. Sollte der X-Wert außerhalb des Wertebereichs liegen, wird der Y-Wert für den kleinsten bzw. größten zugehörigen X-Wert zurückgegeben.

Zeit:
In der Klasse Zeit sind alle Attribute und Methoden enthalten, welche den Programmablauf steuern: Getter- und Setter-Methoden verändern dazu die entsprechenden Laufzeitparameter.

4.3.3 Realisierung der mittleren Systemebene

In der mittleren Ebene sind alle Klassen, welche den System Dynamics Elementen aus World3-03 gleichkommen, enthalten. Jedes Paket enthält die Elemente, wie sie bei der Vensim-Version in den verschiedenen Teilssystemen zusammengefasst sind. Für jedes Element liegt also eine eigene Klasse vor. Jede dieser Klassen beruht auf einer Erweiterung einer Größen-Klasse der unteren Ebene. Die Attribute Name, Einheit, Beschreibung, etc. werden ihrer Entsprechung nach im Konstruktor initialisiert. Die Referenzen der Objekte mit denen die instanziierten Objekte in Beziehung stehen, werden in einer init()-Methode festgelegt. Die eigentliche Berechungsgleichung ist in der Methode berechnung() enthalten. Die nächste Abbildung zeigt ein exemplarisches Klassendiagramm für ein „realisiertes“ System Dynamics Objekt.


Abbildung 18: Klassendiagramm mit Vererbungsbeziehungen für H_GeburtenRate54

Nachdem all diese Klassen geschrieben wurden und in den Paketen vorliegen, können nun in der zentralen Klasse World3_03 Objektinstanzen von ihnen erzeugt werden. Die folgende Abbildung gibt einen Überblick über die Klasse, um anschließend auf die einzelnen Methoden eingehen zu können.


Abbildung 19: Klassendiagramm der World3_03 Klasse55

Zunächst benötigt die Klasse ein Zeit-Objekt, um den Programmablauf zu steuern. In einer Array-Struktur sind alle Konstanten und alle Zwischengrößen56 gespeichert, um leichter auf alle zusammenhängen zugreifen zu können. Der Speicherplatz im Array entspricht der Identifikationsnummer (ID). Die resetModell()-Methode löscht für alle Zwischen- und Zustandsgrößen die Werte-Arrays. GetZeit() gibt einen Link auf das Zeit-Objekt zurück.57 In der berechneModell()-Methode werden schließlich für alle Zwischengrößen die berechne()- und für alle Zustandsgrößen die berechneNext()-Methoden aufgerufen.

4.3.4 Realisierung der oberen Programmebene

In einfacher Form lassen sich die Ergebnisse der Berechnung des World3-03-Modells ohne obere Programmebene erzeugen58. Um die Ergebnisse ansprechend in Form verschiedener Graphen auszugeben, braucht es allerdings eine graphische Benutzeroberfläche. Die Java Klassenbibliothek Swing bietet eine Reihe vorgefertigter graphischer Komponenten, welche zusammen mit der JFreeChart-Klassenbibliothek zur Realisierung der „GUI“ des World3-03-Applet benutzt werden. Die vorgehensweise zur Konstruktion der GUI, wird in dieser Arbeit nicht dokumentiert, da dies zu weit vom eigentlichen Thema wegführt. Alle Möglichkeiten, die sich für einen Benutzer im Umgang mit der graphischen Benutzeroberfläche ergeben, sind im nächsten Kapitel zusammengefasst. Der Vollständigkeit halber soll jedoch noch erwähnt werden, dass die zuletzt besprochene Klasse World3_03 von der Applet-Klasse konstruiert wird und eine Simulation mit der rechneModell()-Methode gestartet wird.