Zur praktischen Anwendung der vermittelten Kenntnisse wird in diesem Teil des Kapitels erläutert, wie die grafische Oberfläche des Spielers, die bereits in Kapitel 4.6 (AWT-Komponenten) und danach in Kapitel 8.8 (Swing-Komponenten) implementiert wurde, an die JavaBeans-Technologie angepasst werden kann. Auf die Erläuterung der Klassenhierarchie dieses Beispiels wird hierbei verzichtet, da sie mit der in Kapitel 4.6 beschriebenen Klassenhierarchie übereinstimmt. Es sei darauf hingewiesen, dass der Schwerpunkt dieser Darstellung auf der Verwendung der JavaBeans-Technologie im Hinblick auf die folgenden Klassenerläuterungen liegt. Zum einfachen Verständnis der Zusammenhänge wurden die Namen der Klassen dieses Beispiels, die an die JavaBeans-Spezifikation angepasst wurden, mit der Endung Bean versehen. Somit entspricht die Klasse GUISVUserBean der AWT-Klasse GUISVUser. bzw. der Swing-Klasse JGUISVUser. Weiterhin wurde für jedes entwikkelte Bean die entsprechende BeanInfo-Klasse implementiert und mit einem Beispiel-Applet getestet. Folglich kann an dieser Stelle auf die Erläuterung der Logik des Programmablaufs verzichtet und die Darstellung auf die Benutzung der Design Patterns von JavaBeans eingeschränkt werden. Im ersten Teil dieses Beispiels wird untersucht, ob die Re-Implementierung der Klassen in Beans sinnvoll ist. Wie schon in der Einleitung verdeutlicht wurde, ist ein JavaBean als eine wiederverwendbare Software-Komponente definiert, die in einer Entwicklungsumgebung visuell bearbeitet werden kann. Wird das in diesem Buch beschriebene Beispiel Schiffe versenken betrachtet, so folgt, dass es vor allem sinnvoll ist, die Klassen Quadrate und SpielCanvas als Beans zu implementieren. Der Grund hierfür liegt in der Wiederverwendbarkeit der Beans. Hauptprogramm Aufgabe des Applets, das in der Klasse GUISVUserBean implementiert ist, ist die Steuerung der gesamten Client-Anwendung. Der Code des dementsprechenden AWT-Applets ändert sich hierbei, da sowohl die Quadrate-Klasse als auch die SpielCanvas-Klasse in Beans umgewandelt wurden. Setzt man die Code-Stücke wieder zusammen, so erhält man das übersetzungsfähige Applet. import java.applet.*; import java.awt.*; import java.awt.event.*; public class GUISVUserBean extends Applet { SpielCanvasBean sc1; SpielCanvasBean sc2; Spielfeld spieler, computer; Button shoot; Button setship; // Panel, in dem die zwei Buttons (shoot und setship) stehen Panel buttonPanel; // auf diesem Panel wird das Spielfeld des Computers platziert Panel sc1Panel; // im Zentrum dieses Panels wird das Spielfeld des Spielers // platziert Panel sc2Panel; Label label1; // Bezeichnung des Spielfelds des Computers Label label2; // Bezeichnung des Spielfelds des Spielers Container c; // Alle anderen Methoden und inneren Klassen dieses Applets } Zunächst werden die Komponenten der Packages java.awt, java.awt.event und java.applet importiert. Die Klasse GUISVUserBean ist ein Applet und wird durch die init()-Methode initialisiert. public void init () { c = this; computer = new Spielfeld(); spieler = new Spielfeld(); setLayout(new GridLayout(1,3,5,0)); setSize(650,266); Im Anschluss daran werden die übrigen Komponenten dieses Applets initialisiert. Die Spielbereiche des Computers und des Spielers, ausgedrückt durch die Verwendung der Klasse SpielCanvas, werden ebenso wie die Buttons nicht direkt in das Applet eingebettet. Hierfür werden Hilfspanels benutzt. An dieser Stelle wird deutlich, wie die Parameter dieser Klasse durch Setter- und Getter-Methoden eingesetzt werden. Anschließend wird die Klasse für die jeweilige Aufgabe (Computer oder Spieler) initialisiert (Methode initialisiere()). //Spielfeld des Computers sc1Panel = new java.awt.Panel(); sc1Panel.setLayout(new BorderLayout(0,0)); sc1Panel.setBounds(142,0,142,266); sc1Panel.setBackground(new Color(16777215)); sc1 = new SpielCanvasBean(); sc1.setPosition1(true); sc1.setS(computer); sc1Panel.add("Center",sc1); sc1.initialisiere(); label1 = new java.awt.Label("Spielfeld des Computers", Label.LEFT); label1.setBounds(0,133,142,133); sc1Panel.add("South",label1); add(sc1Panel); Sowohl das Objekt SpielCanvas als auch dessen Beschriftung werden in das Hilfspanel eingefügt, das in das Applet eingebettet wird. //Spielfeld des Benutzers sc2Panel = new java.awt.Panel(); sc2Panel.setLayout(new BorderLayout(0,0)); sc2Panel.setBounds(284,0,142,266); sc2Panel.setBackground(new Color(16777215)); add(sc2Panel); sc2 = new SpielCanvasBean(); sc2.setS(spieler); sc2Panel.add("Center",sc2); sc2.initialisiere(); label2 = new java.awt.Label("Spielfeld des Benutzers", Label.LEFT); label2.setBounds(0,133,142,133); sc2Panel.add("South",label2); Die Buttons werden dem Objekt buttonPanel direkt hinzugefügt, das dann anschließend dem Applet-Objekt hinzugefügt wird. Jeder dieser Buttons wird mit einem ActionListener-Objekt versehen. Auf die Klasse ButtonListener wird an dieser Stelle nicht weiter eingegangen, da sie bereits in Kapitel 4.6 beschrieben wurde. buttonPanel = new java.awt.Panel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER,5,5)); buttonPanel.setBounds(0,0,142,266); buttonPanel.setBackground(new Color(16777215)); add(buttonPanel); setship = new java.awt.Button(); setship.setLabel("neues Spiel"); setship.addActionListener(new ButtonListener(0)); setship.setBounds(31,5,80,23); setship.setBackground(new Color(12632256)); buttonPanel.add(setship); shoot = new Button("Schuss abgeben"); shoot.setEnabled(false); shoot.addActionListener(new ButtonListener(1)); shoot.setBounds(17,33,108,23); shoot.setBackground(new Color(12632256)); buttonPanel.add(shoot) Im Vergleich zum AWT-Beispiel muss eine weitere Methode, getSchussKoordinaten, geändert werden, um sie an die JavaBeans-Spezifikation anzupassen. Diese Methode verwendet die Methoden getSchussPositionX() und getSchussPositionY() des Objekts SpielCanvasBean, das in diesem Kapitel erklärt wird. public void getSchussKoordinaten() { System.out.println("Schuss auf "+sc1.getSchussPositionX()+" "+sc1.getSchussPositionY()); } Die Benutzeroberfläche dieses Applets ist identisch zu der in Kapitel 4.6 dargestellten. Klasse SchiffFensterBean In der Klasse SchiffFensterBean werden die Objekte für die Zuordnung der Schiffe und des gesamten Spielfelds deklariert. Diese Klasse entspricht der Klasse SchiffFenster (Kapitel 4.6) bis auf eine kleine Änderung, die im Folgenden erläutert wird. Im AWT-Beispiel wurde das Spielfeld des Benutzers mit folgendem Code im Konstruktor dieser Klasse definiert: //Spielfeld des Benutzers sc1 = new SpielCanvasPos2(s, q); centerPanel.add(sc1); Um das Spielfeld des Benutzers zu initialisieren, wird folgender Code entsprechend der Bean-Spezifikation benötigt: //Spielfeld des Benutzers sc1 = new SpielCanvasBean(); sc1.setPosition2(true); sc1.setS(s); sc1.setQ(q); sc1.initialisiere(); centerPanel.add(sc1); Es wird an dieser Stelle auf die Darstellung der Oberfläche verzichtet, da sie der in Kapitel 4.6 dargestellten Oberfläche gleicht. Klasse QuadrateBean Die Klasse QuadrateBean stellt ein Schiff im Spielfeld dar. Für dieses Bean sind drei Variablen nötig, die als private deklariert werden. Wie es die JavaBeans-Spezifikation vorschreibt, wird hier ein Null-Konstruktor verwendet. import java.awt.*; class QuadrateBean extends Canvas { private Color farbe; private int nummer; private Spielfeld s; // Null Konstruktor public QuadrateBean() { } // Alle anderen Methoden und inneren Klassen dieses Applets } Für jede dieser Variablen werden die entsprechenden Setter- und Getter-Methoden definiert. // Setter- und Getter-Methoden der verschiedenen Variablen public void setS(Spielfeld s){ this.s = s; } public Spielfeld getS(){ return s; } public void setNummer(int i){ this.nummer = i; } public int getNummer(){ return nummer; } public void setFarbe(Color c){ farbe = c; repaint(); } public Color getFarbe (){ return farbe; } Zum Schluss wird die paint-Methode dargestellt. Wird dieses Bean in Abhängigkeit von der speziellen Funktion eines Spielfelds verwendet, so wird es mit den Farben Grün und Rot visuell dargestellt. Wird das Bean in einem anderen Kontext verwendet, so kann die Farbe selbst definiert werden (Methode setFarbe()). public void paint(Graphics screen) { screen.setColor(Color.black); // Erzeuge Quadrat screen.drawRect(0,0,15,15); screen.setColor(getFarbe()); screen.fillRect(1,1,14,14); // Erzeuge Quadrat als Spielfeldbegrenzung if (s.schiff[nummer].p[0].x!=-1) screen.setColor(Color.green); else screen.setColor(Color.red); //(1,1,14,14) und nicht (0,0,15,15) damit // die Umrandung gesehen wird screen.fillRect(1,1,14,14); } Ein Beispiel-Applet für die Verwendung der Klasse QuadrateBean ist im Folgenden beschrieben. Klasse QuadrateBeanBeanInfo Ein Bean kann mittels der BeanInfo-Klasse inspiziert werden. Zu jedem Bean kann eine Metaklasse BeanInfo gehören, die durch ihren Namen und durch die Endung BeanInfo charakterisiert wird. Über die BeanInfo-Klasse werden explizit die wichtigsten Eigenschaften des Beans beschrieben. In diesem Beispiel wurde die SimpleBeanInfo-Klasse verwendet. Aus Gründen der Programmiereffizienz ist es stets zu empfehlen, die BeanInfo-Klasse als Subtyp der Klasse SimpleBeanInfo aufzubauen. Die hier implementierte Klasse hat einen Null-Konstruktor und erzeugt kein Icon, das das Bean in einer Entwicklungsumgebung darstellen kann. Das Bean wird daher in der Entwicklungsumgebung standardmäßig mit dem Default-Icon repräsentiert. Diese spezielle BeanInfo-Klasse legt lediglich den Namen des entsprechenden Beans fest. Folglich werden alle Eigenschaften dieses Beans von den Entwicklungsumgebungen mittels der Reflexion bestimmt. import java.beans.*; public class QuadrateBeanBeanInfo extends java.beans.SimpleBeanInfo { // Null-Konstruktor public QuadrateBeanBeanInfo() { } public java.awt.Image getIcon(int nIconKind){ java.awt.Image img = null; return img; } private final static Class beanClass = QuadrateBean.class; } Klassse QuadrateApplet Aufgabe dieser Klasse, die im Zusammenhang mit der Klasse QuadrateBean operiert, ist die Instantiierung der Klasse QuadrateBean und die Zuweisung der Farbe Magenta. import java.applet.*; import java.awt.*; import java.beans.*; public class QuadrateApplet extends Applet { public void init () { QuadrateBean q = new QuadrateBean(); setSize(400,200); setLayout(new BorderLayout()); q.setFarbe(Color.magenta); add(q); } } Paket Quadrate.jar In diesem Teil des Beispiels wird verdeutlicht, wie eine JAR-Datei erzeugt werden kann. Hierzu wird zunächst eine Manifest-Datei implementiert, die angibt, ob die Klassen in Form von Beans vorliegen, bzw. ob sie während der Laufzeit benötigt werden. Die Manifest-Datei sieht wie folgt aus: Manifest-Version: 1 Name: QuadrateApplet.class Java-Bean: False Name: QuadrateBean.class Java-Bean: True Name: QuadrateBeanBeanInfo.class Java-Bean: False Design-Time-Only: true Name: Schiffe.class Java-Bean: False Name: SpielFeld.class Java-Bean: False Zur Erzeugung der Datei quadrate.jar sind die folgenden Zeilen notwendig: jar -cfm quadrate.jar Manifest.mf *.class Nachdem die JAR-Datei erzeugt wurde, wird eine HTML-Datei implementiert, die die Verwendung dieser JAR-Datei darstellt: <HTML> <BODY> <h3> Quadrate-Bean innerhalb einer JAR-Datei <h3> <APPLET CODE="QuadrateApplet.class" ARCHIVE="quadrate.jar"> </BODY> </HTML> Die Bildschirmausgabe dieses Beispiels ist in Abb. 9-22 dargestellt. Abb. 9.22: Verwendung des Quadrate-Beans als Applet Klasse SpielCanvasBean Wie schon zu Beginn dieses Unterkapitels erwähnt, besteht das Ziel dieses Beispiels darin, Design Patterns von JavaBeans zu entwickeln und zu erklären, aber nicht darin, ein neues Beispiel zu erzeugen. Daher unterscheidet sich die Funktionalität der Klasse SpielCanvasBean von derjenigen, die in Kapitel 4.6 (AWT) dargestellt wurde. In diesem Beispiel wurden alle Variablen als private deklariert und mit den Setter- und Getter-Methoden versehen. Weiterhin wurde hier auf die Klassen SpielCanvasPos1 und SpielCanvasPos2 verzichtet, und deren Funktionalität durch Parametrisierung umgesetzt. Diese Funktionalität kann mit Hilfe der zwei boole'schen Variablen position1 und position2 realisiert werden. import java.awt.*; import java.awt.event.*; public class SpielCanvasBean extends Panel { private Spielfeld s; private QuadrateBean[] q; private SelectionAreaPos1 sa1; private SelectionAreaPos2 sa2; // zur Parametrisierung dieses Beans private boolean position1; private boolean position2; public SpielCanvasBean () { sa1 = new SelectionAreaPos1(); sa2 = new SelectionAreaPos2(); } // Weitere Methoden } Im Folgenden werden die Setter- und Getter-Methoden der verschiedenen Variablen dieser Klasse definiert. public void setS(Spielfeld s){ this.s = s; } public Spielfeld getS(){ return s; } public void setQ(QuadrateBean[] q){ this.q = q; } public QuadrateBean[] getQ(){ return q; } public SelectionAreaPos1 getSa1(){ return sa1; } public void setSa1(SelectionAreaPos1 sa1){ this.sa1 = sa1; } public SelectionAreaPos2 getSa2(){ return sa2; } public void setSa2(SelectionAreaPos2 sa2){ this.sa2 = sa2; } Die Parameter zur Feststellung bzw. Festlegung, ob es sich im konkreten Fall um einen Canvas der Position 1 oder der Position 2 handelt, werden mit Hilfe der boole'schen Setter- und Getter-Methoden angegeben. Es sei hier nochmals auf die Design Patterns der boole'schen Variablen hingewiesen, in denen die Getter-Methoden durch die Is-Methoden repräsentiert werden. public void setPosition1(boolean b){ position1 = b; } public boolean isPosition1(){ return position1; } public void setPosition2(boolean b){ position2 = b; } public boolean isPosition2(){ return position2; } Zur Angabe der Schussposition wurden zwei neue Methoden definiert. // Schuss-Koordinaten public int getSchussPositionX(){ return getSa1().tmp.x; } public int getSchussPositionY(){ return getSa1().tmp.y; } Zum Abschluss der Klasse wurde eine Initialisierungsmethode implementiert, die diese Klasse so initialisiert, dass sie entweder dazu eingesetzt wird, nur eine Positionsangabe vornehmen zu können (Spielphase), oder aber so, dass zwei Positionen mit der Maus markiert werden können (Spielaufbauphase). Diese Methode wird aufgerufen, nachdem ein Objekt dieser Klasse erzeugt wurde und nachdem die verschiedenen Parameter dieser Klasse festgelegt wurden. // Initialisierungmethode public void initialisiere(){ if (isPosition1()) { setLayout(new GridLayout(1,0)); sa1 = new SelectionAreaPos1(getS()); add(sa1); validate(); } else if (isPosition2()) { setLayout(new GridLayout(1,0)); sa2 = new SelectionAreaPos2(getS(), getQ()); add(sa2); validate(); } } Die paint-Methode, die in Kapitel 4.6 (AWT) beschrieben wurde, wird für dieses Beispiel unverändert übernommen und daher nicht nochmals beschrieben. Paket GUISVUserBean.jar In diesem Teil des Beispiels wird verdeutlicht, wie das beschriebene Applet im Rahmen einer JAR-Datei verwendet werden muss. Dazu wird zuerst eine Manifest-Datei angelegt. In dieser Manifest-Datei werden allerdings nicht alle Klassen explizit angegeben: Manifest-Version: 1 Name: GUISVUserBean.class Java-Bean: False Zur Erzeugung der JAR-Datei muss folgendes Kommando verwendet werden: jar -cfm guiapplet.jar Manifest.mf *.class Nachdem die JAR-Datei erzeugt wurde, wird eine HTML-Datei angelegt, die die JAR-Datei referenziert. <HTML>.. <BODY>... <APPLET CODE="GUISVUserBean.class" ARCHIVE="guijar.jar"> </BODY></HTML> Die Ausgabe und die visuelle Darstellung dieses Beispiels gleicht der Ausgabe, die sich ohne JAR ergibt.