Was versteht man unter Model View Controller (MVC)?
Eine klare, übersichtliche Strukturierung, die damit verbundene Erleichterung von Wartungsarbeiten und die Wiederverwendbarkeit von Programmteilen gehören mit zu den obersten Zielen eines gelungenen Programmentwurfes. Das MVC-Muster ermöglicht dies dadurch, dass die architektonischen Hauptteile einer Software voneinander weitgehend separat gehalten und somit voneinander unabhängig bearbeitet und gewartet werden können. Drei Teile sind zu unterscheiden:
- Das Modell (engl. model)
- Es enthält die Arbeitsdaten eines Programms, z.B. Nutzereingaben, aus Datenbanken gelesene Daten, etc. Es enthält niemals Referenzen auf die beiden anderen Teile.
- Die Präsentation (engl. view)
- Die (bei einem Desktop-Programm z.B. graphische) Darstellung von Daten, die Programmoberfläche (GUI), etc. Sie enthält im Quelltext weder Daten, noch Teile der Geschäfts- oder Programmlogik vor, sondern ist lediglich für deren Darstellung verantwortlich. Hierzu definiert sie entsprechende Schnittstellen.
- Die Steuerung (engl. controller)
- Die vermittelnde Schicht, die für die Interaktion zwischen Präsentationsschicht und Daten zuständig ist. Sie wird oft mittels eines Observer-Patterns realisiert, das die Daten des Modells beobachtet und diese ggf. mit der Präsentationsschicht wechselseitig aktualisiert. Die Steuerung muss somit sowohl zu den Daten, als auch zu Teilen der Darstellungsschicht Zugang haben.
Das Beispiel demonstriert den grundsätzlichen Aufbau eines MVC-Musters, bleibt dabei aber der Übersichtlichkeit halber sehr einfach (z.B. ohne Observer-Pattern). Innerhalb der einzelnen Quelltextabschnitte wurde aus dem gleichen Grund zudem auf import-Anweisungen verzichtet. Der Quelltext kann in seiner Gesamtheit weiter unten bezogen werden.
Das Modell
Das Modell wurde extremst einfach gehalten. Die Klasse
definiert lediglich eine String
-Instanzvariable,
deren Wert durch eine Getter-Methode zu beziehen ist.
class MVCModel {
private String text = "Hallo Welt!";
public String getText() {
return text;
}
}
Die Präsentation
Sie stellt im gezeigten Beispiel eine Klasse mit einem JFrame
,
einem JLabel
und einem JButton
dar. Die Initialisierung der gesamten graphischen
Oberfläche findet in der Methode init()
statt, die im Konstruktor aufgerufen wird. Nach Klick
auf den Button wird der Inhalt der String
-Variablen
aus dem Modell auf dem Label dargestellt. Hierzu wird
der Button beim Controller angemeldet. Er
implementiert in diesem Fall ActionListener
und bekommt die aktuelle Instanz des View übergeben.
class MVCView {
JLabel label;
public MVCView() {
init();
}
private void init() {
label = new JLabel(" ");
label.setHorizontalAlignment(JLabel.CENTER);
JButton button = new JButton("klick");
button.addActionListener(new MVCController(this));
JFrame frame = new JFrame("MVC");
frame.add(label, BorderLayout.NORTH);
frame.add(button, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void setText(String s) {
label.setText(s);
}
}
Die Steuerung
Die Klasse implementiert ActionListener
und
übernimmt die Ereignis-Behandlung des Buttons. Der
Konstruktor bekommt beim Aufruf das MVCView
-Objekt
der Präsentationsschicht übergeben und erzeugt
eine neue Instanz des Modells.
In der die Aktion
realisierenden Methode actionPerformed()
wird dann der Text des Modells über den Getter des
Modells bezogen und durch eine Accessor-Methode der View
auf das Label gesetzt.
class MVCController implements ActionListener {
private MVCView view;
private MVCModel model;
public MVCController(MVCView view) {
this.view = view;
this.model = new MVCModel();
}
@Override
public void actionPerformed(ActionEvent e) {
view.setText(model.getText());
}
}
Wollte man zukünftig das Programm durch weitere
Darstellungselemente erweitern, so müssen lediglich
die Präsentations- und die Steuerungsklasse
angepasst werden. Das Modell kann unberücksichtigt
bleiben. Umgekehrt ist bei einer Änderung der Daten
kein Eingriff in Steuerung und Darstellung notwendig.
Selbstverständlich
muss hierbei berücksichtigt werden, dass in
konkreten Realisierungen zusätzlicher Aufwand bei
der Implementierung notwendig wird, etwa zur
Gewährleistung der Typsicherheit des Modells, etc.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class MVC {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MVCView();
}
});
}
}
class MVCView {
JLabel label;
public MVCView() {
init();
}
private void init() {
label = new JLabel(" ");
label.setHorizontalAlignment(JLabel.CENTER);
JButton button = new JButton("klick");
button.addActionListener(new MVCController(this));
JFrame frame = new JFrame("MVC");
frame.add(label, BorderLayout.NORTH);
frame.add(button, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void setText(String s) {
label.setText(s);
}
}
class MVCController implements ActionListener {
private MVCView view;
private MVCModel model;
public MVCController(MVCView view) {
this.view = view;
this.model = new MVCModel();
}
@Override
public void actionPerformed(ActionEvent e) {
view.setText(model.getText());
}
}
class MVCModel {
private String text = "Hallo Welt!";
public String getText() {
return text;
}
}
Alternative
Einen hinsichtlich der Aufgabenverteilung noch etwas
konsequenteren Vorschlag liefert Hubert Kauker
im folgenden Beispiel, das allerdings mit
öffentlichen JFrame
und JButton
-Instanzen
arbeitet:
import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; public class MVC implements Runnable { public static void main(String[] args) { SwingUtilities.invokeLater(new MVC()); } @Override public void run() { new MVCController(new MVCModel("Hello, world!"), new MVCView()); } } class MVCView { JLabel label; public JButton button; public JFrame frame; public MVCView() { init(); } private void init() { label = new JLabel(); label.setHorizontalAlignment(JLabel.CENTER); label.setBorder(new EmptyBorder(10, 10, 10, 10)); button = new JButton("Klick"); frame = new JFrame("MVC"); frame.setSize(160, 100); frame.add(label, BorderLayout.NORTH); frame.add(button, BorderLayout.CENTER); frame.setLocationRelativeTo(null); } public void update(MVCModel model) { label.setText(model.getText()); } } class MVCController implements ActionListener { private MVCView view; private MVCModel model; private int count = 0; public MVCController(MVCModel model, MVCView view) { this.model = model; this.view = view; view.update(model); view.button.addActionListener(this); view.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); view.frame.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { count++; if (count < 3) { model.decorateWith("{", "}"); } else if (count == 3) { model.setText(" - ENDE - "); } else { System.exit(0); } view.update(model); } } class MVCModel { private String text; public MVCModel() { text = "???"; } public MVCModel(String string) { text = string; } public void setText(String string) { text = string; } public String getText() { return text; } public void decorateWith(String s, String t) { text = s + text + t; } }
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.