FH Braunschweig/Wolfenbüttel Fachbereich Informatik Institut für angewandte Informatik Studienarbeit Eine Visualisierung vom N. David Mermins EPRExperiment Eingereicht bei Prof. Dr. rer. nat. habil. R. Rüdiger Von Heinrich Östreich [email protected] Braunschweig, den 23. August 2006 Inhaltsverzeichnis Inhaltsverzeichnis..................................................................................................... i Abbildungsverzeichnis............................................................................................. iii Tabellenverzeichnis................................................................................................ iv Abkürzungsverzeichnis............................................................................................ v 1. Einleitung............................................................................................................. 1 2. Theoretischer Teil................................................................................................ 2 2.1. Geschichte.................................................................................................... 2 2.2. EPR-Experiment........................................................................................... 3 2.3. EPR-Experiment nach N. David Mermin...................................................... 4 3. Praktischer Teil.................................................................................................... 9 3.1. Visualisierung vom N. David Mermins EPR- Experiment............................. 9 3.2. Anforderungsanalyse.................................................................................... 9 3.3. Applet, Java-Applet.................................................................................... 11 3.4. Modell-View-Controller-Architektur............................................................. 11 4. Implementierung................................................................................................ 16 4.1. Das Interface Config................................................................................... 17 4.2. Das Modell, die Klasse DiceModel............................................................. 19 4.3. Controller, die Klasse SwitchControl.......................................................... 26 4.4. Das View, die Klasse SwitchView............................................................... 28 4.5. Das View, die Klasse LightsView................................................................ 30 4.6. Der Controller, die Klasse AlphaControl..................................................... 33 4.7. Das View, die Klasse AlphaTextView......................................................... 34 4.8. Das View, die Klasse StatisticsView........................................................... 36 4.9. Der Controller, die Klasse ButtonControl.................................................... 37 4.10. Applet, die Klasse PlayDice...................................................................... 38 4.11. Die Klasse InstructionSet......................................................................... 40 4.12. Die Klasse Result..................................................................................... 41 4.13. Die Klasse ResultSeparate....................................................................... 42 4.14. Die Klasse Simulation............................................................................... 43 5. Zusammenfassung............................................................................................ 45 i 6. Ausblick............................................................................................................. 46 Literaturverzeichnis................................................................................................ 47 Eidesstattliche Erklärung....................................................................................... 49 Anhang.................................................................................................................. 50 Quellcode............................................................................................................... 51 AlphaControl.java.............................................................................................. 51 AlphaTextView.java........................................................................................... 51 ButtonControl.java............................................................................................. 52 Config.java......................................................................................................... 52 DelayControl.java.............................................................................................. 56 DelayTextView.java........................................................................................... 57 DiceModel.java.................................................................................................. 57 InstructionSet.java............................................................................................. 65 LightsView.java.................................................................................................. 65 LoopsControl.java.............................................................................................. 67 LoopsTextView.java.......................................................................................... 67 PlayDice.java..................................................................................................... 68 Result.java......................................................................................................... 73 ResultSeparate.java.......................................................................................... 73 Simulation.java.................................................................................................. 74 StatisticsView.java............................................................................................. 74 SwitchControl.java............................................................................................. 75 SwitchView.java................................................................................................. 76 UML-Diagramm..................................................................................................... 77 ii Abbildungsverzeicnis Abbildung 1: EPR- Experiment................................................................................ 3 Abbildung 2: EPR-Experiment nach N. David Mermin............................................. 4 Abbildung 3: Einbindung der Visualisierung............................................................ 9 Abbildung 4: MVC-Grundmodell............................................................................ 12 Abbildung 5: MVC-Architektur............................................................................... 13 Abbildung 6: java.util.Observable und java.util.Observer...................................... 14 Abbildung 7: Bereinigtes UML-Klassen-Diagramm................................................ 16 Abbildung 8: Interface Config. Definiert alle Variablen im Programm.................... 17 Abbildung 9: Modell, die Klasse DiceModel........................................................... 19 Abbildung 10: Controller, die Klasse SwitchControl............................................... 26 Abbildung 11: View, die Klasse SwitchView.......................................................... 28 Abbildung 12: View, die Klasse LightsView........................................................... 30 Abbildung 13: Controller, die Klasse AlphaControl................................................ 33 Abbildung 14: View, die Klasse AlphaTextView..................................................... 34 Abbildung 15: View, die Klasse StatisticsView...................................................... 36 Abbildung 16: Controller, die Klasse ButtonControl............................................... 37 Abbildung 17: Applet, die Klasse PlayDice............................................................ 38 Abbildung 18: Ableitungsbaum der Applet-Klasse................................................. 39 Abbildung 19: Die Klasse InstructionSet................................................................ 40 Abbildung 20: Die Klasse Result.java.................................................................... 41 Abbildung 21: Die Klasse ResultSeparate............................................................. 42 Abbildung 22: Die Klasse Simulation..................................................................... 43 Abbildung 23: UML-Diagramm der Anwendung.................................................... 77 iii Tabellenverzeichnis Tabelle 1: Bei den Versuchen aufgezeichnete Daten.............................................. 5 Tabelle 2: Gleiche Schalterstellungen führen zu gleichen Farben........................... 5 Tabelle 3: Beliebige Schalterstellungen führen zu zufälligen Farben...................... 5 Tabelle 4: Ergebnisse von einem realen Experiment mit Photonenpolarisation...... 6 Tabelle 5: Zusammenfassung der Ergebnisse........................................................ 7 iv Abkürzungsverzeichnis 2D zweidimensional API Application Programming Interface AWT Abstact Window Toolkit bzw. beziehungsweise d.h. das heißt EPR Einstein Podolski Rosen ggf. gegebenenfalls GUI Graphical User Interface HTML Hypertext Markup Language JDK Java Development Kit JRE Java Runtime Environment JVM Java Virtual Machine MVC Model View Controller SDK Software Development Kit u.a. unter anderem UML Unified Modeling Language z.B. zum Beispiel v 1. Einleitung Die Quantenphysik gehört zu den wichtigsten wissenschaftlichen Revolutionen des 20. Jahrhunderts und bietet breite Basis für viele moderne Technologien.1 Die zahlreichen experimentelle und theoretische Fortschritte in den vergangenen Jahrzehnten führten zu einem Boom in der quantenmechanischen Grundlagenforschung. Durch verbesserte Techniken ließen sich Experimente realisieren, die früher nur als „Gedankenexperimente“ galten.2 „Insbesondere die Frage, ob die Unbestimmtheit quantenphysikalischer Messgrößen lediglich eine Unkenntnis über die wahren Werte darstellt, oder ob die 'Dinge an sich' unbestimmt sind, konnte durch experimentelle Befunde zugunsten der letzteren Aussage erhärtet werden.“3 Eine wichtige Rolle in der Forschung spielt „Verschränkung“. Dieser Begriff ist zum wichtigen Bestandteil neuer Konzepte in der Informationstechnologie geworden. Dazu gehören Quantenkommunikation, -kryptographie und -teleportation, sowie Entwicklung der Quantenrechner.4 Im Folgenden werden zunächst die theoretischen Grundlagen erläutert, auf die sich diese Arbeit stützt. Im Kapitel 2 wird auf die geschichtliche Entwicklung eingegangen. Anschließend folgt die Beschreibung des Gedankenexperiments von Einstein, Podolski und Rosen. Das Kapitel 3 geht ausführlich auf das Experiment von N. David Mermin ein. Im praktischen Teil wird zunächst eine Anforderungsanalyse durchgeführt. Die Umsetzung des Programms wird in mehrere Bereiche gegliedert. Die abschließende Betrachtung des Themas erfolgt im Kapitel 5. Der Ausblick im Kapitel 6 schließt die Arbeit ab. 1 2 3 4 Vgl. FWF, Stand: 17.08.2006 Vgl. Filk, 2004, S. 13 Götz, 2003, S. 1 Vgl. FWF, Stand: 17.08.2006 1 2. Theoretischer Teil 2.1. Geschichte Im Jahre 1935 erschien im Physical Review eine Arbeit von Einstein, Podolski und Rosen mit dem Titel „Can quantum-mechanical description of physical reality be considered complete?“. Darin beschrieben die Wissenschaftler ein Gedankenexperiment, das in der Literatur als EPR-Experiment (Einstein, Podolski und Rosen) genannt wurde und später auch im Labor belegt wurde. Dabei wollten die Wissenschaftler nachweisen, dass die quantenmechanische Beschreibung der physikalischen Wirklichkeit unvollständig sein muss.5 Der Ausgangspunkt war das Verhalten der verschränkten Zweiteilchen-Zustände. Die Quantenteilchen besitzen Welleneigenschaften und sind so miteinander verbunden, dass sie unabhängig von der Entfernung die gleichen Eigenschaften aufwiesen. Albert Einstein nannte dieses Phänomen „spukhafte Fernwirkung”. Der österreichische Nobelpreisträger Erwin Schrödinger bezeichnete diese Eigenschaft als „Verschränkung” und nannte es „Essenz der Quantenphysik“.6 „Verschränkung bedeutet nicht, dass zwei auf diese Art miteinander verbundene Teilchen in der Beobachtung die gleichen Merkmale aufweisen, einfach weil sie mit den gleichen Eigenschaften „geboren” sind. Vielmehr bedeutet es, dass eine Messung, die an einem der beiden Teilchen durchgeführt wird, umgehend den Zustand des anderen Teilchens beeinflusst.“ 7 Im Jahre 1964 entwickelte John S. Bell eine Bedingung, die alle Theorien, die ein lokales Realitätskriterium beinhalten, erfüllen müssen. Dies wurde später als Bellsche Ungleichung bezeichnet. Des Weiteren zeigte Bell, dass die Quantenmechanik diese Bedingung verletzt und beschrieb einen Weg zur experimentellen Realisation.8 Danach war das Ziel der Physiker die Forderungen von EPR experimentell umzusetzen und das Ergebnis bezüglich der Bellschen Ungleichung zu überprüfen. 1982 gelang es Alain Aspect ein Experiment durchzuführen, welches diese Forderungen erfüllt.9 5 6 7 8 9 Vgl. Wallenborn, 1999, S. 1 Vgl. Zeilinger, Stand: 17.08.2006 Zeilinger, Stand: 17.08.2006 Vgl. Wallenborn, 1999, S. 16 Vgl. Jung, 2003, S. 1 2 Was Einstein als „spukhafte Fernwirkung” bezeichnete, ist heutzutage ein zentrales Forschungsobjekt für zahlreiche Experimente weltweit und ist ein wichtiger Bestandteil in dem sich neu entwickelnden Zweig der QuantenInformations-Technologie geworden. Es spielt eine wichtige Rolle bei der Entwicklung von Quantencomputern und bei der technischen Umsetzung von Quantenverschlüsselungsverfahren.10 2.2. EPR-Experiment In diesem Kapitel wird EPR- Experiment detailliert erklärt. Einstein, Podolski und Rosen betrachten bei ihrem Gedankenexperiment ein Quantensystem, das in zwei einzelne Teilchen α (Alpha) und β (Beta) zerlegt wird. Zunächst werden die Teilchen voneinander räumlich getrennt und anschließend wird eine Messung an einem Teilchen durchgeführt. Die Wissenschaftler haben erkannt, dass es aufgrund der quantenmechanischen Gesetze möglich ist, eine Art „telepathische“ Verbindung zwischen zwei Teilchen herzustellen.11 Die Abbildung 1 veranschaulicht diesen Vorgang. Abbildung 1: EPR- Experiment Quelle: eigene Darstellung in Anlehnung an Iliopoulos, 2003, S. 2 Jedes Teilchen kann den Wert „+“ oder „-“ annehmen. Wird am Teilchen α eine Messung vorgenommen, so wird im gleichen Moment der Wert des Teilchen β bekannt sein. Ist der Wert des Teilchen α „+“, dann besitzt das Teilchen β den Wert „-“ und umgekehrt.12 Dieses Phänomen ist auch dann zu beobachten, wenn die Teilchen durch große Entfernungen voneinander getrennt sind.13 10 11 12 13 Vgl. Zeilinger, Stand: 17.08.2006 Vgl. Tillemans, Stand: 17.08.2006, sowie Wallenborn, 1999, S. 1-2 Vgl. Wallenborn, 1999, S. 1-2, sowie Iliopoulos, 2003, S. 2 Vgl. Tillemans, Stand: 17.08.2006 3 2.3. EPR-Experiment nach N. David Mermin Bei seinem Experiment verwendete Mermin zwei Detektoren (A und B) und eine Quelle (C). Die Detektoren stehen weit auseinander. Jeder von denen verfügt über einen Schalter, der in eine von drei möglichen Positionen gesetzt werden kann. Beide Detektoren reagieren auf ein Ereignis durch ein Blinken - entweder ein rotes oder ein grünes Lichtsignal. Die Quelle befindet sich zwischen den Detektoren. Zwischen den Bestandteilen des Aufbaus (Detektoren A und B und Quelle C) existiert weder mechanische noch elektromagnetische oder andersartige Verbindung. Somit ist sichergestellt, dass die Detektoren weder untereinander noch zur Quelle Signale senden können. Der Schalter jeden Detektors wird unabhängig und zufällig auf eine seiner drei Positionen gesetzt. Danach wird der Knopf an der Quelle betätigt. Daraufhin leuchtet auf jedem Detektor entweder Rot oder Grün auf. Die Einstellungen von den Schaltern und die aufgeleuchteten Farben werden aufgezeichnet. Das ganze Verfahren wird mehrmals wiederholt. Die Schaltereinstellungen werden bei jedem Vorgang zufällig verändert. Abbildung 2: EPR-Experiment nach N. David Mermin Quelle: eigene Darstellung Die Daten bestehen aus einer vierstelligen Zeichenkette, die Zahlen stehen für Schalterpostionen und Buchstaben für die Lichtsignale. Beispielsweise wurde der Schalter von A auf 3 und der Schalter von B auf 2 gesetzt. Nach dem Betätigen des Knopfes an der Quelle leuchtet A Rot und B Grün auf. Das wird als „32RG“ erfasst.14 14 Vgl. Mermin, 1985, S. 4-5 4 Nach jedem Vorgang werden die Ergebnisse aufgezeichnet. Daraus ergibt sich folgende Wertetabelle (Tabelle 1). 31RR 12GR 23GR 13RR 33RR 12RR 22RR 32RG 13GG 22GG 23GR 33RR 13GG 31RG 31RR 33RR 32RG 32RR 31RG 33GG 11RR 12GR 33GG 21GR 21RR 22RR 31RG 33GG 11GG 23RR 32GR 12GR 12RG 11GG 31RG 21GR 12RG 13GR 22GG 12RG 33RR 31GR 21RR 13GR 23GR Tabelle 1: Bei den Versuchen aufgezeichnete Daten Quelle: Mermin, 1985, S. 6 Bei einer Reihe von Experimenten wurde eine Vielzahl an Daten gesammelt. Diese Daten stellen dennoch nur einen Bruchteil von diesen Werten dar. 31RR 12GR 23GR 13RR 33RR 12RR 22RR 32RG 13GG 22GG 23GR 33RR 13GG 31RG 31RR 33RR 32RG 32RR 31RG 33GG 11RR 12GR 33GG 21GR 21RR 22RR 31RG 33GG 11GG 23RR 32GR 12GR 12RG 11GG 31RG 21GR 12RG 13GR 22GG 12RG 33RR 31GR 21RR 13GR 23GR Tabelle 2: Gleiche Schalterstellungen führen zu gleichen Farben Quelle: Mermin, 1985, S. 6 In der Tabelle 2 sind die Datensätze hervorgehoben, bei denen beide Detektoren die gleichen Schaltereinstellungen haben. Dabei ist auffällig, dass die Lichter immer die gleichen Farben haben. 31RR 12GR 23GR 13RR 33RR 12RR 22RR 32RG 13GG 22GG 23GR 33RR 13GG 31RG 31RR 33RR 32RG 32RR 31RG 33GG 11RR 12GR 33GG 21GR 21RR 22RR 31RG 33GG 11GG 23RR 32GR 12GR 12RG 11GG 31RG 21GR 12RG 13GR 22GG 12RG 33RR 31GR 21RR 13GR 23GR Tabelle 3: Beliebige Schalterstellungen führen zu zufälligen Farben Quelle: Mermin, 1985, S. 6 In der Tabelle 3 sind die Datensätze hervorgehoben, die in jedem Vorgang blinkten, unabhängig von der Position des Schalters. Dabei ist zu beachten, dass die Farben vollkommen zufällig sind. Bei den durchgeführten Experimenten ist Folgendes festzustellen: ● „gleiche Schalterstellung führt mit Sicherheit zu gleicher Farbe: R/R oder G/G ● beliebige Schalterpositionen führen zu zufälligen Farbkombinationen: R/R, 5 R/G, G/R oder G/G, gleiche und verschiedene Farben gleich häufig ● Gesamtwahrscheinlichkeit für gleiche Farbe: 1/2“15 Dies kann in einem realen Experiment mit Photonenpolarisation bestätigt werden. Alice 1 Bob 2 3 1 2 3 1 2 3 1 2 3 1 1/4 1/4 1/4 1 1/4 1/4 1/4 1 Schalterposition Wahrscheinl. für gleiche Farbe Tabelle 4: Ergebnisse von einem realen Experiment mit Photonenpolarisation Quelle: Rüdiger, 2004, S. 17 Die Wahrscheinlichkeit für gleiche Farbkombinationen wird folgendermaßen errechnet: Pr [gleiche Farbe] = 1 3 * 1 3 (3*1+3*2* 1 4 )= 1 2 Bei zwei Detektoren mit je drei möglichen Schalterpositionen (1/3 * 1/3 oder 2 * 1/3) ist bei den Kombinationen mit den gleichen Schalterstellungen (11, 22 und 33) an den Detektoren die Wahrscheinlichkeit für gleiche Farbkombinationen jeweils gleich eins (3 * 1) und bei anderen Schalterpositionen (12, 13, 21, 23, 31 und 32) ist dieselbe Wahrscheinlichkeit jeweils gleich einviertel (3 * 2 *1/4). Was nach dem Ausrechnen ½ ergibt. Dies kann dann zustande kommen, wenn: ● Alice und Bob untereinander kommunizieren und/oder ● das Ergebnis hängt von dem Zufallsereignis ab, das vorher ausgewürfelt wird Die erste Erklärung widerspricht der speziellen Relativitätstheorie. Die zweite Erklärung ist ebenfalls nicht zutreffend, da diese bereits bei einem Experiment mit 3 Schalterpositionen, wie oben beschrieben, nicht funktioniert. Dies wird im folgenden statistischen Experiment beschrieben: 15 Rüdiger, 2004, S. 16 6 Bei zwei unterschiedlichen Farben (rot und grün) und drei Schalterpositionen (1, 2 und 3) sind acht Farbkombinationen möglich, die im Folgenden als Instruktionen bezeichnet werden (RRR, RRG, RGR, RGG, GRR, GRG, GGR und GGG). Dabei stehen beispielsweise bei der Instruktion GRR für grün bei Schalterposition 1, rot bei 2 und rot bei 3.16 Wenn alle Möglichkeiten für einen Versuchsaufbau mit zwei Detektoren aufgezeichnet werden, ergibt sich folgende Tabelle 5: Schalterposition gleiche Farbe Instruktion 1 2 3 Wahrsch. 1 R R R 11 22 33 12 13 21 23 31 32 p1 2 R R G 11 22 33 12 13 21 23 31 32 p2 3 R G R 11 22 33 12 13 21 23 31 32 p3 4 R G G 11 22 33 12 13 21 23 31 32 p4 5 G R R 11 22 33 12 13 21 23 31 32 p5 6 G R G 11 22 33 12 13 21 23 31 32 p6 7 G G R 11 22 33 12 13 21 23 31 32 p7 8 G G G 11 22 33 12 13 21 23 31 32 p8 Gelb = ungleiche Farbe Tabelle 5: Zusammenfassung der Ergebnisse Quelle: eigene Darstellung in Anlehnung an Rüdiger, 2004, S. 20 Aus der obigen Tabelle kann entnommen werden, dass bei den Instruktionen 1 und 8 die Wahrscheinlichkeit für gleiche Farbe gleich 1 (9/9) ist. Bei den Instruktionen 2 bis 7 ist die Wahrscheinlichkeit für gleiche Farbe hingegen 5/9. Die Gesamtwahrscheinlichkeit für gleiche Farbe wird nach folgender Formel berechnet: Die Summe von α und β ist 16 Vgl. Rüdiger, 2004, S. 16 ff. 7 gleich eins und entspricht der Gesamtwahrscheinlichkeit, dass die gleiche Farbkombination ausfällt.17 7 α := ∑ pi , β: =p1 + p8 und α+β=1 i -1 Daraus ergibt sich die Wahrscheinlichkeit für gleiche Farbe mit dem Instruktionensatz: p mit Instruktion:= Pr [gleiche Farbe] = 1 - 4 9 α, 0 ≤ α ≤1 Die Wahrscheinlichkeit für gleiche Farbe mit dem Instruktionensatz ist also beschränkt gemäß: 1/2 < 5/9 ≤ p mit Instruktion ≤1 Aus den vorhergegangenen Experimenten und aus der Wahrscheinlichkeitstheorie ist jedoch bekannt, dass: pquantenmechanisch = 1/2 Daraus ergibt sich: ● die so genannten lokalrealistischen Theorien sind widerlegt ● eine Erklärung mittels klassischer Physik ist nicht möglich18 John Preskill sagte in diesem Zusammenhang: „.... To some people, the peculiar correlations unmasked by Bell's theorem call out for a deeper explanation than quantum mechanics seems to provide. They see the EPR phenomenon as a harbinger of new physics awaiting discovery. But they may be wrong. We have been waiting over 60 years since EPR, and so far no new physics.“19 17 Vgl. Rüdiger, 2004, S. 20-21 18 Vgl. Rüdiger, 2004, S. 19 19 Rüdiger, 2004, S. 22 8 3. Praktischer Teil 3.1. Visualisierung vom N. David Mermins EPR- Experiment Ziel dieser Arbeit ist es, eine Visualisierung vom Mermins EPR- Experiment zu entwickeln. Schon lange vor Erfindung des Computers diente die Visualisierung als ein wichtiges Hilfsmittel zum Verständnis technisch-wissenschaftlicher Zusammenhänge. Die Visualisierung kann nach folgendem Muster ablaufen: Abbildung 3: Einbindung der Visualisierung Quelle: Lehle, 1997, S. 13 Der Mensch beobachtet ein Objekt und führt Experimente mit diesem durch, die ihm Messdaten liefern. Die Ergebnisse werden mit geeigneten Computerprogrammen in Bilder umgewandelt. Dieser Vorgang wird durch mathematische Erkenntnisse erweitert, welche erlauben Schlüsse zu ziehen und Vorhersagen zu machen.20 3.2. Anforderungsanalyse Unter Anforderungsanalyse wird ein Teil des Software- und Systementwicklungsprozesses bezeichnet, der dazu dient, die Anforderungen des Auftraggebers an das zu entwickelnde System oder ein Anwendungsprogramm zu ermitteln.21 Bei einer objektorientierten Entwicklung werden die Objekte des 20 Vgl. Lehle, 1997, S. 13 21 Vgl. Wikipedia, Stand: 17.08.2006 9 Anwendungsbereichs analysiert und anschließend strukturiert, so dass das erstellte Modell ein Gerüst für die Implementierung darstellt. Das zu entwickelnde System soll korrekt, vollständig, eindeutig, verständlich und nachvollziehbar erklärt werden.22 Um das Experiment von N. David Mermin visuell umsetzen zu können, müssen, bezugnehmend auf seinen Versuchsaufbau, folgende Bestandteile vorhanden sein: ● A und B: zwei Detektoren, die jeweils mit einem Schalter und zwei Lichtern (rot und grün) ausgerüstet sind. Jeder Schalter kann auf die Positionen 1, 2 oder 3 eingestellt werden. ● C: eine Quelle, die das Signal an die Detektoren aussendet. Zusätzlich sind folgende Komponenten erforderlich: ● Eine Möglichkeit, um die Werte für Alpha und Beta einzustellen. ● Graphische Darstellung der Ergebnisse, wie oft (prozentual) wurde die gleiche Farbe an den Detektoren A und B angezeigt. ● Eine Möglichkeit, die Anzahl der durchzuführenden Experimente einzustellen. ● Eine Komponente, mit der sich die Ausführungsgeschwindigkeit einstellen lässt. Damit der Anwender das Programm von einem beliebeigen Web-Browser aus ausführen kann, erfolgt die Umsetzung als Applet in der Programmiersprache Java. Darüber hinaus soll eine spätere Änderung oder Erweiterung des Systems einfach durchführbar sein. Weiterhin soll sichergestellt werden, dass die einzelnen Module des Programms wiederverwendbar sind. Um diese Forderungen zu erfüllen und um die Trennung der Verarbeitungslogik von der Darstellung zu erreichen, ist es erforderlich bei der Implementierung das Modell-View-ControllerPrinzip (MVC-Prinzip) anzuwenden. 22 Vgl. Meißner, 2002, S. 31 10 3.3. Applet, Java-Applet Java-Applet ist ein in der Programmiersprache Java geschriebenes und im WebBrowser oder Applet-Viewer lauffähiges Programm.23 Applets werden aus einer HTML-Seite aufgerufen und in der Java Virtual Machine (JVM) auf dem Rechner ausgeführt. Die JVM kann entweder ein Teil des Web-Browsers sein oder in Form einer Java-Laufzeitumgebung auf dem Rechner nachträglich installiert werden. Da die Applets auf dem Rechner des Anwenders ausgeführt werden, stellen diese ein Sicherheitsrisiko dar. Da sie jedoch nur innerhalb einer JVM ausgeführt werden können, ist dieses Risiko kontrollierbar.24 Im Unterschied zu normalen JavaApplikationen dürfen Applets nicht auf das Dateisystem zugreifen und beispielsweise Dateien löschen.25 Zu Problemen kann es nur dann kommen, wenn die JVM fehlerhaft ist. Vorteilhaft an der Applet-Technologie ist der volle Funktionsumfang aus der J2SEAPI unter Berücksichtigung der Sicherheitsregeln. Außerdem eignen sich Applets gut für Anwendungen in firmeninternen Netzwerken, bei denen alle Anwender die gleiche Java-Version haben. Als Nachteil wird die Größe der Java Runtime Environment (JRE) angesehen, die bei Sicherheitsproblemen komplett neu heruntergeladen und installiert werden muss (etwa alle 1–2 Monate).26 3.4. Modell-View-Controller-Architektur Wie auf der Abbildung 4 zu sehen ist, besteht die klassische MVC-Architektur aus drei Bestandteilen: dem Modell, dem View und dem Controller. Das MVC-Prinzip hat als Ziel die Trennung der Verarbeitung eines Problemgebietes von dessen Präsentation zu ermöglichen. So eine Trennung erlaubt flexibles Programmdesign, das die spätere Änderung oder Erweiterung vereinfacht. Weiterhin reduziert das MVC-Konzept die Komplexität des Programm Übersichtlichkeit. 23 24 25 26 Vgl. Krüger, 2003, S. 273 Vgl. Wikipedia, Stand: 17.08.2006 Vgl. Ullenboom, 2005, Stand: 17.08.2006 Vgl. Wikipedia, Stand: 17.08.2006 11 und verbessert dessen Abbildung 4: MVC-Grundmodell Quelle: eigene Darstellung ● „Das Modell enthält die Daten des Dialogelements und speichert seinen Zustand. ● Der View ist für die grafische Darstellung der Komponente verantwortlich. ● Der Controller wirkt als Verbindungsglied zwischen beiden. Er empfängt Tastatur- und Mausereignisse und stößt die erforderlichen Maßnahmen zur Änderung von Model und View an.“27 Das Modell beinhaltet die gesamte Verarbeitungslogik. Dabei ist zu beachten, dass ein Model mehrere Views gleichzeitig haben kann. „Damit Veränderungen des Modells in allen Benachrichtigungsmechanismus zugeordneten Views über Views sichtbar implementiert, mit werden, dem das wird ein Modell die Änderungen informiert. Diese Vorgehensweise entspricht dem Observer-Pattern.“28 Vorteilhaft an einer MVC-basierten Architektur ist die Möglichkeit durch die Trennung der einzelnen Teile das Aussehen einer Komponente zu verändern. Dabei ist es nicht notwendig deren Verhalten zu modifizieren. Als Nachteil wird die strikte Trennung von View und Controller angesehen, da dadurch die Kommunikation zwischen diesen Bestandteilen komplex und unüberschaubar werden kann. Die MVC-Architektur entkoppelt das mathematische Modell von der eigentlichen Programmsteuerung. Dabei erfolgen die Auswertung der Eingaben und Ausgaben getrennt. Die Abgrenzung dieser Aufgaben wird durch drei Klassen, Modell, View und Controller ermöglicht. 27 Krüger, 2003, S. 751 28 Krüger, 2003, S. 751 12 Abbildung 5: MVC-Architektur Quelle: Meiler, o.J., S. 3 Das Modell enthält die Daten und Kernfunktionalität der Anwendung. Es ist unabhängig von den anderen Komponenten und hat folgende Aufgaben: es kennt alle Views und Controller, die zum Einsatz kommen, und informiert diese über Änderung in den Anwenderdaten. View ermöglicht die Darstellung der Daten aus dem Modell und reagiert auf Datenveränderungen im Modell. Controller nimmt die Eingaben des Benutzers entgegen und reicht diese an das Model weiter. Ein Controller kann auf ein spezielles View zugeschnitten sein.29 Von den Veränderungen im Modell ist das Verhalten des View und eventuell des Controllers abhängig. Daher müssen View und Controller das Modell überwachen. Um dies zu ermöglichen, stellt Java folgende Klassen bereit: ● Observable, die überwachten Klassen (Modell) werden von dieser Klasse abgeleitet. ● Observer, die überwachenden Klassen (View, Controller) implementieren dieses Interface. Die Klasse Observable (java.util.Observable) bietet folgende Funktionalität: ● Verwaltung beliebig vieler Observer ● Registrierung der Observer 29 Vgl. Meiler, o.J., S. 3 13 ● Änderungen in den Anwenderdaten werden registriert ● Registrierten Observern werden die Änderungen mitgeteilt ● Streichung der Observer aus der Observerliste Klassen, welche eine andere Klasse überwachen, implementieren das Interface Observer (java.util.Observer), dabei muss nur die Methode update() realisiert werden.30 Die Methode update(Observable, Object) wird bei bei Änderungen des Observable-Objektes aufgerufen. Das zweite Argument der update()-Methode ist die hereinkommende Nachricht, welche über die Methode notifyObservers() versendet wurde.31 Der Mechanismus zur Überwachung wird in der Abbildung 6 dargestellt. Abbildung 6: java.util.Observable und java.util.Observer Quelle: Meiler, o.J., S. 3 Zunächst wird die Klasse Modell als Ableitung der Klasse Observable implementiert. Dabei werden die Methoden zum Lesen getData() der darzustellenden Daten coreData und service() öffentlich gemacht. Durch die Observable-Methoden setChanged() und notifyObservers() werden die Änderungen veröffentlicht. In der Klasse View wird die draw()-Methode implementiert und übernimmt die Einrichtung des Überwachungsmechanismus. Diese ist für die Datenausgabe zuständig und ist von der verwendeten Plattform abhängig. Die Verbindung zu den Modell- und Controller-Klassen wird mittels der initialize()-Methode hergestellt. 30 Vgl. Meiler, o.J., S. 4 31 Vgl. Ullenboom, 2005, Stand: 17.08.2006 14 Diese Methode richtet das View als Observer des Modells mit Hilfe von addObserver() ein. Ein für diese View-Klasse spezifischer Controller wird mit der Methode makeController() geschaffen. Mittels der getData()-Methoden greift View auf die Daten der Klasse Modell zu. Über die update()-Methode wird die Benachrichtigung über die Änderungen implementiert. Die View-Klasse steuert auch die Abmeldung des Überwachungsmechanismus. Dabei meldet die Methode release() das View beim Modell ab, löst die Verbindungen zu ihm und gibt die nicht mehr benötigten Ressourcen frei. Die Klasse Controller reagiert mittels der handleEvent()-Methoden auf die Eingaben des Benutzers. Die Beziehung der Controller-Klasse zu den Modell- und View-Klassen wird über eine initialize()-Methode aufgebaut. Bei Bedarf richtet die Methode initialize() den Controller über addObserver() als Observer des Modells ein. Mittels der update()-Methode kann der Controller auf Veränderung der Daten reagieren. Die getData()-Methoden erlauben dem Controller die Informationen aus dem Modell abzurufen. Über die service()-Methoden übergibt die Klasse Controller die Eingaben des Benutzers an das Modell.32 32 Vgl. Meiler, o.J., S. 4-5 15 4. Implementierung Im Folgenden wird die Implementierung der Visualisierung des EPR-Experimentes von N. David Mermin detailliert dargestellt. Bei der Realisierung wurde auf die Erfüllung aller an das Programm gestellter Anforderungen ein großer Wert gelegt. Für den Aufbau des Simulators wurde das Modell-View-Controller-Prinzip angewendet. Auf der Abbildung 7 ist das um Attributen und Operationen bereinigtes UML-Klassen-Diagramm des Applets zu sehen. Im weiteren Verlauf der Arbeit wird genauer auf einzelne Komponenten des Abbildung 7: Bereinigtes UML-Klassen-Diagramm Quelle: eigene Darstellung 16 4.1. Das Interface Config Im Interface Config sind alle Variablen definiert, die von den anderen Klassen verwendet werden. Die Abbildung 8 zeigt das Diagramm dieses Interface in UMLNotation. Abbildung 8: Interface Config. Definiert alle Variablen im Programm. Quelle: eigene Darstellung Diese Variablen sind den anderen Klassen nur über das Model, die Klasse DiceModel zugänglich. Dieser Mechanismus wurde zwecks einfacherer Wartung des Programms eingerichtet – alle Variablen des Programms sind in diesem Interface zentral versammelt und müssen somit nur da geändert werden. Anschließend ist nur eine Neukompilierung des Programms notwendig. Einige Variablen werden in diesem Interface errechnet, wie beispielsweise die Variable zur Berechnung des Umfangs des Kreises im Bogenmaß: ... 045 046 // Umfang des Kreises im Bogenmaß static final double sphere = 2 * Math.PI; ... 17 Dabei wird auch der Kreis für den Schalter definiert: ... 091 092 093 // Circle static final Ellipse2D circle = new Ellipse2D.Double(zX - rR * rV, zY - rR * rV, 2 * rR * rV, 2 * rR * rV); ... Die bei den Berechnungen verwendeten Variablen sind ebenfalls im Interface definiert, z.B. die Mitte des Panels, welche über die x- und die y-Koordinaten festgelegt wird, und vordefinierter Radius des Elementes: ... 015 016 017 018 019 020 // x- Koordinate der Panelmitte static final int zX = 100; // y- Koordinate der Panelmitte static final int zY = 100; // Radius eines Kreises static final int rR = 75; ... Des weiteren wird sichergestellt, dass der Mausklick auch in einem definierten Bereich (in diesem Fall 20%) außerhalb des sichtbaren Kreises noch gültig ist: ... 041 042 // Noch gueltiger Radius static final double rV = 1.20; ... Außerdem sind in Config die Strings definiert, wie beispielsweise die Beschriftung der Buttons oder Tooltips: ... 144 145 // Beschriftung des Buttons START static final String stringStart = "START"; ... 164 165 166 // ToolTip fuer den Button START static final String sToolTipStart = new String("Press the button to run this program"); ... 18 4.2. Das Modell, die Klasse DiceModel Die Abbildung 9 zeigt das Diagramm der Klasse DiceModel in der UML-Notation. Abbildung 9: Modell, die Klasse DiceModel Quelle: eigene Darstellung 19 Die Klasse DiceModel wird von der Java-eigenen Klasse Observable (java.util.Observable) abgeleitet und implementiert das Interface Runnable (java.lang.Runnable). Sie enthält die Kernfunktionalität der Anwendung. Über diese Klasse werden alle Variablen aus dem Interface Config den anderen Klassen zur Verfügung gestellt: ... 117 118 protected final String stringStart = Config.stringStart; // Beschriftung des Buttons STOP ... 136 137 // ToolTip fuer den Button START protected final String sToolTipStart = Config.sToolTipStart; ... In der Methode notificationOfChange() wurden die von der Klasse Observable bereitgestellten Methoden setChanged() und notifyObservers() zusammengefasst. Somit wird bei Aktualisierungen in anderen Methoden nur notificationOfChange() aufgerufen, welche dadurch die Änderungen veröffentlicht. ... 295 296 297 298 final private void notificationOfChange() { setChanged(); notifyObservers(); } ... In der Methode setSwitch() wird der Schalter (hier Switch) umgestellt. Dazu wird bei einem Klick innerhalb des Kreises und in einem gültigen Bereich außerhalb dessen (zusätzlich 20% zum Radius des Kreises) von der Klasse ButtonControl die Koordinate des Klicks an das Modell durchgereicht. Dabei wird mittels der booleschen Variablen LEFT (entspricht true, Zeile 006 im Interface Config) und RIGHT (entspricht false, Zeile 008 im Interface Config) zwischen den Schaltern A und B unterschieden. Hierbei ist die Variable LEFT dem Schalter A und die Variable RIGHT dem Schalter B zugeordnet. In der Methode setSwitch() werden diese Informationen dazu verwendet, um den Zeiger des jeweiligen Schalters in die gewünschte Position zu bringen. Bei der Berechnung wird die Position des Klicks in Relation zum Zentrum des Kreises bestimmt. Der Kreis ist in drei gleich große Bereiche eingeteilt worden. In Abhängigkeit davon in welchem Bereich des Kreises der Mausklick erfolgte, wird ein Wert ermittelt. Dieser Wert ist eine Zahl (in 20 Java int – Integer), die nur 0, 1 oder 2 sein kann. Diese Zahlen werden in der Klasse SwitchView den Schalterpositionen 1, 2 oder 3 entsprechend zugeordnet. ... 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 final protected void setSwitch (int sourceX, int sourceY, boolean lr) { adjacent = sourceX - zX; opposite = zY - sourceY; hypotenuse = Math.sqrt((adjacent * adjacent) + (opposite * opposite)); angle = Math.acos(adjacent / hypotenuse); if (opposite > 0) { angleXY = sphere - angle + shiftScan; } else { angleXY = angle + shiftScan; } if (lr == LEFT) { signalSwitchLeft = (int) ((angleXY / (sphere / parts)) % parts); } else if(lr == RIGHT) { signalSwitchRight = (int) ((angleXY / (sphere / parts)) % parts); } notificationOfChange(); } ... Die Methode getSwitch() gibt die Werte zurück, die in der Methode setSwitch() ermittelt wurden. Dabei wird auch unterschieden, ob es sich um Schalter A (hier LEFT) oder B (hier RIGHT) handelt. ... 262 263 264 265 266 267 268 269 270 271 272 final protected int getSwitch(boolean lr) { if (lr == LEFT) { return signalSwitchLeft; } else if(lr == RIGHT) { return signalSwitchRight; } else { // exception // because of boolean is unreachable return 0; } } ... Die Methode setLights() ist zuständig für die korrekte Ausgabe der Lichtsignale in den Detektoren A und B. In der Zeile 327 wird ein neues InstructionSet-Objekt erzeugt. Die Information über auszugebende Farbe wird mittels der Klasse ResultSeparate aus der Klasse InstructionSet gewonnen. Dies wird in der 21 Methode setLights() dazu verwendet, um den Wert für eine Farbe zu setzten: rot = 0, grün = 1 und weiß = default = 2. ... 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 final protected void setLights(int instrNumber, int signalSwitch, boolean lr) { int lightsValue; if(lr == LEFT) { this.signalSwitchLeft = signalSwitch; } else if(lr == RIGHT) { this.signalSwitchRight = signalSwitch; } else { // not reachable // because of boolean is unreachable } InstructionSet instrSet = new InstructionSet(); ResultSeparate result = instrSet.apply (instrNumber, signalSwitch); // deterministic if (result.toString() == "R") { // Light, RED lightsValue = 0; } else if (result.toString() == "G" ) { // Light, GREEN lightsValue = 1; } else if (result.toString() == "D" ) { // Light, DEFAULT // only with RESET reachable lightsValue = 2; } else { // Light, DEFAULT lightsValue = 2; } if (lr == LEFT) { this.signalLightsLeft = lightsValue; } else if (lr == RIGHT) { this.signalLightsRight = lightsValue; } else { // not reachable } notificationOfChange(); } ... Die Methode getLights() liefert die in der Methode setLights() ermittelten Werte zurück. Dabei wird an dieser Stelle ebenfalls unterschieden, ob es sich um das Lichtsignal für Detektor A oder B handelt. ... 349 350 351 352 353 354 355 final protected int getLights (boolean lr) { if(lr == LEFT) { return signalLightsLeft; } else if(lr == RIGHT) { return signalLightsRight; } else { // exeption 22 356 357 358 359 // because of boolean is unreachable return 0; } } ... Die Methode setStatistics() berechnet den durchschnittlichen Wert (statisticsAverage) mit Hilfe vom Zähler für gleiche Farbe in den Detektoren A und B (countLights) und mittels Zählers für die Gesamtzahl der Experimente (countClicks). Dieser Wert wird verwendet von der Klasse StatisticsView um den Fortschrittsbalken zu aktualisieren. ... 299 300 301 302 303 304 305 306 307 308 309 310 311 312 final protected void setStatistics (int countClicks, int countLights) { if (countClicks != 0) { statisticsAverage = statisticsMax * countLights / countClicks; } else if (countClicks == 0) { // Ueber RESET erreichbar // weil setStatistics(0, 0); aufgerufen wird // notwendig um StatisticsView (JProgressBar) // auf 0 zurueckzusetzen statisticsAverage = statisticsMin; } notificationOfChange(); } ... Da bestimmte Objekte der Klasse DiceModel als Thread (java.lang.Thread) ablaufen sollen, implementiert diese das Interface Runnable. Nach dem Betätigen des „START“-Buttons wird in der Klasse ButtonControl mit dem Aufruf der Methode start() der Thread gestartet. Die Methode start() ist ein Bestandteil von Thread, welche für die Steuerung (starten, beenden) von Threads zuständig ist. Die Methode run() wird innerhalb eines Thread ausgeführt. Darin wird der auszuführende Programmcode eingebettet. In dem vorliegenden Fall ist es die forSchleife. Die Klasse LoopsControl gibt vor, wie oft diese for-Schleife durchlaufen wird. Dabei wird unterschieden, ob die Anzahl von Schleifen gleich oder größer eins ist. Der Unterschied besteht darin, dass bei genau einer Schleife der Anwender selbst die Positionen der Schalter A und B bestimmt, d.h. jedes Experiment wird einzeln durchgeführt. Bei mehr als einer Schleife werden die Positionen der Schalter A und B „automatisch“ mittels eines Zufallsgenerators umgestellt. Damit kann der Anwender mehrere Experimente auf einmal 23 durchführen. Weiterhin sind der Gesamtzähler (countLoops) und der Zähler für gleiche Farben in den Detektoren A und B (countLights) in dieser Methode untergebracht. Von hier aus wird mit diesen Zählern die Methode setStatistics() aufgerufen. Die for-Schleife bzw. die Methode run() kann vom Anwender durch Betätigen des Buttons „STOP“ angehalten werden. Nach der Abarbeitung des Threads oder dessen Anhalten wird in der Zeile 399 die Methode setBStop() der Klasse PlayDice aktiviert. Diese Methode sperrt bzw. gibt bestimmte Buttons frei. Weiterhin kann der Anwender die Ausführungsgeschwindigkeit des Threads bestimmen (Zeile 373). Dabei wird die Geschwindigkeit stufenlos mittels Einstellung der Verzögerung (delay) reguliert. Die Verzögerung kann damit in einem Bereich zwischen null Sekunden (entspricht keiner Verzögerung, somit normale Ausführgeschwindigkeit) und den momentan im Interface Config definierten zwei Sekunden eingestellt werden. Die Kontrolle der Ausführungsgeschwindigkeit wurde mit einer try-catch-Anweisung ergänzt, um mögliche Fehler abzufangen. ... 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 final public void run() { InstructionSet instrSet = new InstructionSet(); Simulation sim = new Simulation(instrSet); Result result; alpha = getValueAlpha(); loops = getValueLoops(); stop = false; int switchL = 0; int switchR = 0; countClicks = getCountClicks(); countLights = getCountLights(); for (int k = 0; (k < loops) && (!stop); k++) { try { Thread.sleep(delayMax - getValueDelay()); } catch (InterruptedException ie) { ie.printStackTrace(); } int instrNo = sim.chooseInstruction(alpha); if (loops == loopsLimit) { // Loops == 1 switchL = getSwitch(LEFT); switchR = getSwitch(RIGHT); } else if (loops > loopsLimit) { // Loops > 1 switchL = sim.chooseSwitch(); switchR = sim.chooseSwitch(); } else { // Loops == null or negative // not reachable System.err.println("DiceModel.run(): ERROR!"); 24 387 388 389 390 391 392 393 394 395 396 397 398 399 400 } result = instrSet.apply(instrNo, switchL, switchR); // deterministic setLights(instrNo, switchL, LEFT); setLights(instrNo, switchR, RIGHT); countClicks++; if (result.colorL == result.colorR) { countLights++; } setStatistics(countClicks, countLights); } stop = false; pd.setBStop(); } ... Die Methode resetAll() wird durch Betätigen des Buttons „RESET“ über die Klasse ButtonControl aktiviert. Diese Methode ermöglicht dem Anwender das Programm auf die Starteinstellungen bei Bedarf zurückzustellen. ... 221 222 223 224 225 226 227 228 229 230 231 final protected void resetAll() { setValueAlpha(startAlpha); setValueLoops(startLoops); setValueDelay(startDelay); setStatistics(resetStatCC, resetStatCL); setCountClicks(resetStatCC); setCountLights(resetStatCL); setLights(defaultLights, startSwitch, LEFT); setLights(defaultLights, startSwitch, RIGHT); notificationOfChange(); } ... Weiterhin besteht die Klasse DiceModel aus einigen set- und get-Methoden, die unterschiedliche Variablen für die Berechnungen und Ausgaben zuordnen bzw. bereitstellen. Als Beispiel sind die Methoden setValueAlpha() und getValueAlpha() zu nennen: ... 273 274 275 276 277 278 279 280 final protected void setValueAlpha (int valueAlpha) { this.valueAlpha = valueAlpha; valAlpha = (double) valueAlpha / (double) alphaMax; notificationOfChange(); } final protected double getValueAlpha() { return valAlpha; } ... 25 In der Methode setValueAlpha() wird die Varibale valAlpha gesetzt. Der Wert von Alpha darf zwischen null und eins liegen. Mit JSlider lässt sich dieser Wertebereich jedoch nicht präzise einstellen. Daher wurde ein wesentlich größerer Wertebereich (0 bis 1000) definiert, der innerhalb des Programms anhand einer Transformation umgerechnet wird. Die Methode getValueAlpha() hat die Variable valAlpha als Rückgabewert. valAlpha wird später von der Klasse AlphaTextView zur graphischen Darstellung des Wertes als Text verwendet. Weiterhin wird diese Variable beim Aufruf der Methode chooseInstruction() der Klasse Simulation als Parameter übergeben, um damit die Berechnungen zur Auswahl der Instruktion durchzuführen. Über set- und get-Methoden wird sichergestellt, dass andere Klassen oder Methoden auf Variablen zugreifen können. Andere set- und get-Methoden sind ähnlich aufgebaut, aber derartige Berechnungen werden von diesen Methoden nicht durchgeführt. Daher wird auf diese Methoden im Einzelnen nicht weiter eingegangen. 4.3. Controller, die Klasse SwitchControl Auf der Abbildung 10 ist das vollständige Diagramm der Klasse SwitchControl nach der UML-Notation zu sehen. Mittels dieser Klasse werden die Schalter A und B zwischen den Positionen 1, 2 oder 3 umgestellt. Abbildung 10: Controller, die Klasse SwitchControl Quelle: eigene Darstellung Da die Positionen der Schalter im Programm mittels eines Klicken mit der Maus umgestellt werden, wird diese Klasse von der Klasse MouseAdapter (java.awt.event.MouseAdapter) abgeleitet (Zeile 05). MouseAdapter stellt dabei eine leere Implementierung der Klasse MouseListener (java.awt.event.MouseListener) dar. „MouseListener definiert eine Schnittstelle für Klassen, die vom Auftreten eines MouseEvent benachrichtigt werden wollen.“33 33 Middendorf u.a., Stand: 17.08.2006 26 Diese Implementierung ermöglicht in der Klasse SwitchControl nur die Methoden zu überschreiben, die tatsächlich von dem Programm benötigt werden. Da das Umstellen des Schalters auf einen Mausklick erfolgen soll, wird in diesem Fall nur die Methode mouseClicked() überschrieben. In der Methode mouseClicked() wird geprüft, ob der Mausklick innerhalb des definierten Bereiches (der Kreis plus 20% vom Radius, ist im Interface Config definiert, darauf kann nur über die Klasse DiceModel zugegriffen werden) oder außerhalb dessen erfolgte. Im Falle positiver Überprüfung wird die Koordinate des Mausklicks an das Modell (die Klasse DiceModel) weitergereicht, genauer an die Methode setSwitch(). Diese Weitergabe erfolgt in Verbindung mit der Information, um welchen Schalter es sich handelt – A oder B. Weiterhin ist eine optionale Möglichkeit vorgesehen, um mittels Bewegung der Maus innerhalb eines vorher definierten Bereiches den Schalter umzustellen. Um diese Möglichkeit zu nutzen, müssen in dieser Klasse alle Zeilen, die für das Reagieren auf Mausklick zuständig sind (Zeilen 03, 06, 12, 13 und 19) auskommentiert und in den Zeilen 02, 05, 10, 11 und 18 die Kommentarzeichen entfernt. Dann müssen in der Klasse PlayDice die Zeilen 253 und 257 auskommentiert werden und in den Zeilen 252 und 256 die Kommentarzeichen entfernt werden. Anschließend muss das Programm neu kompiliert werden, damit diese Funktionalität zur Verfügung steht. Dadurch wird die Klasse SwitchControl nun von der Klasse MouseMotionAdapter (java.awt.event.MouseMotionListener) abgeleitet. Analog zu den Klassen MouseAdapter und MouseListener stellt die Klasse MouseMotionAdapter (java.awt.event.MouseMotionAdapter) Implementierung der zu Klasse MouseMotionListener eine leere (java.awt.event. MouseMotionListener) und ermöglicht das Überschreiben von nur solcher Methoden, die auch tatsächlich verwendet werden. In diesem Fall handelt es sich um die Methode mouseMoved(). 01 02 03 04 05 06 import java.awt.event.MouseEvent; // import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter; // public class SwitchControl extends MouseMotionAdapter { public class SwitchControl extends MouseAdapter { 27 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private DiceModel dm; private SwitchView sv; private boolean lr; // public SwitchControl(SwitchView sView, // DiceModel dModel, boolean leftright) { public SwitchControl(SwitchView sView, DiceModel dModel, boolean leftright) { this.sv = sView; this.dm = dModel; this.lr = leftright; } // public void mouseMoved(MouseEvent me) { public void mouseClicked(MouseEvent me) { if ((sv.contains(me.getX(), me.getY())) && (dm.circle.contains(me.getX(), me.getY()))) { dm.setSwitch(me.getX(), me.getY(), lr); } } } Die Methode SwitchControl() baut eine Beziehung zum Modell (die Klasse DiceModel, Kapitel 4.2) und zuständigen View (die Klasse SwitchView, Kapitel 4.4) auf. 4.4. Das View, die Klasse SwitchView Das in der Abbildung 11 dargestellte Diagramm in UML-Notation gehört zu der View-Klasse SwitchView. Diese Klasse ist zuständig für die Darstellung der gewünschten Einstellung der Schalter A und B. Abbildung 11: View, die Klasse SwitchView Quelle: eigene Darstellung Die Klasse SwitchView wird von der Java-eigenen Klasse JPanel (javax.swing.JPanel) abgeleitet und implementiert das Interface Observer. Bei der Klasse JPanel handelt es sich um eine Art Container, der andere Komponenten aufnimmt, um diese graphisch abzubilden. 28 Das Interface Observer dient der Überwachung der Änderungen, welche infolge des Wechsels der Schalterstellung durch den Benutzer oder „automatisch“ entstehen. Diese Änderungen bekommt die Klasse als Wert übergeben und aktualisiert dementsprechend die graphische Ausgabe. Die Methode SwitchView stellt Verbindung zum Modell her und richtet das View mittels addObserver() als Observer des Modells ein. Die Methode update() ist für die Aktualisierung der Bildschirmdarstellung zuständig. Diese erfolgt durch den Aufruf der Methode setSignal(). Die notwendigen Daten werden vom Modell (durch Aufruf der Methode getSwitch()) abgerufen. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import javax.swing.JPanel; import java.awt.Graphics; import java.util.Observer; import java.util.Observable; public class SwitchView extends JPanel implements Observer { private static final long serialVersionUID = -7140745252271819121L; private DiceModel dm; private int signal; private double signalAngle; private boolean lr; public SwitchView(DiceModel dModel, boolean leftright) { this.dm = dModel; this.lr = leftright; dm.addObserver(this); signal = dm.startSwitch; // Default- Signal } public void paintComponent(Graphics g) { super.paintComponent(g); g.drawOval(dm.zX - dm.rR, dm.zY - dm.rR, 2 * dm.rR, 2 * dm.rR); g.drawString(dm.stringOne, dm.intXforOne, dm.intYforOne); g.drawString(dm.stringTwo, dm.intXforTwo, dm.intYforTwo); g.drawString(dm.stringTree, dm.intXforTree, dm.intYforTree); signalAngle = (2 * Math.PI * signal / 3) - (Math.PI / 2); g.drawLine(dm.zX, dm.zY, dm.zX + (int) ((dm.rR - 5) * Math.cos(signalAngle)), dm.zY + (int) ((dm.rR - 5) * Math.sin(signalAngle))); g.dispose(); } public void setSignal(int sig) { this.signal = sig; if (lr) { signal = dm.getSwitch(dm.LEFT); } else { signal = dm.getSwitch(dm.RIGHT); } 29 37 38 39 40 41 42 43 repaint(); } public void update(Observable obs, Object obj) { this.dm = (DiceModel) obs; setSignal(signal); } } Die Methode paintComponent() ist von JComponent (javax.swing.JComponent) abgeleitet und ist für das Zeichnen der Komponente zuständig. 4.5. Das View, die Klasse LightsView Das in der Abbildung 12 dargestellte Diagramm in UML-Notation gehört zu der View-Klasse LightsView. Diese Klasse wird von der Java-eigenen Klasse Canvas (java.awt.Canvas) abgeleitet. Sie implementiert das Interface Observer und ist für die Darstellung der Lichtsignale in den Detektoren A und B zuständig. Abbildung 12: View, die Klasse LightsView Quelle: eigene Darstellung „Die Klasse Canvas stellt einen einfachen Hintergrund dar, auf dem Bilder platziert oder grafische Ausgaben gemacht werden können.“34 Um das zu erreichen, wurde die Methode paint() überschrieben. Das Interface Observer dient der Überwachung der Änderungen. Die Methode LightsView stellt eine Verbindung zum Modell her und richtet das View als Observer (durch addObserver()) des Modells ein. Die Methode update() ist für die Aktualisierung der Bildschirmdarstellung zuständig. Diese erfolgt durch den Aufruf der Methode setSignal(). Die notwendigen Daten werden vom Modell durch Aufruf der Methode getLights() abgerufen. 34 Middendorf u.a., Stand: 17.08.2006 30 Die Animation der Lichter zeigte ausgeprägtes Flackern während der Ausführung einer Vielzahl der Experimente ohne Verzögerung. Dieses Flackern entsteht dadurch, dass vor dem Aufruf der paint()-Methode „zunächst das Fenster gelöscht wird und dadurch unmittelbar vor der Ausgabe des nächsten Bildes ganz kurz ein vollständig leerer Hintergrund erscheint.“35 Um dieses Flackern zu reduzieren wurde die zweite update()-Methode (Zeile 62) eingerichtet. Dadurch wurde erreicht, dass bei einem Aufruf von repaint() in der Methode setSignal(), nicht gleich paint(), sondern zunächst die Methode update() aufgerufen wird. Da die Methode update() durch eine eigene Version überschrieben wird, die den Hintergrund unverändert lässt, konnte das Flackern auf ein Minimum reduziert werden. Jedoch konnte das Flackern nicht vollständig eliminiert werden, daher wurde zusätzlich der Doppelpufferung-Mechanismus (Zeilen 20, 21) angewendet. „Beim Doppelpuffern wird Bildschirmausgabe bei in ein jedem Animationsschritt Offscreen-Image zunächst geschrieben. die Erst gesamte wenn alle Ausgabeoperationen abgeschlossen sind, wird dieses Offscreen-Image auf die Fensteroberfläche kopiert.“36 Durch Doppelpuffering wird erreicht, dass das Bild komplett neu aufgebaut ist, bevor es sichtbar wird. ... 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class LightsView extends Canvas implements Observer { private static final long serialVersionUID = -8511530608735372306L; private DiceModel dm; private int signal; private boolean lr; public LightsView(DiceModel dModel, boolean leftright) { this.dm = dModel; this.lr = leftright; dm.addObserver(this); signal = dm.startLights; // Default- Signal } public void paint(Graphics g) { BufferedImage theImage = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics g1 = theImage.getGraphics(); Color oldColor = g.getColor(); g1.setColor(this.getBackground()); g1.fillRect(0, 0, getWidth(), getHeight()); g1.setColor(oldColor); switch (signal) { 35 Krüger, 2003, S. 736 36 Krüger, 2003, S. 741 31 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 case 0: paintLight(g1, dm.ENABLED, dm.DISABLED); break; case 1: paintLight(g1, dm.DISABLED, dm.ENABLED); break; default: paintLight(g1, dm.DISABLED, dm.DISABLED); } g.drawImage(theImage, 0, 0, getWidth(), getHeight(), null); } private void paintLight(Graphics g, boolean red, boolean green) { g.drawRect(dm.zX - dm.bR, dm.zY - dm.hR, 2 * dm.bR, 2 * dm.hR); g.setColor(dm.defco); g.fillOval(dm.rX - dm.lR, dm.rY - dm.lR, 2 * dm.lR, 2 * dm.lR); g.setColor(dm.defco); g.fillOval(dm.gX - dm.lR, dm.gY - dm.lR, 2 * dm.lR, 2 * dm.lR); if (red) { g.setColor(dm.klick); g.fillOval(dm.rX - dm.lR, dm.rY – dm.lR, 2 * dm.lR, 2 * dm.lR); g.setColor(dm.defco); g.fillOval(dm.gX - dm.lR, dm.gY – dm.lR, 2 * dm.lR, 2 * dm.lR); } if (green) { g.setColor(dm.defco); g.fillOval(dm.rX - dm.lR, dm.rY – dm.lR, 2 * dm.lR, 2 * dm.lR); g.setColor(dm.klack); g.fillOval(dm.gX - dm.lR, dm.gY – dm.lR, 2 * dm.lR, 2 * dm.lR); } } public void setSignal(int sig) { this.signal = sig; signal = dm.getLights(lr); repaint(); } public void update(Graphics g){ paint(g); } public void update(Observable obs, Object obj) { this.dm = (DiceModel) obs; setSignal(signal); } } In Abhängigkeit von dem Signal, das von der Methode getLights() des Modells geliefert wird, wird in dem switch-case-Konstrukt festgelegt, welche Lichterkombinationen angezeigt werden sollen. Aus diesem switch-case-Konstrukt wird mit den entsprechenden Parametern die Methode paintLight() aufgerufen, die dann das Zeichnen übernimmt. In dieser Klasse wird (genau wie in der Klasse SwitchView, Kapitel 4.4) zwischen den Detektoren A und B unterschieden. 32 4.6. Der Controller, die Klasse AlphaControl Die Abbildung 33 zeigt das Diagramm der Controller-Klasse AlphaControl in UMLNotation. Diese Klasse ist für die Weitergabe des vom JSlider gelieferten Wertes für Alpha an das Modell zuständig. Abbildung 13: Controller, die Klasse AlphaControl Quelle: eigene Darstellung Die Klasse AlphaControl implementiert die Interfaces ChangeListener (javax.swing.event.ChangeListener) und Observer. Wenn der Wert von JSlider von dem Benutzer verändert wird, sendet JSlider ein ChangeEvent (javax.swing.event.ChangeEvent) aus. Um auf dieses Ereignis zu reagieren, wird das Interface ChangeListener implementiert. Dabei wird die dazugehörige Methode stateChanged() aufgerufen, wenn Änderungen vorliegen. Dadurch wird der ermittelte Wert über den Aufruf der setValueAlpha()-Methode an das Modell übergeben. Beim Implementieren der Funktionalität des „RESET“-Buttons ist aufgefallen, dass der JSlider auf das Zurücksetzen nicht reagierte. Um das Zurücksetzen des JSlider zu gewährleisten, wurde in dieser Klasse das Interface Observer implementiert. Mittels der update()-Methode wird der Wert vom Modell bezogen (in diesem Fall durch den Aufruf der Methode getValueAlpha()), konvertiert und gesetzt. 01 02 03 04 05 06 07 08 import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.util.Observable; import java.util.Observer; public class AlphaControl implements ChangeListener, Observer { private DiceModel dm; 33 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private int value; private JSlider sl; public AlphaControl(DiceModel dModel) { this.dm = dModel; dm.addObserver(this); } public void stateChanged(ChangeEvent ce) { sl = (JSlider) ce.getSource(); if (sl.getValueIsAdjusting()) { value = sl.getValue(); dm.setValueAlpha(value); } } public void update(Observable obs, Object arg) { this.dm = (DiceModel) obs; if (sl != null) { sl.setValue((int) (dm.getValueAlpha() * dm.alphaMax)); } } } Analog zu dieser Klasse sind auch die Klassen DelayControl und LoopsControl aufgebaut. Die Klasse DelayControl ist für die Weitergabe des vom JSlider dieser Klasse gelieferten Wertes für die Verzögerung der Ausführungsgeschwindigkeit an das Modell zuständig. Die Klasse LoopsControl übergibt den Wert für die Anzahl der Experimente an das Modell. 4.7. Das View, die Klasse AlphaTextView Folgendes Diagramm in UML-Notation gehört zu der Klasse AlphaTextView. Diese Klasse ist für die Darstellung der vom JSlider gelieferten Werte als Text zuständig. Abbildung 14: View, die Klasse AlphaTextView Quelle: eigene Darstellung Diese Klasse wird von der Java-eigenen Klasse JPanel (javax.swing.JPanel) abgeleitet und sie implementiert das Interface Observer. In der Beschreibung der Klasse SwitchView (Kapitel 4.4.) wurde bereits erläutert, worum es sich bei der 34 Klasse JPanel handelt und wozu das Interface Observer dient, daher wird darauf nicht mehr eingegangen. Die Ausgabe erfolgt als Text, dabei wird dieser vorher durch die Klasse DecimalFormat (java.text.DecimalFormat) als String gemäß der Vorlage (Zeile 12) formatiert. Die Formatierung ist notwendig, um der unschönen Ausgabe vorzubeugen, welche durch variable Anzahl an Vor- und bzw. oder Nachkommastellen bei den Werten entstehen können. Der anzuzeigende Wert wird durch den Aufruf von getValueAlpha() aus dem Modell bezogen. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import javax.swing.JLabel; import javax.swing.JPanel; import java.text.DecimalFormat; import java.util.Observer; import java.util.Observable; public class AlphaTextView extends JPanel implements Observer { private static final long serialVersionUID = -1744194831062161442L; private DiceModel dm; private JLabel output; private double text; private DecimalFormat df = new DecimalFormat("#0.0000"); public AlphaTextView(DiceModel dModel) { this.dm = dModel; text = dm.getValueAlpha(); dm.addObserver(this); output = new JLabel("Value of Alpha: " + df.format(text)); add(output); } public void setTextView(double text) { this.text = text; text = dm.getValueAlpha(); output.setText("Value of Alpha: " + df.format(text)); } public void update(Observable obs, Object v) { this.dm = (DiceModel) obs; setTextView(text); } } Die Klassen DelayTextView und LoopsTextView sind analog aufgebaut. Klasse DelayTextView gibt den Wert für die Verzögerung in Sekunden aus und die Klasse LoopsTextView gibt die von dem Benutzer eingestellte bzw. gewünschte Anzahl an Experimenten als Text graphisch aus. 35 4.8. Das View, die Klasse StatisticsView Die Abbildung 15 zeigt das Diagramm in UML-Notation der Klasse StatisticsView. Diese Klasse ist für die graphische Darstellung des prozentualen Wertes für Häufigkeit der gleichen Farbsignale in den Detektoren A und B zuständig. Abbildung 15: View, die Klasse StatisticsView Quelle: eigene Darstellung Die Klasse StatisticsView unterscheidet sich nicht wesentlich von der Klasse AlphaTextView (Kapitel 4.7.). Der einzige Unterschied besteht darin, dass die visuelle Ausgabe nicht als Text, sondern als Fortschrittsbalken in einer JProgressBar erfolgt. Dabei wird der darzustellende Wert durch den Aufruf der Methode getStatistics() aus dem Modell bezogen. ... 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class StatisticsView extends JPanel implements Observer { private static final long serialVersionUID = 4410445127875722664L; private DiceModel dm; private JProgressBar stat; private int value; public StatisticsView(JProgressBar jpbS, DiceModel dModel) { this.dm = dModel; this.stat = jpbS; dm.addObserver(this); value = dm.getStatistics(); stat.setMinimum(dm.statisticsMin); stat.setMaximum(dm.statisticsMax); this.add(stat); } public void setStat(int val) { this.value = val; value = dm.getStatistics(); stat.setValue(value); } public void update(Observable obs, Object obj) { this.dm = (DiceModel) obs; setStat(value); } } 36 4.9. Der Controller, die Klasse ButtonControl Das folgende Diagramm in UML-Notation zeigt die Klasse ButtonControl. Diese Klasse reagiert auf das Betätigen der Buttons durch den Benutzer. Abbildung 16: Controller, die Klasse ButtonControl Quelle: eigene Darstellung Durch das Betätigen eines Buttons durch den Benutzer wird die Klasse zum Empfänger von „Action-Events“ und implementiert das Interface ActionListener (java.awt.event.ActionListener). Dadurch bekommt die Klasse Ereignisse vom Typ ActionEvent übergeben. „ActionEvent erweitert die Klasse AWTEvent und stellt neben getID() und getSource() vor allem die Methode getActionCommand() zur Verfügung, mit der die verschiedenen Ereignisquellen unterschieden werden können. [...] Das Interface ActionListener stellt lediglich die Methode actionPerformed() zur Verfügung, die beim Aufruf ein ActionEvent übergeben bekommt.“37 Beim Betätigen des Buttons „START“ wird von dieser Klasse die Ausführung des Threads im Modell in Gang gesetzt. Da die Methode run() niemals von dem Programm direkt aufgerufen werden sollte, wird dazu in der Zeile 14 die Methode start() aufgerufen.38 ... 04 05 06 07 08 09 10 11 12 13 public class ButtonControl implements ActionListener { private DiceModel dm; private Thread th; public ButtonControl(DiceModel dModel) { this.dm = dModel; } public void actionPerformed(ActionEvent ae) { th = new Thread(dm); if (ae.getActionCommand() == dm.commandStart) { dm.pd.setBStart(); 37 Krüger, 2003, S. 632-633 38 Vgl. Krüger, 2003, S. 466 37 14 15 16 17 18 19 20 21 22 23 th.start(); } else if (ae.getActionCommand() == dm.commandStop) { dm.pd.setBStop(); dm.stopAll(); } else if (ae.getActionCommand() == dm.commandReset) { dm.pd.setBReset(); dm.resetAll(); } } } 4.10. Applet, die Klasse PlayDice Folgendes Diagramm in UML-Notation zeigt die Klasse PlayDice. Die Klasse PlayDice wird von der Klasse JApplet (javax.swing.JApplet) abgeleitet und ist für den Aufbau des Applets zuständig. Abbildung 17: Applet, die Klasse PlayDice Quelle: eigene Darstellung Die Klasse JApplet „erweitert java.applet.Applet um die Unterstützung der SwingArchitektur“39 und implementiert das RootPaneContainer-Interface. Dabei ist es erforderlich, dass alle Komponenten an die ContentPane übergeben werden.40 Die Klasse Applet (java.applet.Applet) ist „die Basis für alle Java-Anwendungen, die in HTML-Dokumente eingebunden werden sollen.“41 Die Abbildung 18 zeigt die Ableitungshierarchie dieser Klasse.42 39 40 41 42 Middendorf u.a., Stand: 17.08.2006 Vgl. Krüger, 2003, S. 775 Middendorf u.a., Stand: 17.08.2006 Vgl. Krüger, 2003, S. 775 38 Abbildung 18: Ableitungsbaum der Applet-Klasse Quelle: Krüger, 2003, S. 888 „Nach dem Laden wird das Applet zunächst instanziert und dann initialisiert. Anschließend wird es gestartet, erhält GUI-Events und wird irgendwann wieder gestoppt.“43 Zum Instanzieren wird der parameterlose Default-Konstruktor der Klasse aufgerufen. Anschließend wird vom Browser die init()-Methode (Zeile 118) aufgerufen und dadurch initialisiert.44 ... 117 118 // Initialize Applet public void init() { ... Die Klasse Container (java.awt.Container, Zeile 194) dient dazu, „Steuerelemente zu Fenstern oder Dialogen zusammenzufassen. Ein Exemplar von Container kann verschiedene Steuerelemente, beispielsweise Buttons und sogenannte Scrollbars.“45 Komponenten, Zunächst wird die enthalten, Methode getContentPane() aufgerufen, diese gibt einen Container zurück. Auf dem zurückgegebenen Container wird die Komponente per add() platziert (Zeilen 196198).46 ... 194 195 196 197 198 Container cp = getContentPane(); cp.setLayout(new BorderLayout()); cp.add(BorderLayout.NORTH, pInscription); cp.add(BorderLayout.CENTER, pLightsAndSwitches); cp.add(BorderLayout.SOUTH, pStatisticsAndAlphaBeta); ... Die Methoden setBStart(), setBStop() und setBReset() aktivieren und bzw. oder 43 44 45 46 Krüger, 2003, S. 889 Vgl. Krüger, 2003, S. 889 Middendorf u.a., Stand: 17.08.2006 Vgl. Krüger, 2003, S. 758 39 deaktivieren die einzelnen Buttons und Slider. ... 265 266 267 268 269 270 271 272 273 274 275 276 public void setBStart() { if (dModel.getValueLoops() == dModel.loopsLimit) { bStart.setEnabled(dModel.ENABLED); bStop.setEnabled(dModel.DISABLED); bReset.setEnabled(dModel.ENABLED); } else if (dModel.getValueLoops() > dModel.loopsLimit) { bStart.setEnabled(dModel.DISABLED); bStop.setEnabled(dModel.ENABLED); bReset.setEnabled(dModel.DISABLED); slLoops.setEnabled(dModel.DISABLED); } } ... 4.11. Die Klasse InstructionSet Das Diagramm in der Abbildung 19 zeigt die Klasse InstructionSet in UMLNotation. Die Klasse InstrictionSet definiert den Instruktionensatz. Abbildung 19: Die Klasse InstructionSet Quelle: eigene Darstellung Die einzelnen Instruktionen sind in einem zwei-dimensionalen Array abgespeichert. Jede Instruktion besteht aus einer Kodierung, die angibt, welche Farbe bei welcher Schalterstellung angezeigt werden soll. Dabei steht „R“ für rote Farbe, „G“ für Grüne und „D“ für Default (entspricht Weiß) -Farben. Beispielsweise die Kombination aus der Zeile 05: {„R“, „G“, „R“} bedeutet, dass bei der Schalterposition auf 1 - rot, 2 – grün und 3 – rot ausgeben wird. Die zwei apply()-Methoden in den Zeilen 14 und 18 ermöglichen das Abrufen der gewünschten Instruktion in entsprechend benötigter Form. 01 02 03 04 05 06 07 08 public class InstructionSet { protected String[][] colors = { {"R", "R", "R"}, // 1. Instruction (0) {"R", "R", "G"}, // 2. Instruction (1) {"R", "G", "R"}, // 3. Instruction (2) {"R", "G", "G"}, // 4. Instruction (3) {"G", "R", "R"}, // 5. Instruction (4) {"G", "R", "G"}, // 6. Instruction (5) 40 09 10 11 12 13 14 15 16 17 18 19 20 21 22 {"G", "G", "R"}, // 7. Instruction (6) {"G", "G", "G"}, // 8. Instruction (7) // Default for LIGHTS, only with RESET reachable {"D", "D", "D"} // 9. Instruction (8) }; protected Result apply(int instrNo, int switchL, int switchR) { return new Result(switchL, switchR, colors[instrNo][switchL], colors[instrNo][switchR]); } protected ResultSeparate apply(int instrNo, int switchSeparate) { return new ResultSeparate(switchSeparate, colors[instrNo][switchSeparate]); } } 4.12. Die Klasse Result Die Abbildung 20 zeigt das Diagramm der Klasse Result in UML-Notation. Mittels dieser Klasse wird festgestellt, bei welcher Schalterpostion an den Detektoren welche Farbe ausgegeben werden muss. Abbildung 20: Die Klasse Result.java Quelle: eigene Darstellung Der Aufruf der Methode Result() der gleichnamigen Klasse erfolgt von der Methode run() vom Modell aus. Dabei werden die Nummer der Instruktion und die Positionen der Schalter A und B übergeben. Zurückgeliefert wird die Information, zu welcher Schalterstellung von A und B, welche Farbe jeweils ausgegeben werden muss. 01 02 03 04 05 06 07 08 09 10 11 public class Result { private int switchL; // position of Switch LEFT private int switchR; // position of Switch RIGHT protected String colorL; // Color LEFT protected String colorR; // Color RIGHT protected Result(int switchL, int switchR, String colorL, String colorR) { this.switchL = switchL; this.switchR = switchR; this.colorL = colorL; this.colorR = colorR; 41 12 13 14 15 16 } public String toString() { return switchL + " " + switchR + " " + colorL + " " + colorR; } } Diese Klasse ist notwendig, um festzustellen, ob in den Detektoren A und B die gleiche Farbe ausgegeben wurde. Wenn die ausgegebenen Farben in den Detektoren A und B gleich sind, wird der Zähler countLights im Modell hochgezählt. 4.13. Die Klasse ResultSeparate In der Abbildung 21 ist das Diagramm der Klasse ResultSeparate in UML-Notation zu sehen. Diese Klasse zeigt an, welche Farbe bei welcher Schalterposition an einem Detektor ausgeben wird. Abbildung 21: Die Klasse ResultSeparate Quelle: eigene Darstellung Diese Klasse unterscheidet sich von der Klasse Result (Kapitel 4.12) darin, dass die Information nur für einen der beiden Detektoren – A oder B zurückgeliefert wird. Der Aufruf erfolgt ebenfalls vom Modell aus, allerdings von der Methode setLights(). Die Methode setLights() ihrerseits wird von der Methode run() zwei mal in Folge mit unterschiedlichen Parametern (getrennt für die Detektoren A und B) aufgerufen. Die zurückgelieferten Informationen werden dazu verwendet, um die Farben in den Detektoren zu setzen und graphisch auszugeben. 01 02 03 04 05 06 07 08 09 10 11 public class ResultSeparate { protected int switchSeparate; private String colorSeparate; protected ResultSeparate(int switchSeparate, String colorSeparate) { this.switchSeparate = switchSeparate; this.colorSeparate = colorSeparate; } public String toString() { return colorSeparate; } } 42 4.14. Die Klasse Simulation Die Abbildung 22 zeigt das Diagramm der Klasse Simulation in UML-Notation. Die Klasse Simulation simuliert das EPR-Experiment. Abbildung 22: Die Klasse Simulation Quelle: eigene Darstellung Hierbei wird an zwei Stellen die Klasse Random (java.util.Random) verwendet: um die Nummer der Instruktion auszuwürfeln und um die Position des Schalters A oder B zu ermitteln. „Die Klasse Random kann Folgen von Pseudo-Zufallszahlen erzeugen. Es können Zufallszahlen für alle Standarddatentypen angefordert werden.“47 Die Methode chooseInstruction() bekommt den vom Benutzer eingestellten Wert Alpha übergeben und führt in Abhängigkeit von diesem Wert und ausgewürfelter Instruktionsnummer die im Kapitel 2.3. beschrieben Berechnungen durch. Dabei wird die Nummer der Instruktion (0 bis 7) an das Modell zurückgeliefert. Die Methode chooseSwitch() wird nur bei mehr als einem Experiment von der run()- Methode des Modells aufgerufen. Hierbei wird die Position des Schalters 0, 1 oder 2 ausgewürfelt und zurückgeben, welche entsprechend der Schalterpositionen als 1, 2 oder 3 dem Benutzer angezeigt werden. Diese Methode wird zwei mal in Folge aufgerufen, um die Position der Schalter A und B separat zu ermitteln. 01 02 03 04 05 import java.util.Random; public class Simulation { private Random rdSwitchPs = new Random(); private Random rdInstrSet = new Random(); 47 Middendorf u.a., Stand: 17.08.2006 43 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 private int nbOfInstr; // should be attribute of InstructionSet private int nbOfSwPos; protected InstructionSet instrSet; protected Simulation(InstructionSet instrSet) { this.instrSet = instrSet; this.nbOfInstr = instrSet.colors.length - 1; // = 8 this.nbOfSwPos = instrSet.colors[0].length; // = 3 } protected int chooseInstruction(double alpha) { // Precondition: 0 < alpha < 1 double p = rdInstrSet.nextDouble(); double val; double beta = 1 - alpha; if (0 <= p && p < beta / 2) { val = 2 * p / beta; } else if (beta / 2 <= p && p < 1 - beta / 2) { val = 6 * (p - 0.5) / alpha + 4; } else { val = 2 * (p - 1) / beta + 8; } if (val >= nbOfInstr) { // not reachable System.err.println("Simulation.chooseInstruction(): ERROR!"); } return (int) Math.floor(val); } protected int chooseSwitch() { double p = rdSwitchPs.nextDouble(); return (int) Math.floor(p * nbOfSwPos); } } 44 5. Zusammenfassung Das Ziel dieser Arbeit bestand in der Entwicklung eines Programms zur Visualisierung des EPR-Experiments nach N. David Mermin. Dafür wurden zunächst die theoretischen Grundlagen erläutert. Dabei wurden die Beschreibung des quantenmechanische und mathematische Zusammenhänge erklärt. Die Grundlage für Programmentwicklung bildete die Experiments von Mermin. Daraus wurden auch die Anforderungen für Aufbau abgeleitet. Die Umsetzung erfolgte in Java (Version 5.0) als Applet nach dem MVC-Prinzip. Weiterhin wurde Swing verwendet, weil es eine Reihe an Vorteilen gegenüber Abstract Windowing Toolkit (AWT) wie z.B. die Möglichkeit komplexe graphische Oberflächen zu konstruieren oder Fähigkeit sich an die Fähigkeiten der jeweiligen Plattform anzupassen. Manche von den eingesetzten Komponenten erforderten den Einsatz von Java 2D-API, welche komplexe Grafikoperationen und Routinen zur Bildbearbeitung zur Verfügung stellt. Als Entwicklungsumgebung wurde Eclipse SDK 3.1.x verwendet. Zur Erstellung der UML-Diagramme wurde NetBeans (Enterprise Pack 5.5 Beta) herangezogen. Die vorgelegte Arbeit beinhaltet eine detaillierte Beschreibung der angewandten Klassen und Methoden. Zum besseren Verständnis wurden die Erklärungen durch Quellcode, Tabellen und Abbildungen ergänzt. Das Programm wurde während der Implementierung iterativ getestet. Dazu wurden vorübergehend Funktionen verwendet, die die Ausgabe auf die Konsole umgeleitet haben. Anhand der Anforderungsanalyse wurden Tests erstellt und durchgeführt. Dabei wurde die ist-Ausgabe mit der soll-Ausgabe verglichen. Bei einer festgestellten Abweichung wurde nach deren Ursache gesucht und diese beseitigt bzw. korrigiert. Danach wurde der Testlauf wiederholt. Der Vorgang wurde iterativ fortgesetzt, bis das gewünschte Ergebnis erreicht war. Diese Ausgabefunktionen wurden aus der fertigen Anwendung entfernt. Die durchgeführten Tests bescheinigten den korrekten und stabilen Betrieb der Anwendung, sowie die Erfüllung aller Anforderungen. 45 an das Programm gestelleten 6. Ausblick Das Erweiterungspotenzial des Programms ist bei weitem noch nicht ausgeschöpft. Zur weiteren Entwicklung ist es vorstellbar, das Programm um Funktionalität zur Speicherung der Ergebnisse der durchgeführten Experimente zu erweitern. Dabei könne die Ergebnisse beispielsweise in einer Tabelle abspeichert werden. Diese Funktion ermöglicht dem Anwender die so gewonnenen Daten eventuell mit einem Parser auszulesen und die Ergebnisse weiter zu verwerten. Als eine mögliche Verwendung der gewonnenen Ergebnisse ist die Verifikation anhand der mathematischen Berechnung vorstellbar, der zu Anfang der Arbeit erwähnten Wahrscheinlichkeiten. Weiterhin kann das Programm um eine Anzeige zur Darstellung des gerade ablaufenden Experimentes erweitert werden. Dadurch kann der Benutzer nicht nur erkennen, an welchem Experiment das Programm gerade arbeitet, sondern auch abschätzen, wie lange es noch dauert, bis es fertig ist. Weitere Möglichkeit ist die Restdauer als Zeit anzuzeigen. Da diese von einem Rechner zum anderen unterschiedlich ausfallen kann, ist dies mittels fester Umrechnung nicht realisierbar. Die Lösung kann so aussehen, dass das Programm die Dauer der Ausführung bestimmter (mindest-) Anzahl an Experimenten misst und daraus die Restdauer berechnet. Die Restadauer sollte während der Ausführung der Experimente laufend aktualisiert werden. Daraus kann auch eine etwas andere Modifikation abgeleitet werden – der Anwender stellt die gewünschte Dauer der Experimente in Minuten ein. Daraus berechnet das Programm selbst, wie viele Experimente es durchführen kann. Selbstverständlich muss auch hierbei sichergestellt werden, dass diese Anzahl während des Programmablaufs laufend aktualisiert wird. Möglicher Einsatzzweck des Programms mit derartiger Modifikation ist beispielsweise das Vorführen des Experimentes bei einer Präsentation oder Vorlesung. Denkbar ist auch die Ergänzung der Bedienungsfunktionalität um Bedienung mit der Tastatur, z.B. Slider per Focus-Umschaltung mit der Tastatur einzustellen. Dadurch kann höhere Präzision bei den Einstellungen erreicht werden. 46 Literaturverzeichnis Filk, 2004: Grundlagen und Probleme der Quantenmechanik. Online im Internet. URL: http://idefix.physik.uni-freiburg.de/~aufgabe/Skripte/Grundlagen.pdf Stand: 17.08.2006 FWF, 2004: Kurzbeschreibung des Projekts. Online im Internet. URL: http://www.fwf.ac.at/de/abstracts/abstract.asp?L=D&PROJ=L135 Stand: 17.08.2006 Götz, 2003: Das EPR-Paradoxon und die Bell'sche Ungleichung. Online im Internet. URL: http://www.ikg.rt.bw.schule.de/qphfbmat/epr.pdf Stand: 17.08.2006 Iliopoulos, 2003: Quantenkryptographie Teil 2. Online im Internet. URL: http://www.cip.physik.uni-muenchen.de/~iliopoul/Ausarbeitung2.pdf Stand: 17.08.2006 Jung, 2003: Einstein-Podolsky-Rosen-Experimente. Online im Internet. URL: http://www.bistr-o-mathik.org/resources/files/seminars/epr.pdf Stand: 17.08.2006 Krüger, 2003: Handbuch der Java-Programmierung. München Lehle, 1997: Visualisierung in der Relativitätstheorie.Online im Internet. URL: http://www.tat.physik.uni-tuebingen.de/~lehle/diss-97.pdf Stand: 17.08.2006 Meißner, 2002: Anforderungsanalyse. Online im Internet. URL: http://www.tfh-berlin.de/~lmeissne/WS02/ch03.pdf Stand: 17.08.2006 Meiler, o.J.: Einführung in die objektorientierte Programmierung (MVC). Online im Internet. URL: http://http://www.informatik.unileipzig.de/~meiler/Java.dir/JavaWS05.dir/Vorlesung/V13.pdfsung/V13.pdf Stand: 17.08.2006 Mermin, 1985: Is the moon there when nobody looks? Reality and the quantum theory. IN: PHYSICS TODAY. Online im Internet. URL: http://www.physics.iitm.ac.in/~arvind/ph350/mermin.pdf Stand: 17.08.2006 47 Middendorf u.a., 2002: JavaTM. Programmierhandbuch und Referenz für die JavaTM-2Plattform. Online im Internet. URL: http://www.dpunkt.de/java/ Stand: 17.08.2006 Rüdiger, 2004: Quantenkryptographie. Vortrag im Rahmen der Vortragsreihe Wissenschaft trifft Wirtschaft. Online im Internet. URL: http://public.fh-wolfenbuettel.de/~ruediger/talks/VortragQuCrypt.041130.pdf Stand: 17.08.2006 Tillemans, 2004: Beam mich zum anderen Ufer, Scotty! Wiener Physikern gelingt die Quantenteleportation von Photonen über die Donau. Online im Internet. URL: http://www.einsteingalerie.de/aktuell/quantenteleportation.html. Stand: 17.08.2006 Ullenboom, 2005.: Java ist auch eine Insel. Programmieren für die Java 2-Plattform in der Version 5. Online im Internet. URL: http://www.galileocomputing.de/openbook/javainsel5/javainsel01_000.htm Stand: 17.08.2006 Wallenborn, 1999: Herr Einstein, Herr Podolski und Herr Rosen. Online im Internet. URL: http://theory.gsi.de/~vanhees/faq-pdf/epr.pdf. Stand: 17.08.2006 Wikipedia: Anforderungsanalyse. Online im Internet. URL: http://de.wikipedia.org/wiki/Anforderungsanalyse Stand: 17.08.2006 Wikipedia: Applet. Online im Internet. URL: http://de.wikipedia.org/wiki/Applet. Stand: 17.08.2006 Zeilinger: Die Spukhaftigkeit der Realität. Online im Internet. URL: http://www.project-syndicate.org/commentary/zeilinger1/German Stand: 17.08.2006 48 Eidesstattliche Erklärung Ich erkläre hiermit an Eides Statt, dass ich die vorliegende Arbeit selbständig und ohne Benutzung anderer als der angegebenen Hilfsmittel angefertigt habe; die aus fremden Quellen direkt oder indirekt übernommenen Gedanken sind als solche kenntlich gemacht. Die Arbeit wurde bisher in gleicher oder ähnlicher Form keiner anderen Prüfungsbehörde vorgelegt und auch noch nicht veröffentlicht. __________________________ _______________________ Ort, Datum Unterschrift 49 Anhang 50 Quellcode AlphaControl.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.util.Observable; import java.util.Observer; public class AlphaControl implements ChangeListener, Observer { private DiceModel dm; private int value; private JSlider sl; public AlphaControl(DiceModel dModel) { this.dm = dModel; dm.addObserver(this); } public void stateChanged(ChangeEvent ce) { sl = (JSlider) ce.getSource(); if (sl.getValueIsAdjusting()) { value = sl.getValue(); dm.setValueAlpha(value); } } public void update(Observable obs, Object arg) { this.dm = (DiceModel) obs; if (sl != null) { sl.setValue((int) (dm.getValueAlpha() * dm.alphaMax)); } } } AlphaTextView.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 import javax.swing.JLabel; import javax.swing.JPanel; import java.text.DecimalFormat; import java.util.Observer; import java.util.Observable; public class AlphaTextView extends JPanel implements Observer { private static final long serialVersionUID = -1744194831062161442L; private DiceModel dm; private JLabel output; private double text; private DecimalFormat df = new DecimalFormat("#0.0000"); public AlphaTextView(DiceModel dModel) { this.dm = dModel; text = dm.getValueAlpha(); dm.addObserver(this); output = new JLabel("Value of Alpha: " + df.format(text)); add(output); } 51 20 21 22 23 24 25 26 27 28 29 public void setTextView(double text) { this.text = text; text = dm.getValueAlpha(); output.setText("Value of Alpha: " + df.format(text)); } public void update(Observable obs, Object v) { this.dm = (DiceModel) obs; setTextView(text); } } ButtonControl.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class ButtonControl implements ActionListener { private DiceModel dm; private Thread th; public ButtonControl(DiceModel dModel) { this.dm = dModel; } public void actionPerformed(ActionEvent ae) { th = new Thread(dm); if (ae.getActionCommand() == dm.commandStart) { dm.pd.setBStart(); th.start(); } else if (ae.getActionCommand() == dm.commandStop) { dm.pd.setBStop(); dm.stopAll(); } else if (ae.getActionCommand() == dm.commandReset) { dm.pd.setBReset(); dm.resetAll(); } } } Config.java 001 import java.awt.Color; 002 import java.awt.geom.Ellipse2D; 003 004 public interface Config { 005 // Rechts und Links unterscheiden 006 static final boolean LEFT = true; 007 // Rechts und Links unterscheiden 008 static final boolean RIGHT = false; 009 // Etwas zu aktivieren 010 static final boolean ENABLED = true; 011 // Etwas zu deaktivieren 012 static final boolean DISABLED = false; 013 // Berechnung von Abstaenden 014 static final int diffXY = 5; 015 // x- Koordinate der Panelmitte 016 static final int zX = 100; 52 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 046 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 // y- Koordinate der Panelmitte static final int zY = 100; // Radius eines Kreises static final int rR = 75; // Halbe Breite eines Rechtecks static final int bR = 50; // Halbe Hoehe eines Rechtecks static final int hR = 85; // Hoehe der Buttons static final int hB = 25; // Breite der Buttons static final int wB = 75; // x- Koordinate des Rotlichts static final int rX = 100; // y- Koordinate des Rotlichts static final int rY = 60; // x- Koordinate des Gruenlichts static final int gX = 100; // y- Koordinate des Gruenlichts static final int gY = 140; // Radius der Lichter static final int lR = 35; // Laenge des Zeigers static final double lP = 0.95; // Noch gueltiger Radius static final double rV = 1.20; // Anzahl der Bereiche in die der Kreis eingeteilt wurde static final int parts = 3; // Umfang des Kreises im Bogenmas static final double sphere = 2 * Math.PI; // Abtastbereich verschieben static final double shiftScan = 5 * Math.PI / 6; // Zeichenbereich verschieben static final double shiftDraw = Math.PI / 2; // Maximaler Wert fuer slLoops static final int loopsMin = 1; // Maximaler Wert fuer slLoops static final int loopsMax = 100000; // Maximaler Wert fuer slDelay static final int delayMin = 0; // Maximaler Wert fuer slDelay static final int delayMax = 2000; // Variable um Sekunden in double zu berechnen static final int delaySec = 1000; // Minimaler Wert fuer pbStatistics static final int statisticsMin = 0; // Maximaler Wert fuer pbStatistics static final int statisticsMax = 100; // Wert um Statistk zurueckzusetzen static final int resetStatCC = 0; // Wert um Statistk zurueckzusetzen static final int resetStatCL = 0; // Minimaler Wert fuer slAlphaBeta static final int alphaMin = 0; // Maximaler Wert fuer slAlphaBeta 53 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 static final int alphaMax = 1000; // Startwert fuer LOOPS static final int startLoops = 1; // GrenzWert fuer LOOPS static final int loopsLimit = 1; // Startwert fuer DELAY static final int startDelay = 1500; // Startwert fuer ALPHA static final int startAlpha = 500; // Startwert fuer SWITCH static final int startSwitch = 0; // Startwert fuer LIGHTS static final int startLights = 2; // Defaultwert fuer LIGHTS static final int defaultLights = 8; // Anzahl der Klicks UND Schleifen die ausgefuehrt wurden static final int countClicks = 0; // Anzahl der gleichen Lichter Links UND Rechts Gesamt static final int countLights = 0; // Circle static final Ellipse2D circle = new Ellipse2D.Double(zX - rR * rV, zY - rR * rV, 2 * rR * rV, 2 * rR * rV); // Color, RED static final Color klick = Color.RED; // Color, GREEN static final Color klack = Color.GREEN; // Color, WHITE, default static final Color defco = Color.WHITE; // Color, BLACK, rectangle static final Color rectc = Color.BLACK; // Beschriftung, die Zahl 1 static final String stringOne = "1"; // Beschriftung, die Zahl 2 static final String stringTwo = "2"; // Beschriftung, die Zahl 3 static final String stringTree = "3"; // Variable zur Berechnung des Kreises, Position fuer Zahl 1 static final int intOne = 0; // Variable zur Berechnung des Kreises, Position fuer Zahl 2 static final int intTwo = 1; // Variable zur Berechnung des Kreises, Position fuer Zahl 3 static final int intTree = 2; // Variable zur Berechnung des Kreises, Position fuer Zahl 1 static final double doubleOne = (2 * Math.PI * intOne / 3) - (Math.PI / 2); // Variable zur Berechnung des Kreises, Position fuer Zahl 2 static final double doubleTwo = (2 * Math.PI * intTwo / 3) - (Math.PI / 2); // Variable zur Berechnung des Kreises, Position fuer Zahl 3 static final double doubleTree = (2 * Math.PI * intTree / 3) - (Math.PI / 2); // Berechnung des Abstandes der Beschriftung vom Kreismittelpunkt static final int intValDia = 2 * rR; // Berechnung des Abstandes der Beschriftung vom Rand des Kreises static final int intValRad = rR / 5; 54 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 170 180 181 // X-Koordinate fuer die Beschriftung mit fer Zahl 1 static final int intXforOne = zX; // Y-Koordinate fuer die Beschriftung mit fer Zahl 1 static final int intYforOne = zY - (int) ((rR - intValDia diffXY) * Math.sin(doubleOne)); // X-Koordinate fuer die Beschriftung mit fer Zahl 2 static final int intXforTwo = zY - (int) ((rR - intValDia ((3 + (2 * diffXY)) * diffXY)) * Math.sin(doubleTwo)); // Y-Koordinate fuer die Beschriftung mit fer Zahl 2 static final int intYforTwo = zY + (int) ((rR + intValRad) * Math.sin(doubleTwo)); // X-Koordinate fuer die Beschriftung mit fer Zahl 3 static final int intXforTree = zX - (int) ((rR - intValDia (3 * diffXY)) * Math.cos(doubleTree)); // Y-Koordinate fuer die Beschriftung mit fer Zahl 3 static final int intYforTree = zY + (int) ((rR + intValRad) * Math.sin(doubleTree)); // Beschriftung des Butons START static final String stringStart = "START"; // Beschriftung des Butons STOP static final String stringStop = "STOP"; // Beschriftung des Butons RESET static final String stringReset = "RESET"; // ActionCommand des Butons START static final String commandStart = "Run"; // ActionCommand des Butons STOP static final String commandStop = "Hold"; // ActionCommand des Butons RESET static final String commandReset = "Res"; // Font for Inscription static final String fontFont = "SansSerif"; // Dimension of Font, Big static final int fontBig = 16; // Dimension of Font, Middle static final int fontMiddle = 14; // Dimension of Font, Small static final int fontSmall = 12; // ToolTip fuer den Button START static final String sToolTipStart = new String("Press the button to run this program"); // ToolTip fuer den Button STOP static final String sToolTipStop = new String("Press the button to stop this program"); // ToolTip fuer den Button RESET static final String sToolTipReset = new String("Press the button to reset this program"); // ToolTip fuer den Slider LOOPS static final String sToolTipLoops = new String("Move to change the number of experiments"); // ToolTip fuer den Slider DELAY static final String sToolTipDelay = new String("Move to change the value of delay"); // ToolTip fuer den Slider ALPHA static final String sToolTipAlpha = new String("Move to change the value of Alpha"); 55 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 } // ToolTip fuer Switches static final String sToolTipSwitches = new String ("Click on the area next to a number to change the pointer"); // ToolTip fuer das ProgressBar Statistics static final String sToolTipStatistics = new String ("Shows how often similar lights apperance on both sides"); // Label fuer die Bezeichnung static final String inscriptionTerm = new String("Animation of Mermin's EPR-Experiment"); // Label fuer die Quelle static final String inscriptionSource = new String("PHYSICS TODAY / APRIL 1985 PAG. 38-47"); // Label fuer den Urheber static final String inscriptionDeveloper = new String("(C) 2006 Heinrich Östreich, Roland Rüdiger"); // Label fuer LoopsTextView static final String sLoops = new String("Number of Experiments: "); // Label fuer DelayTextView static final String sDelay = new String("Delay: "); // Label fuer DelayTextView static final String sSecond = new String(" second"); // Label fuer AlphaTextView static final String sAlpha = new String("Value of Alpha: "); DelayControl.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.util.Observable; import java.util.Observer; public class DelayControl implements ChangeListener, Observer { private DiceModel dm; private int value; private JSlider sl; public DelayControl(DiceModel dModel) { this.dm = dModel; dm.addObserver(this); } public void stateChanged(ChangeEvent ce) { sl = (JSlider) ce.getSource(); if (sl.getValueIsAdjusting()) { value = sl.getValue(); dm.setValueDelay(value); } } public void update(Observable obs, Object arg) { this.dm = (DiceModel) obs; if (sl != null) { sl.setValue(dm.getValueDelay()); } } } 56 DelayTextView.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import javax.swing.JLabel; import javax.swing.JPanel; import java.text.DecimalFormat; import java.util.Observer; import java.util.Observable; public class DelayTextView extends JPanel implements Observer { private static final long serialVersionUID = -1365880714846853979L; private DiceModel dm; private JLabel output; private double text; private DecimalFormat df = new DecimalFormat("#0.000"); public DelayTextView(DiceModel dModel) { this.dm = dModel; text = (double) (dm.delayMax - dm.getValueDelay()) / dm.delaySec; dm.addObserver(this); output = new JLabel("Delay: " + df.format(text) + dm.sSecond); add(output); } public void setTextView(double text) { this.text = text; text = (double) (dm.delayMax - dm.getValueDelay()) / dm.delaySec; output.setText("Delay: " + df.format(text) + dm.sSecond); } public void update(Observable obs, Object v) { this.dm = (DiceModel) obs; setTextView(text); } } DiceModel.java 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 import java.awt.Color; import java.awt.Dimension; import java.awt.geom.Ellipse2D; import java.util.Observable; public class DiceModel extends Observable implements Runnable { // Damit man auf die Buttons in PlayDice.java zugreifen kann protected PlayDice pd; // Variable um Rechts und Links zu unterscheiden protected final boolean LEFT = Config.LEFT; // Variable um Rechts und Links zu unterscheiden protected final boolean RIGHT = Config.RIGHT; // Variable um etwas zu aktivieren protected final boolean ENABLED = Config.ENABLED; // Variable um etwas zu deaktivieren protected final boolean DISABLED = Config.DISABLED; // Color, RED protected final Color klick = Config.klick; // Color, GREEN protected final Color klack = Config.klack; // Color, WHITE, default 57 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 protected final Color defco = Config.defco; // Color, BLACK, rectangle protected final Color rectc = Config.rectc; // x- Kooridnate der Kreismitte protected final int zX = Config.zX; // y- Kooridnate der Kreismitte protected final int zY = Config.zY; // Radius eines Kreises protected final int rR = Config.rR; // Halbe Breite eines Rechtecks protected final int bR = Config.bR; // Halbe Hoehe eines Rechtecks protected final int hR = Config.hR; // Hoehe der Buttons protected final int hB = Config.hB; // Breite der Buttons protected final int wB = Config.wB; // x- Koordinate des Rotlichts protected final int rX = Config.rX; // y- Koordinate des Rotlichts protected final int rY = Config.rY; // x- Koordinate des Gruenlichts protected final int gX = Config.gX; // y- Koordinate des Gruenlichts protected final int gY = Config.gY; // Radius der Lichter protected final int lR = Config.lR; // Laenge des Zeigers protected static final double lP = Config.lP; // Noch gueltiger Radius protected static final double rV = Config.rR; // Abtastbereich verschieben protected static final double shiftS = Config.shiftScan; // Zeichenbereich verschieben protected static final double shiftD = Config.shiftDraw; // Minimaler Wert fuer slLoops protected final int loopsMin = Config.loopsMin; // Maximaler Wert fuer slLoops protected final int loopsMax = Config.loopsMax; // Minimaler Wert fuer slDelay protected final int delayMin = Config.delayMin; // Maximaler Wert fuer slDelay protected final int delayMax = Config.delayMax; // Variable um Sekunden in double zu berechnen protected final int delaySec = Config.delaySec; // Minimaler Wert fuer pbStatistics protected final int statisticsMin = Config.statisticsMin; // Maximaler Wert fuer pbStatistics protected final int statisticsMax = Config.statisticsMax; // Wert um Statistk zurueckzusetzen private final int resetStatCC = Config.resetStatCC; // Wert um Statistk zurueckzusetzen private final int resetStatCL = Config.resetStatCL; // Minimaler Wert fuer slAlphaBeta protected final int alphaMin = Config.alphaMin; 58 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 // Maximaler Wert fuer slAlphaBeta protected final int alphaMax = Config.alphaMax; // Circle protected final Ellipse2D circle = Config.circle; // Beschriftung, die Zahl 1 protected final String stringOne = Config.stringOne; // Beschriftung, die Zahl 2 protected final String stringTwo = Config.stringTwo; // Beschriftung, die Zahl 3 protected final String stringTree = Config.stringTree; // X-Koordinate fuer die Beschriftung mit fer Zahl 1 protected final int intXforOne = Config.intXforOne; // Y-Koordinate fuer die Beschriftung mit fer Zahl 1 protected final int intYforOne = Config.intYforOne; // X-Koordinate fuer die Beschriftung mit fer Zahl 2 protected final int intXforTwo = Config.intXforTwo; // Y-Koordinate fuer die Beschriftung mit fer Zahl 2 protected final int intYforTwo = Config.intYforTwo; // X-Koordinate fuer die Beschriftung mit fer Zahl 3 protected final int intXforTree = Config.intXforTree; // Y-Koordinate fuer die Beschriftung mit fer Zahl 3 protected final int intYforTree = Config.intYforTree; // Startwert fuer LOOPS private int valueLoops = Config.startLoops; // GrenzWert fuer LOOPS protected final int loopsLimit = Config.loopsLimit; // Startwert fuer DELAY private int valueDelay = Config.startDelay; // Startwert fuer ALPHA private int valueAlpha = Config.startAlpha; // Startwert fuer DELAY protected final int startDelay = Config.startDelay; // Startwert fuer LOOPS protected final int startLoops = Config.startLoops; // Startwert fuer ALPHA protected final int startAlpha = Config.startAlpha; // Startwert fuer ALPHA private double valAlpha = (double) valueAlpha / (double) alphaMax; // ** Inscription // Beschriftung des Butons START protected final String stringStart = Config.stringStart; // Beschriftung des Butons STOP protected final String stringStop = Config.stringStop; // Beschriftung des Butons RESET protected final String stringReset = Config.stringReset; // ActionCommand des Butons START protected final String commandStart = Config.commandStart; // ActionCommand des Butons STOP protected final String commandStop = Config.commandStop; // ActionCommand des Butons RESET protected final String commandReset = Config.commandReset; // Font for Inscription protected final String fontFont = Config.fontFont; // Dimension of Font, Big protected final int fontBig = Config.fontBig; 59 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 // Dimension of Font, Middle protected final int fontMiddle = Config.fontMiddle; // Dimension of Font, Small protected final int fontSmall = Config.fontSmall; // ToolTip fuer den Button START protected final String sToolTipStart = Config.sToolTipStart; // ToolTip fuer den Button STOP protected final String sToolTipStop = Config.sToolTipStop; // ToolTip fuer den Button RESET protected final String sToolTipReset = Config.sToolTipReset; // ToolTip fuer den Slider LOOPS protected final String sToolTipLoops = Config.sToolTipLoops; // ToolTip fuer den Slider DELAY protected final String sToolTipDelay = Config.sToolTipDelay; // ToolTip fuer den Slider ALPHA protected final String sToolTipAlpha = Config.sToolTipAlpha; // ToolTip fuer Switches protected final String sToolTipSwitches = Config.sToolTipSwitches; // ToolTip fuer das ProgressBar Statistics protected final String sToolTipStatistcs = Config.sToolTipStatistics; // Label fuer die Bezeichnung protected final String inscriptionTerm = Config.inscriptionTerm; // Label fuer die Quelle protected final String inscriptionSource = Config.inscriptionSource; // Label fuer den Urheber protected final String inscriptionDeveloper = Config.inscriptionDeveloper; // Label fuer LoopsTextView protected final String sLoops = Config.sLoops; // Label fuer DelayTextView protected final String sDelay = Config.sDelay; // Label fuer DelayTextView protected final String sSecond = Config.sSecond; // Label fuer AlphaTextView protected final String sAlpha = Config.sAlpha; // ** Variables in setSwitch() // Ankathete = Kosinus private static double adjacent; // Gegenkathete = Sinus private static double opposite; // Hypotenuse private static double hypotenuse; // Winkel unter dem der Mausklick stattfand private static double angleXY; // Umfang des Kreises im Bogenmas private static double sphere = Config.sphere; // Kosinus- Berechnung private static double angle; // Abtastbereich verschieben private static double shiftScan = 5 * Math.PI / 6; // Anzahl der Bereiche in die der Kreis eingeteilt wurde protected static final int parts = Config.parts; // ** Variables in setStatistics private int statisticsAverage = statisticsMin; // ** Variables in setLights() 60 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 // Startwert fuer SWITCH Left private int signalSwitchLeft = Config.startSwitch; // Startwert fuer SWITCH Right private int signalSwitchRight = Config.startSwitch; // Startwert fuer LIGHTS Left private int signalLightsLeft = Config.startLights; // Startwert fuer LIGHTS Right private int signalLightsRight = Config.startLights; // Startwert fuer SWITCH protected final int startSwitch = Config.startSwitch; // Startwert fuer LIGHTS protected final int startLights = Config.startLights; // Defaultwert fuer LIGHTS private static final int defaultLights = Config.defaultLights; // ** Variables for setStop // STOP, boolean private boolean stop; // ** Variables in run() // ALPHA private static double alpha; // LOOPS private static int loops; // Anzahl der Klicks UND Schleifen die ausgefuehrt wurden private int countClicks = Config.countClicks; // Anzahl der gleichen Lichter Links UND Rechts Gesamt private int countLights = Config.countLights; // ** Constructor protected DiceModel(PlayDice pd){ this.pd = pd; } final protected void stopAll() { setStop(true); notificationOfChange(); } final protected void resetAll() { setValueAlpha(startAlpha); setValueLoops(startLoops); setValueDelay(startDelay); setStatistics(resetStatCC, resetStatCL); setCountClicks(resetStatCC); setCountLights(resetStatCL); setLights(defaultLights, startSwitch, LEFT); setLights(defaultLights, startSwitch, RIGHT); notificationOfChange(); } final protected void setStop(boolean value) { this.stop = value; } final protected boolean getStop() { return stop; } final protected Dimension getPreferredSize() { return new Dimension(wB, hB); } final protected void setSwitch (int sourceX, 61 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 int sourceY, boolean lr) { adjacent = sourceX - zX; opposite = zY - sourceY; hypotenuse = Math.sqrt((adjacent * adjacent) + (opposite * opposite)); angle = Math.acos(adjacent / hypotenuse); if (opposite > 0) { angleXY = sphere - angle + shiftScan; } else { angleXY = angle + shiftScan; } if (lr == LEFT) { signalSwitchLeft = (int) ((angleXY / (sphere / parts)) % parts); } else if(lr == RIGHT) { signalSwitchRight = (int) ((angleXY / (sphere / parts)) % parts); } notificationOfChange(); } final protected int getSwitch(boolean lr) { if (lr == LEFT) { return signalSwitchLeft; } else if(lr == RIGHT) { return signalSwitchRight; } else { // exception // because of boolean is unreachable return 0; } } final protected void setValueAlpha (int valueAlpha) { this.valueAlpha = valueAlpha; valAlpha = (double) valueAlpha / (double) alphaMax; notificationOfChange(); } final protected double getValueAlpha() { return valAlpha; } final protected void setValueLoops(int valueLoops) { this.valueLoops = valueLoops; notificationOfChange(); } final protected int getValueLoops() { return valueLoops; } final protected void setValueDelay(int valueDelay) { this.valueDelay = valueDelay; notificationOfChange(); } final protected int getValueDelay() { return valueDelay; } final private void notificationOfChange() { setChanged(); 62 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 notifyObservers(); } final protected void setStatistics (int countClicks, int countLights) { if (countClicks != 0) { statisticsAverage = statisticsMax * countLights / countClicks; } else if (countClicks == 0) { // Ueber RESET erreichbar // weil setStatistics(0, 0); aufgerufen wird // notwendig um StatisticsView (JProgressBar) // auf 0 zurueckzusetzen statisticsAverage = statisticsMin; } notificationOfChange(); } final protected int getStatistics() { return statisticsAverage; } final protected void setLights(int instrNumber, int signalSwitch, boolean lr) { int lightsValue; if(lr == LEFT) { this.signalSwitchLeft = signalSwitch; } else if(lr == RIGHT) { this.signalSwitchRight = signalSwitch; } else { // not reachable // because of boolean is unreachable } InstructionSet instrSet = new InstructionSet(); ResultSeparate result = instrSet.apply (instrNumber, signalSwitch); // deterministic if (result.toString() == "R") { // Light, RED lightsValue = 0; } else if (result.toString() == "G" ) { // Light, GREEN lightsValue = 1; } else if (result.toString() == "D" ) { // Light, DEFAULT // only with RESET reachable lightsValue = 2; } else { // Light, DEFAULT lightsValue = 2; } if (lr == LEFT) { this.signalLightsLeft = lightsValue; } else if (lr == RIGHT) { this.signalLightsRight = lightsValue; } else { // not reachable } notificationOfChange(); } final protected int getLights (boolean lr) { if(lr == LEFT) { return signalLightsLeft; 63 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 } else if(lr == RIGHT) { return signalLightsRight; } else { // exeption // because of boolean is unreachable return 0; } } final public void run() { InstructionSet instrSet = new InstructionSet(); Simulation sim = new Simulation(instrSet); Result result; alpha = getValueAlpha(); loops = getValueLoops(); stop = false; int switchL = 0; int switchR = 0; countClicks = getCountClicks(); countLights = getCountLights(); for (int k = 0; (k < loops) && (!stop); k++) { try { Thread.sleep(delayMax - getValueDelay()); } catch (InterruptedException ie) { ie.printStackTrace(); } int instrNo = sim.chooseInstruction(alpha); if (loops == loopsLimit) { // Loops == 1 switchL = getSwitch(LEFT); switchR = getSwitch(RIGHT); } else if (loops > loopsLimit) { // Loops > 1 switchL = sim.chooseSwitch(); switchR = sim.chooseSwitch(); } else { // Loops == null or negative // not reachable System.err.println("DiceModel.run(): ERROR!"); } result = instrSet.apply(instrNo, switchL, switchR); // deterministic setLights(instrNo, switchL, LEFT); setLights(instrNo, switchR, RIGHT); countClicks++; if (result.colorL == result.colorR) { countLights++; } setStatistics(countClicks, countLights); } stop = false; pd.setBStop(); } public int getCountClicks() { return countClicks; } public void setCountClicks(int countClicks) { this.countClicks = countClicks; notificationOfChange(); 64 407 408 409 410 411 412 413 414 415 } } public int getCountLights() { return countLights; } public void setCountLights(int countLights) { this.countLights = countLights; notificationOfChange(); } InstructionSet.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 public class InstructionSet { protected String[][] colors = { {"R", "R", "R"}, // 1. Instruction (0) {"R", "R", "G"}, // 2. Instruction (1) {"R", "G", "R"}, // 3. Instruction (2) {"R", "G", "G"}, // 4. Instruction (3) {"G", "R", "R"}, // 5. Instruction (4) {"G", "R", "G"}, // 6. Instruction (5) {"G", "G", "R"}, // 7. Instruction (6) {"G", "G", "G"}, // 8. Instruction (7) // Default for LIGHTS, only with RESET reachable {"D", "D", "D"} // 9. Instruction (8) }; protected Result apply(int instrNo, int switchL, int switchR) { return new Result(switchL, switchR, colors[instrNo][switchL], colors[instrNo][switchR]); } protected ResultSeparate apply(int instrNo, int switchSeparate) { return new ResultSeparate(switchSeparate, colors[instrNo][switchSeparate]); } } LightsView.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 import java.awt.Canvas; import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.util.Observable; import java.util.Observer; public class LightsView extends Canvas implements Observer { private static final long serialVersionUID = -8511530608735372306L; private DiceModel dm; private int signal; private boolean lr; public LightsView(DiceModel dModel, boolean leftright) { this.dm = dModel; this.lr = leftright; dm.addObserver(this); signal = dm.startLights; // Default- Signal } 65 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 public void paint(Graphics g) { BufferedImage theImage = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics g1 = theImage.getGraphics(); Color oldColor = g.getColor(); g1.setColor(this.getBackground()); g1.fillRect(0, 0, getWidth(), getHeight()); g1.setColor(oldColor); switch (signal) { case 0: paintLight(g1, dm.ENABLED, dm.DISABLED); break; case 1: paintLight(g1, dm.DISABLED, dm.ENABLED); break; default: paintLight(g1, dm.DISABLED, dm.DISABLED); } g.drawImage(theImage, 0, 0, getWidth(), getHeight(), null); } private void paintLight(Graphics g, boolean red, boolean green) { g.drawRect(dm.zX - dm.bR, dm.zY - dm.hR, 2 * dm.bR, 2 * dm.hR); g.setColor(dm.defco); g.fillOval(dm.rX - dm.lR, dm.rY - dm.lR, 2 * dm.lR, 2 * dm.lR); g.setColor(dm.defco); g.fillOval(dm.gX - dm.lR, dm.gY - dm.lR, 2 * dm.lR, 2 * dm.lR); if (red) { g.setColor(dm.klick); g.fillOval(dm.rX - dm.lR, dm.rY – dm.lR, 2 * dm.lR, 2 * dm.lR); g.setColor(dm.defco); g.fillOval(dm.gX - dm.lR, dm.gY – dm.lR, 2 * dm.lR, 2 * dm.lR); } if (green) { g.setColor(dm.defco); g.fillOval(dm.rX - dm.lR, dm.rY – dm.lR, 2 * dm.lR, 2 * dm.lR); g.setColor(dm.klack); g.fillOval(dm.gX - dm.lR, dm.gY – dm.lR, 2 * dm.lR, 2 * dm.lR); } } public void setSignal(int sig) { this.signal = sig; signal = dm.getLights(lr); repaint(); } public void update(Graphics g){ paint(g); } public void update(Observable obs, Object obj) { this.dm = (DiceModel) obs; setSignal(signal); } } 66 LoopsControl.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 23 24 25 26 27 import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.util.Observable; import java.util.Observer; public class LoopsControl implements ChangeListener, Observer { private DiceModel dm; private int value; private JSlider sl; public LoopsControl(DiceModel dModel) { this.dm = dModel; dm.addObserver(this); } public void stateChanged(ChangeEvent ce) { sl = (JSlider) ce.getSource(); if (sl.getValueIsAdjusting()) { value = sl.getValue(); dm.setValueLoops(value); } } public void update(Observable obs, Object arg) { this.dm = (DiceModel) obs; if (sl != null) { sl.setValue(dm.getValueLoops()); } } } LoopsTextView.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 import javax.swing.JLabel; import javax.swing.JPanel; import java.text.DecimalFormat; import java.util.Observer; import java.util.Observable; public class LoopsTextView extends JPanel implements Observer { private static final long serialVersionUID = 6295443550098419757L; private DiceModel dm; private JLabel output; private int text; private DecimalFormat df = new DecimalFormat("#000000"); public LoopsTextView(DiceModel dModel) { this.dm = dModel; text = dm.startLoops; dm.addObserver(this); output = new JLabel(dm.sLoops + df.format(text)); add(output); } public void setTextView(int text) { this.text = text; text = dm.getValueLoops(); 67 23 24 25 26 27 28 29 output.setText(dm.sLoops + df.format(text)); } public void update(Observable obs, Object v) { this.dm = (DiceModel) obs; setTextView(text); } } PlayDice.java 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 import java.awt.BorderLayout; import java.awt.Container; import java.awt.Font; import java.awt.GridLayout; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JSlider; public class PlayDice extends JApplet { // von Eclipse IDE generiert private static final long serialVersionUID = -4543724741710966619L; // DiceModel private DiceModel dModel; // Panel fuer Beschriftung private JPanel pInscription; // Panel fuer alle Lights, Switches und Buttons private JPanel pLightsAndSwitches; // Panel fuer StatisticsView und AlphaBetaControl private JPanel pStatisticsAndAlphaBeta; // Panel fuer Beschriftungen private JPanel pInscriptions; // Panel fier die Bezeichnung private JPanel pInscriptionTerm; // Panel fuer die Quelle private JPanel pInscriptionSource; // Panel fuer den Urheber private JPanel pInscriptionDeveloper; // Font fuer die Bezeichnung private Font fontTerm; // Font fuer die Quelle private Font fontSource; // Font fuer den Urheber private Font fontDeveloper; // Label fuer die Bezeichnung private JLabel lInscriptionsTerm; // Label fuer die Quelle private JLabel lInscriptionsSource; // Label fuer dden Urheber private JLabel lInscriptionsDeveloper; // Panel fuer alle Lights und Buttons private JPanel pLightsAndButtons; // Panel fuer alle Switches und Leeres Panel 68 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 private JPanel pSwitchesAndEmpty; // Panel fuer alle Buttons private JPanel pButtonsPanel; // Panel fuer den START- Button- Panel private JPanel pStartButtonPanel; // Panel fuer den STOP- Button- Panel private JPanel pStopButtonPanel; // Panel fuer den RESET- Button- Panel private JPanel pResetButtonPanel; // Panel fuer den START-Button private JPanel pStartButton; // Panel fuer den STOP-Button private JPanel pStopButton; // Panel fuer den RESET-Button private JPanel pResetButton; // Panel fuer Slider LOOPS und SPEED private JPanel pLoopsAndSpeed; // Panel fuer Leeres Panel private JPanel pEmptyViewLS; // Panel fuer Leeres Panel private JPanel pEmptyViewStABL; // Panel fuer Leeres Panel private JPanel pEmptyViewStABR; // Panel fuer StatisticsView und AlphaBetaControl private JPanel pStatisticsAlphaBeta; // Button START private JButton bStart; // Button STOP private JButton bStop; // Button RESET private JButton bReset; // Slider um die Anzahl von Schleifen einzustellen private JSlider slLoops; // Slider um die Ausfuehrgeschwindigkeit zu regeln private JSlider slDelay; // Slider fuer AlphaControl private JSlider slAlpha; // ProgressBar fuer StatisticsView private JProgressBar pbStatistics; // LightsView Links private LightsView lightsViewLeft; // LightsView Rechts private LightsView lightsViewRight; // ButtonControl, START private ButtonControl startControl; // ButtonControl, STOP private ButtonControl stopControl; // ButtonContral, RESET private ButtonControl resetControl; // SwitchView Links private SwitchView switchViewLeft; // SwitchView Rechts private SwitchView switchViewRight; // Statistics- View private StatisticsView statisticsView; 69 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 // ALPHA- Text- View private AlphaTextView alphaTextView; // LOOPS- Text- View private LoopsTextView loopsTextView; // SPEED- Text- View private DelayTextView delayTextView; // LOOPS private LoopsControl loopsControl; // DELAY private DelayControl delayControl; // ALPHA private AlphaControl alphaControl; // SwitchControl, LEFT private SwitchControl switchControlLeft; // SwitchControl, RIGHT private SwitchControl switchControlRight; // Initialize Applet public void init() { dModel = new DiceModel(this); pInscription = new JPanel(); pLightsAndSwitches = new JPanel(); pStatisticsAndAlphaBeta = new JPanel(); pInscriptions = new JPanel(); pInscriptionTerm = new JPanel(); pInscriptionSource = new JPanel(); pInscriptionDeveloper = new JPanel(); fontTerm = new Font(dModel.fontFont, Font.BOLD, dModel.fontBig); fontSource = new Font(dModel.fontFont, Font.BOLD, dModel.fontMiddle); fontDeveloper = new Font(dModel.fontFont, Font.BOLD, dModel.fontSmall); lInscriptionsTerm = new JLabel(dModel.inscriptionTerm); lInscriptionsTerm.setFont(fontTerm); lInscriptionsSource = new JLabel(dModel.inscriptionSource); lInscriptionsSource.setFont(fontSource); lInscriptionsDeveloper = new JLabel(dModel.inscriptionDeveloper); lInscriptionsDeveloper.setFont(fontDeveloper); pLightsAndButtons = new JPanel(); pSwitchesAndEmpty = new JPanel(); pStatisticsAlphaBeta = new JPanel(); pButtonsPanel = new JPanel(); pStartButtonPanel = new JPanel(); pStopButtonPanel = new JPanel(); pResetButtonPanel = new JPanel(); pStartButton = new JPanel(); pStopButton = new JPanel(); pResetButton = new JPanel(); pLoopsAndSpeed = new JPanel(); pEmptyViewLS = new JPanel(); pEmptyViewStABL = new JPanel(); pEmptyViewStABR = new JPanel(); bStart = new JButton(dModel.stringStart); bStart.setPreferredSize(dModel.getPreferredSize()); bStart.setActionCommand(dModel.commandStart); 70 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 bStart.setEnabled(dModel.ENABLED); bStart.setToolTipText(dModel.sToolTipStart); bStop = new JButton(dModel.stringStop); bStop.setPreferredSize(dModel.getPreferredSize()); bStop.setActionCommand(dModel.commandStop); bStop.setEnabled(dModel.DISABLED); bStop.setToolTipText(dModel.sToolTipStop); bReset = new JButton(dModel.stringReset); bReset.setPreferredSize(dModel.getPreferredSize()); bReset.setActionCommand(dModel.commandReset); bReset.setEnabled(dModel.ENABLED); bReset.setToolTipText(dModel.sToolTipReset); slLoops = new JSlider(JSlider.HORIZONTAL, dModel.loopsMin, dModel.loopsMax, dModel.startLoops); slLoops.setToolTipText(dModel.sToolTipLoops); slDelay = new JSlider(JSlider.HORIZONTAL, dModel.delayMin, dModel.delayMax, dModel.startDelay); slDelay.setToolTipText(dModel.sToolTipDelay); pbStatistics = new JProgressBar(); pbStatistics.setStringPainted(dModel.ENABLED); pbStatistics.setToolTipText(dModel.sToolTipStatistcs); slAlpha = new JSlider(JSlider.HORIZONTAL, dModel.alphaMin, dModel.alphaMax, dModel.startAlpha); slAlpha.setToolTipText(dModel.sToolTipAlpha); lightsViewLeft = new LightsView(dModel, dModel.LEFT); lightsViewRight = new LightsView(dModel, dModel.RIGHT); startControl = new ButtonControl(dModel); stopControl = new ButtonControl(dModel); resetControl = new ButtonControl(dModel); switchViewLeft = new SwitchView(dModel, dModel.LEFT); switchViewRight = new SwitchView(dModel, dModel.RIGHT); statisticsView = new StatisticsView(pbStatistics, dModel); alphaTextView = new AlphaTextView(dModel); loopsTextView = new LoopsTextView(dModel); delayTextView = new DelayTextView(dModel); alphaControl = new AlphaControl(dModel); loopsControl = new LoopsControl(dModel); delayControl = new DelayControl(dModel); Container cp = getContentPane(); cp.setLayout(new BorderLayout()); cp.add(BorderLayout.NORTH, pInscription); cp.add(BorderLayout.CENTER, pLightsAndSwitches); cp.add(BorderLayout.SOUTH, pStatisticsAndAlphaBeta); pInscription.add(pInscriptions); pInscriptions.setLayout(new GridLayout(3, 1)); pInscriptions.add(pInscriptionTerm); pInscriptionTerm.add(lInscriptionsTerm); pInscriptions.add(pInscriptionSource); pInscriptionSource.add(lInscriptionsSource); pInscriptions.add(pInscriptionDeveloper); pInscriptionDeveloper.add(lInscriptionsDeveloper); pLightsAndSwitches.setLayout(new GridLayout(2, 1)); pLightsAndSwitches.add(pLightsAndButtons); pLightsAndSwitches.add(pSwitchesAndEmpty); pLightsAndButtons.setLayout(new GridLayout(1, 3)); 71 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 pLightsAndButtons.add(lightsViewLeft); // LightsView, LEFT pLightsAndButtons.add(pButtonsPanel); pLightsAndButtons.add(lightsViewRight); // LightsView, RIGHT pButtonsPanel.setLayout(new GridLayout(3, 1)); pButtonsPanel.add(pStartButtonPanel); pStartButtonPanel.setLayout(new BorderLayout()); pStartButtonPanel.add(BorderLayout.SOUTH, pStartButton); pStartButton.add(bStart); pButtonsPanel.add(pStopButtonPanel); pStopButtonPanel.setLayout(new BorderLayout()); pStopButtonPanel.add(BorderLayout.SOUTH, pStopButton); pStopButton.add(bStop); pButtonsPanel.add(pResetButtonPanel); pResetButtonPanel.setLayout(new BorderLayout()); pResetButtonPanel.add(BorderLayout.NORTH, pResetButton); pResetButton.add(bReset); pSwitchesAndEmpty.setLayout(new GridLayout(1, 3)); pSwitchesAndEmpty.add(switchViewLeft); pSwitchesAndEmpty.add(pLoopsAndSpeed); pSwitchesAndEmpty.add(switchViewRight); pLoopsAndSpeed.setLayout(new GridLayout(7, 1)); pLoopsAndSpeed.add(loopsTextView); pLoopsAndSpeed.add(slLoops); pLoopsAndSpeed.add(pEmptyViewLS); pLoopsAndSpeed.add(delayTextView); pLoopsAndSpeed.add(slDelay); pLoopsAndSpeed.add(pEmptyViewLS); pLoopsAndSpeed.add(pEmptyViewLS); pStatisticsAndAlphaBeta.setLayout(new GridLayout(1, 3)); pStatisticsAndAlphaBeta.add(pEmptyViewStABL); pStatisticsAndAlphaBeta.add(pStatisticsAlphaBeta); pStatisticsAlphaBeta.setLayout(new GridLayout(3, 1)); pStatisticsAlphaBeta.add(statisticsView); pStatisticsAlphaBeta.add(alphaTextView); pStatisticsAlphaBeta.add(slAlpha); pStatisticsAndAlphaBeta.add(pEmptyViewStABR); bStart.addActionListener(startControl); bStop.addActionListener(stopControl); bReset.addActionListener(resetControl); switchControlLeft = new SwitchControl(switchViewLeft, dModel, dModel.LEFT); // switchViewLeft.addMouseMotionListener(switchControlLeft); switchViewLeft.addMouseListener(switchControlLeft); switchControlRight = new SwitchControl(switchViewRight, dModel, dModel.RIGHT); // switchViewRight.addMouseMotionListener(switchControlRight); switchViewRight.addMouseListener(switchControlRight); slLoops.addChangeListener(loopsControl); slDelay.addChangeListener(delayControl); slAlpha.addChangeListener(alphaControl); } public JButton getBStart() { return bStart; } public void setBStart() { 72 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 } if (dModel.getValueLoops() == dModel.loopsLimit) { bStart.setEnabled(dModel.ENABLED); bStop.setEnabled(dModel.DISABLED); bReset.setEnabled(dModel.ENABLED); } else if (dModel.getValueLoops() > dModel.loopsLimit) { bStart.setEnabled(dModel.DISABLED); bStop.setEnabled(dModel.ENABLED); bReset.setEnabled(dModel.DISABLED); slLoops.setEnabled(dModel.DISABLED); } } public JButton getBStop() { return bStop; } public void setBStop() { bStart.setEnabled(dModel.ENABLED); bStop.setEnabled(dModel.DISABLED); bReset.setEnabled(dModel.ENABLED); slLoops.setEnabled(dModel.ENABLED); } public JButton getBReset() { return bReset; } public void setBReset() { bStart.setEnabled(dModel.ENABLED); bStop.setEnabled(dModel.DISABLED); bReset.setEnabled(dModel.ENABLED); } Result.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 public class Result { private int switchL; // position of Switch LEFT private int switchR; // position of Switch RIGHT protected String colorL; // Color LEFT protected String colorR; // Color RIGHT protected Result(int switchL, int switchR, String colorL, String colorR) { this.switchL = switchL; this.switchR = switchR; this.colorL = colorL; this.colorR = colorR; } public String toString() { return switchL + " " + switchR + " " + colorL + " " + colorR; } } ResultSeparate.java 01 02 03 04 public class ResultSeparate { protected int switchSeparate; private String colorSeparate; protected ResultSeparate(int switchSeparate, String colorSeparate) { 73 05 06 07 08 09 10 11 this.switchSeparate = switchSeparate; this.colorSeparate = colorSeparate; } public String toString() { return colorSeparate; } } Simulation.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import java.util.Random; public class Simulation { private Random rdSwitchPs = new Random(); private Random rdInstrSet = new Random(); private int nbOfInstr; // should be attribute of InstructionSet private int nbOfSwPos; protected InstructionSet instrSet; protected Simulation(InstructionSet instrSet) { this.instrSet = instrSet; this.nbOfInstr = instrSet.colors.length - 1; // = 8 this.nbOfSwPos = instrSet.colors[0].length; // = 3 } protected int chooseInstruction(double alpha) { // Precondition: 0 < alpha < 1 double p = rdInstrSet.nextDouble(); double val; double beta = 1 - alpha; if (0 <= p && p < beta / 2) { val = 2 * p / beta; } else if (beta / 2 <= p && p < 1 - beta / 2) { val = 6 * (p - 0.5) / alpha + 4; } else { val = 2 * (p - 1) / beta + 8; } if (val >= nbOfInstr) { // not reachable System.err.println("Simulation.chooseInstruction(): ERROR!"); } return (int) Math.floor(val); } protected int chooseSwitch() { double p = rdSwitchPs.nextDouble(); return (int) Math.floor(p * nbOfSwPos); } } StatisticsView.java 01 02 03 04 05 06 import javax.swing.JPanel; import javax.swing.JProgressBar; import java.util.Observer; import java.util.Observable; public class StatisticsView extends JPanel implements Observer { 74 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private static final long serialVersionUID = 4410445127875722664L; private DiceModel dm; private JProgressBar stat; private int value; public StatisticsView(JProgressBar jpbS, DiceModel dModel) { this.dm = dModel; this.stat = jpbS; dm.addObserver(this); value = dm.getStatistics(); stat.setMinimum(dm.statisticsMin); stat.setMaximum(dm.statisticsMax); this.add(stat); } public void setStat(int val) { this.value = val; value = dm.getStatistics(); stat.setValue(value); } public void update(Observable obs, Object obj) { this.dm = (DiceModel) obs; setStat(value); } } SwitchControl.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import java.awt.event.MouseEvent; // import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter; // public class SwitchControl extends MouseMotionAdapter { public class SwitchControl extends MouseAdapter { private DiceModel dm; private SwitchView sv; private boolean lr; // public SwitchControl(SwitchView sView, // DiceModel dModel, boolean leftright) { public SwitchControl(SwitchView sView, DiceModel dModel, boolean leftright) { this.sv = sView; this.dm = dModel; this.lr = leftright; } // public void mouseMoved(MouseEvent me) { public void mouseClicked(MouseEvent me) { if ((sv.contains(me.getX(), me.getY())) && (dm.circle.contains(me.getX(), me.getY()))) { dm.setSwitch(me.getX(), me.getY(), lr); } } } 75 SwitchView.java 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import javax.swing.JPanel; import java.awt.Graphics; import java.util.Observer; import java.util.Observable; public class SwitchView extends JPanel implements Observer { private static final long serialVersionUID = -7140745252271819121L; private DiceModel dm; private int signal; private double signalAngle; private boolean lr; public SwitchView(DiceModel dModel, boolean leftright) { this.dm = dModel; this.lr = leftright; dm.addObserver(this); signal = dm.startSwitch; // Default- Signal } public void paintComponent(Graphics g) { super.paintComponent(g); g.drawOval(dm.zX - dm.rR, dm.zY - dm.rR, 2 * dm.rR, 2 * dm.rR); g.drawString(dm.stringOne, dm.intXforOne, dm.intYforOne); g.drawString(dm.stringTwo, dm.intXforTwo, dm.intYforTwo); g.drawString(dm.stringTree, dm.intXforTree, dm.intYforTree); signalAngle = (2 * Math.PI * signal / 3) - (Math.PI / 2); g.drawLine(dm.zX, dm.zY, dm.zX + (int) ((dm.rR - 5) * Math.cos(signalAngle)), dm.zY + (int) ((dm.rR - 5) * Math.sin(signalAngle))); g.dispose(); } public void setSignal(int sig) { this.signal = sig; if (lr) { signal = dm.getSwitch(dm.LEFT); } else { signal = dm.getSwitch(dm.RIGHT); } repaint(); } public void update(Observable obs, Object obj) { this.dm = (DiceModel) obs; setSignal(signal); } } 76 UML-Diagramm Abbildung 23: UML-Diagramm der Anwendung Quelle: eigene Darstellung 77