Metamodeling
and its Applications
tud

Swing ist eine Bibliothek zur Erstellung graphischer Benutzungsoberflächen und ein Teil der Java Foundation Classes, die 1997 bei der JavaOne Developer Conference vorgestellt wurden. Swing löst damit das AWT-Toolkit von JDK 1.1 ab.

Das offizielle Tutorial zu Swing befindet sich auf den Sun-Seiten. Die ersten beiden Kapitel geben eine Einführung, die für kleinere Aufgaben vollständig ausreichend ist. Die Informationen zu den Layout-Managern sind ebenfalls sehr hilfreich.

Ebenfalls hilfreich kann der Teil über Graphische Benutzungsoberflächen der Vorlesung "Grundlagen der Informatik I" von Frau Prof. Mezini und Herrn Prof. Ostermann sein. Dieses Tutorial basiert auf den ebenfalls in dieser Veranstaltung verwendeten Sourcen zu TicTacToe. Wir implementieren im Folgenden die Klasse TicTacToeView.java Schritt für Schritt nach. Die kompletten Sourcen sind im Archiv tictactoe_source.zip zu finden.

Model View Controller

Der Aufbau der Klassen entspricht dem Model-View-Controller-Pattern. Da wir uns in diesem Tutorial mit Swing auseinandersetzen möchten, ist natürlich der View der interessanteste Teil. Betrachten wir also zunächst folgenden Ausschnitt:
public class TicTacToeView extends JFrame implements Observer
{
    private TicTacToeModel model;
    private TicTacToeController controller;
    private JButton[][] mBtns;    // button array
    public void won(Player i) {...}
public void tie() {...}
public void changed(int row, int col) {...}
public void reset() {...}
    public TicTacToeView(TicTacToeModel model, TicTacToeController controller) {
        this.model = model;
        this.controller = controller;
        viewInit();
        callbackInit();
        setSize(300, 300);
        setTitle("TicTacToe");
        model.addObserver(this);
    }
}

Das JFrame, das aus dem Sun-Tutorial schon bekannt sein sollte, stellt ein Fenster dar. In diesem können wir nun unsere Elemente platzieren. Die Methoden setSize() und setTitle() sollten selbsterklärend sein.

Wichtig ist zudem, das der View als Observer des Models registriert wird (siehe Entwurfsmuster "Observer"). Das Model verwendet das Interface Observer, um auf die Methoden des View zuzugreifen. So kann nicht nur der User, sondern auch das Model das Aussehen des Fensters verändern - beispielsweise durch Züge des anderen Spielers in einem anderen Fenster.

Das Spielfeld

private void viewInit()
    {
        int iRow;        // button row
        int iCol;        // button column
        Container pane;  // content pane 
               // get content pane, set to grid layout                pane = getContentPane();         pane.setLayout(new GridLayout(3, 3));         // create and attach buttons                    mBtns = new JButton[3][3];         for (iRow = 0; iRow < 3; iRow++) {             for (iCol = 0; iCol < 3; iCol++) {                 mBtns[iRow][iCol] = new JButton();                    pane.add(mBtns[iRow][iCol]);             }         }     }

Als nächstes wird ein Spielfeld erzeugt. Dazu greifen wir das erste Mal auf den sogenannten ContentPane Container zu. Er enthält alle sichtbaren Elemente und ist daher genau der richtige Ort um einige Buttons hinzuzufügen. Zunächst wird das Layout der Buttons mit einem sogenannten LayoutManager festgelegt. Da wir unsere Buttons rasterförmig anordnen möchten, verwenden wir ein GridLayout. Das Layout des ContentPanes wird also auf GridLayout gesetzt und anschließend neun Buttons hinzugefügt

Reaktionsfrei

Die Klasse läßt sich jetzt schon Kompilieren und zeigt uns ein kleines TicTacToe-Feld. Allerdings reagiert es noch auf keine Eingabe. Damit Eingaben auch an das Model weitergeleitet werden, fügen wir folgenden Quellcode ein:
    private void callbackInit()
    {
        int iR;        // row and column loop indices
        int iC;
 
        for (iR=0; iR<3; iR++) {
            for (iC = 0; iC < 3; iC++) {    
                final int row = iR; final int col = iC;
                mBtns[iR][iC].addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        controller.playerMove(row,col);
                        
                    }
                });
            }
        }
    }
Im markierten Teil wird ein sogenannter ActionListener erzeugt und bei allen Buttons als Observer angemeldet. Observer stellt unter anderem aus diesem Grund ein sehr häufig gebrauchtes Entwurfsmuster in der GUI-Programmierung dar.

Wird nun auf einen Button geklickt, wird die actionPerformed() Methode aller Observer ausgeführt. In diesem Fall soll der Controller dann den Zug ausführen. Beim erneuten Starten des Programms passiert allerdings noch nichts. Außerdem existieren immer noch einige leere Methoden am Anfang der Klasse.

Das Interface

Hier kommt das Interface Observer ins Spiel:
public interface Observer {
  public void changed(int row, int col);   public void won(Player player);   public void tie();   public void reset(); }
Wir erinnern uns, dass wir den View zu Beginn als Observer beim Model angemeldet haben. Allerdings nicht direkt, sondern über das obige Interface. Diese Methoden werden nun vom Model ausgeführt um die View zu verändern. Wir implementieren nun die Methoden des Interfaces die bisher noch leer sind:
public void won(Player i) {
JOptionPane.showMessageDialog(this,"Player "+i.ordinal()+" won!" ,
"Game Over", JOptionPane.INFORMATION_MESSAGE);
}
public void tie() {
JOptionPane.showMessageDialog(this, "Tie - Nobody wins",
"Game Over", JOptionPane.INFORMATION_MESSAGE);
}

public void changed(int row, int col) {
mBtns[row][col].setText(model.getOwnerOfFigureOnPlayingField(row,col).name());
}
public void reset() {
int iR, iC;
for (iR=0; iR<3; iR++)
for (iC = 0; iC < 3; iC++)
mBtns[iR][iC].setText("");
}

Wenn das Programm jetzt gestartet wird, steht einer kleinen Partie TicTacToe nichts mehr im Wege!