BigBastis Blog

Tutorial: Model-View-Controller (MVC) Struktur in Java Projekten nutzen

Introduction

user

Sebastian Gross

Sebastian Gross arbeitet in Bielefeld als Softwareentwickler für .NET und Java im Bereich Web.Als Fan der .NET-Plattform lässt er sich kein Userguppen Treffen und Community Event im Raum OWL entgehen.Dabei hat er eine besondere Vorliebe für das ASP.NET MVC Framework und für das Test Driven Development (TDD) entwickelt.


LATEST POSTS

Handling too long scrollspy menus 10th June, 2015

Java: Create ZIP archive 23rd March, 2015

Java

Tutorial: Model-View-Controller (MVC) Struktur in Java Projekten nutzen

Posted on .

Die MVC-Architektur erfreut sich in letzter Zeit sehr hoher Beliebtheit, ständig hört man über neue Frameworks die auf diese Architektur schwören. (Beispiele: Spring Framework, ASP.NET MVC, Objective C…)

Bild 1: Der Aufbau – Quelle: Wikipedia

Doch was steckt eigentlich dahinter und warum sollte man seine Struktur überdenken?

Ich denke jeder, der eine Anwendung geschrieben hat schon die Erfahrungen gemacht, dass der Code immer unübersichtlicher wird, je größer ein Projekt wird.

Hier setzt MVC an, denn durch die strickte Teilung von Präsentation (View), der Programmlogik (Controller) und der Datenschicht (Model) erhöht sich die Lesbarkeit und Wartbarkeit des Codes. So kann man ein Projekt viel einfacher um weitere Funktionen ergänzen ohne den halben Sourcecode durchzugucken.

In diesem Artikel möchte ich diese Architektur an einem kleinem Java Beispiel demostrieren. Das Programm ist natürlich nicht wirklich zu gebrauchen, es soll lediglich das Vorgehen nahelegen.

Wie bereits oben erwähnt soll die Präsentation, also das was der Benutzer zu sehen bekommt (üblicherweise das Formular, also die GUI) von der Logik getrennt werden. Deshalb dürfen in der View-Klasse nur Sachen stehen, die der Darstellung der Elemente dienen. Das was passiert wenn man eins dieser Elemente anklickt wird dann über den Controller gesteuert.

Die Berechnungen an sich stehen dann im Model, wo auch alle Objekte verweilen, die unser Programm nutzt.

Unser Demo Programm ist sehr einfach aufgebaut, wir haben nur vier Klassen:

Bild 2: Der Aufbau des Projekts

Bevor ihr euch wundert, das Programm soll uns später die Quadratwurzel einer Zahl berechnen.

An Bild 1 könnt ihr erkennen, dass die Controller Klasse die wichtigste ist, da sie eigentlich das Ganze Programm steuert und dafür sorgt, dass die View und das Model sich verständigen können. Deswegen muss die Controller Klasse die anderen Klassen kennen, die anderen brauchen sich gegenseitig aber nicht!

public class Main {

    static WurzelController controller;

    /**
     * Diese Klasse wird nur dazu benutzt alle nötigen
     * Komponenten zu Initialisieren und die erste
     * View anzuzeigen
     */
    public static void main(String [] args){
        controller = new WurzelController();

        controller.showView();
    }
}

Die Klasse Main macht hier auch nichts anderes, als die anderen Klassen zu instanzieren und uns die View anzuzeigen.

An den Konstruktoren kann man auch die Abhängigkeiten erkennen. Eigentlich macht man der View auch das Model bekannt (siehe Bild 1), aber da unser Beispiel zu simpel ist können wir uns diese Bekanntschaft sparen.

Das Model ist ebenfalls sehr einfach aufgebaut, es enthält die nötigen Rechenoperationen die für unser Programm wichtig sind und bietet öffentliche Methoden mit denen der  Controller die Werte abfragen kann

/**
 * Das Model ist komplett unabhängig von den anderen
 * Klassen und weiß nicht was um ihn herum geschieht.
 * Es ist völlig egal ob man dieses Model aus einem
 * Fenster oder einer Konsolen Eingabe verwendet -
 * beiden würde funktionieren.
 */</pre>
<pre>public class WurzelModel {

    long _value;

    public WurzelModel(){
        zurückSetzen();
    }

    public void zurückSetzen(){
        this._value = 0;
    }

    public void berechneWurzel(long wert){
        this._value =  (wert * wert);
    }

    public long getWurzel(){
        return this._value;
    }
}

Wichtig hierbei ist, dass diese Klasse keine Beziehungen zu den anderen Klassen hat! Man muss sie so aufbauen, dass man sie ohne eine Änderung in einem anderen Projekt benutzen könnte.

Unsere View ist ganz typisch aufgebaut. Es werden die Steuerelemente initialisiert und auf der JForm plaziert, desweiteren muss die View Methoden bieten, mit denen man die ActionListener für die auf dem Formular liegenden Steuerelemente setzen kann. Über diese wird unser Controller mit der View kommunizieren. Dazu kommen noch die getter und setter Methoden für die Textfelder, diese werden benötigt, damit man aus dem Controller Zugriff darauf hat!

/** Die View-Klasse diese Enthält nur die Präsentation
 * hier sollte man keinerlei Programmlogik finden
 * alle Berechnungen und Reaktionen auf Benutzeraktionen
 * sollten allesammt im Controller stehen
 */</pre>
<pre>public class WurzelView extends JFrame{

    private JLabel lbl1 = new JLabel("Eingabe: ");
    private JTextField txtEingabe = new JTextField(3);
    private JButton cmdCalc = new JButton("Wurzen Berechnen >");
    private JTextField txtErg = new JTextField(5);
    private JButton cmdClear = new JButton("Zurüclsetzen");

    public WurzelView(){
        super("Wurzel Berechnen");

        initForm();
    }

    /**
     * Die JForm initialisieren und alle Steuerelemente
     * darauf positionieren
     */
    private void initForm(){
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(new FlowLayout());
        this.setBounds(200, 200, 500, 100);

        this.add(lbl1);
        this.add(txtEingabe);
        this.add(cmdCalc);
        this.add(txtErg);
        this.add(cmdClear);

    }

    public void resetView(){
        this.txtEingabe.setText("");
        this.txtErg.setText("");
    }

    public String getEingabe(){
        return this.txtEingabe.getText();
    }

    public void setErgebnis(String erg){
        this.txtErg.setText(erg);
    }

    /**
     * Funktionen bereitstellen, mit denen man später aus
     * dem Controller die nötigen Listener hinzufügen kann
     */
    public void setWurzelBerechnenListener(ActionListener l){
        this.cmdCalc.addActionListener(l);
    }

    public void setResetFormListener(ActionListener l){
        this.cmdClear.addActionListener(l);
    }
}

Bleibt nur noch der Controller. Dieser ist auch sehr simpel aufgebaut:

/**
 * Der Controller muss beide die View und das Model kennen
 * da dieser für die Kommunikation zwischen den Beiden sorgt
 */
public class WurzelController {

    private WurzelView _view;
    private WurzelModel _model;

    public WurzelController(){
        this._model = new WurzelModel();
        this._view = new WurzelView;

        addListener();
    }</pre>
<pre>
    public void showView(){
        this._view.setVisible(true);
    }</pre>
<pre>
/**
     * Die Listener, die wir aus den Internen Klassen generieren
     * werden der View bekannt gemacht, sodass diese mit
     * uns (dem Controller) kommunizieren kann
     */
    private void addListener(){
        this._view.setWurzelBerechnenListener(new WurzelBerechnenListener());
        this._view.setResetFormListener(new ResetFormListener());
    }

    /**
     * Inneren Listener Klassen implementieren das Interface ActionListener
     *
     * 1: Hier wird erst der eingegebene Wert aus der View geholt
     * 2: Der Wert wird dem Model übergeben und die Wurzel berechnet
     * 3: Die berechnete Wurzel wird aus dem Model geladen und
     * 4: Wieder der View zum Darstellen übergeben
     *
     * ACHTUNG: Fehlerprüfung muss noch implementeirt werden
     */
    class WurzelBerechnenListener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            long wert = Long.valueOf(_view.getEingabe());
            _model.berechneWurzel(wert);
            _view.setErgebnis(String.valueOf(_model.getWurzel()));
        }
    }

    /**
     * Hier wird dem View und dem Model gesagt ihre gespeicherten
     * Werte zu löschen.
     */
    class ResetFormListener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            _view.resetView();
            _model.zurückSetzen();
        }
    }
}

Wie man sieht werden hier nur die dem Konstruktor übergebenen Referenzen auf die View und das Model gespeichert und dann die Erzeugten ActionListener der View übergeben.

Die ActionListener sind interne Klassen die nun automatisch von unserer View aufgerufen werden, wenn der Benutzer einen der Buttons klickt! Praktisch oder?

Wenn man die WurzelBerechnenListener Klasse anschaut kann man auch die Typische Kommunikation zwischen den Schichten sehen. Die View lößt ein Event aus, für dessen Abhandlung der Controller verantwortlich ist. Nun holt der Controller sich alle nötigen Daten aus der View (in diesem Fall ist es nur ein Wert, die Eingabe) und lässt das Model das Quadrat berechnen. Schließlich holt der Controller sich die berechneten Daten zurück und lässt die View diese anzeigen.

Im Grunde gar nicht kompliziert oder? So sieht das Fenster zur Laufzeit aus:

Bild 3: Das Programm zur Laufzeit

Wenn man ein neues Projekt anfängt sollte man sich natürlich erst einmal genau überlegen ob man die MVC Architektur benutzt, denn diese ist bei kleinen Projekten mit einem höheren Aufwand verbunden. Wenn man aber weiß, dass in absehbarer Zeit an dem Programm weitere Änderungen oder Erweiterungen vorgenommen werden sollte man MVC in Betracht ziehen!

ACHTUNG: Habe eben erst gemerkt, dass ich überall „Wurzel“ geschrieben habe, das ergibt natürlich gar keinen Sinn! 😀 oh mann, muss wohl sehr verwirrt gewesen sein! Quadrat müsste es natürlich heißen. 🙂 Keine Lust das nun alles nochmal zu ändern!

Die Sourcen könnt ihr hier laden: Download

profile

Sebastian Gross

http://www.bigbasti.com

Sebastian Gross arbeitet in Bielefeld als Softwareentwickler für .NET und Java im Bereich Web.Als Fan der .NET-Plattform lässt er sich kein Userguppen Treffen und Community Event im Raum OWL entgehen.Dabei hat er eine besondere Vorliebe für das ASP.NET MVC Framework und für das Test Driven Development (TDD) entwickelt.

Comments
user

Author bigbasti87

Posted at 16:44 29. Januar 2010.

Bloggd: Tutorial: Model-View-Controller (MVC) Struktur in Java Projekten nutzen – http://blog.bigbasti.com/tutorial-model-… #java #mvc

user

Author under42

Posted at 22:54 8. Februar 2010.

Also ich muss sagen, ich versuche schon seit längerem den MVC-Ansatz zu verstehen…jedoch habe ich nie ein Tutorial komplett gelesen, da es mich nie gefesselt hatte bzw. schon mittendrin so komplex war, dass die Motivation schnell fehlte…aber jetzt habe ich es mit diesem verstanden!

Vielen Dank!

user

Author Schade

Posted at 20:36 20. April 2010.

Hmmm under42, nur leider ist das nicht wirklich MVC, denn der Controller regelt normalerweise nur die Weiterleitung der Requests an das Model und das Model sendet dann alles mit dem Observer Pattern an alle Observer(in diesem Fall die View)
Ich hätte einfach zu gerne eine richtige definition für das MVC Paradigma…

user

Author admin

Posted at 21:21 20. April 2010.

Hallo, ich muss zugeben, das Beispiel ist eine vereinfachung der MVC Struktur, aber völlig korrekt! Wie du sagtest, leitet der Controller alle Aktionen „nur“ weiter – und genau das macht der auch! Bette sag mir genauer was du meinst, denn ich verstehe deinen Einwand nicht ganz.

user

Author asd

Posted at 21:46 7. August 2010.

Hallo
wegen deinem Abschlusskommentar: da ist nur in einer Zeile ein kleiner Fehler

WurzelModell -> berechneWurzel
this._value = (wert * wert);
->
this._value = Math.sqrt(wert);

(hoffe ich habe es aus dem Kopf richtig geschrieben)

user

Author Zylindermann

Posted at 12:44 3. August 2011.

Hey!
Ich glaube er meint, dass nach dem ursprünglichen MVC Ansatz der Controller nur die Berechnenmethode des Modells aufruft. Das Model sendet darauf hin (mit Observer-Pattern) dem View, dass es geändert wurde, woraufhin sich die View die Daten auf dem Model holt, also ohne den Umweg über den Controller.
Wobei ich auch grad bei Wikipedia gelesen habe, dass es wohl keine feste Definition gibt.

user

Author Georg

Posted at 14:25 23. August 2011.

Hi,
ich muss Zylindermann zustimmen, das MVC ohne Observer-Pattern zwischen Model und View macht nicht so viel Sinn. Der Urgdanke des MVC besteht darin, die drei Komponenten Model, View und Controller so voneinander zu trennen, dass sie möglichst unabhängig voneinander geändert, erweitert, ausgetauscht und vor allem hinzugefügt werden können.

Wenn ich eine weitere View haben möchte, z.B. eine Liste der letzten Quadrat-Berechnungs-Ausgaben, also eine Art History-Box, müsste ich hier dem Controller der beschriebenen View mitteilen, dass er sich jetzt auch um die neue History-Viw kümmern muss, indem er die neuen Eingaben parallel in die neue History-View eintragen muss. Das heißt ich muss bei einer neuen View an den Controller einer alten View ran. Genau das möchte man beim MVC vermeiden – man will die drei Komponenten möglichst unabhängig voneinander machen.

Deshalb muss sich jede View, die an den Werten in einem Model interessiert ist, beim Model direkt als Observer anmelden und das Model muss bei jeder Änderung all ihre Observer durch ein universelles „notify()“ informieren. Jede View ist dann selbst dafür verantwortlich, auf diese Änderung zu reagieren.

So können beliebige Controller, die durch beliebige Views angestoßen werden, beliebige Models modifizieren und alle Views bleiben immer aktuell, ohne etwas voneinander oder gar von den Controllern der anderen Views zu wissen. Das ist die Unabhängigkeit und Erweiterbarkeit, die von MVC angestrebt wird.

Es tut mir leid, under42 enttäuschen zu müssen, aber das MVC ist halt doch ein Tickchen komplizierter als hier dargestellt. Dafür sieht man aber in 2 von 3 Stellenausschreibungen in der Informatik-Branche, dass das Beherrschen von MVC gefordert wird (gleich neben einem entsprechenden Abschluss etc.) – Wenn alles so einfach wäre, würden die Arbeitgeber nicht so einen großen Wert drauf legen, dass man es beherrscht…

user

Author Georg

Posted at 14:43 23. August 2011.

und nachdem ich einen halben Roman geschrieben habe, will ich jetzt doch dem Autor recht geben (mir aber auch ;). Und zwar, wie so oft in der Informatik, sind beide Ansätze verbreitet – sowohl der in diesem Tutorial beschriebene (in dem Link von mir als „Modified MVC“ bezeichnet) als auch der von mir dargestellte (in dem Link von mir als klassisches MVC beschrieben).

Welches davon wann zu benutzen ist oder ob eins davon generell besser ist, weiß ich leider nicht. Da die im Tutorial dargestellte Variante aber die neuere ist und diese auch in Cocoa von Apple verwendet wird (wobei Apple für sehr saubere Software bekannt ist), ziehe ich all meine Kritik zurück und bitte meinen vorherigen Post lediglich als einen Hinweis auf die sich von der vorgestellten unterscheidende klassische Variante des MVC zu verstehen.

user

Author Georg

Posted at 14:44 23. August 2011.
user

Author Gion

Posted at 17:39 7. Januar 2012.

Ändere doch einfach die berechnungsfunktion 🙂 Math.sqrt ? 🙂 dann stimmts ja auch wieder und du must nicht alles ändern 😉

user

Author Mario

Posted at 19:55 28. April 2012.

Mir hat der Beitrag und die Feedbacks gefallen. Thx

user

Author nino

Posted at 19:32 12. Mai 2012.

VIELEN DANK !!!

user

Author daniel seelig

Posted at 09:34 29. Juli 2012.

Das ist schon mvc, aber eher im sinn der serverseitigen webentwicklung, denn das geht es ja ausschlieslich um requests und da brauch meine keine observer zwischen model und view schalten. Wenn veränderungen durch events oder ereignisse passieren dann macht sinn über den controller model und view die jeweiligen zugehörigen observer mit zu geben, zu injezieren. Mir gefällt nur eins in dem tut nicht. Dad model sollte eine reine daten collection sein, für die ergebnissbringenden bzw. ausführenden methoden würde Ich in einen mapper stecken der das model via DI übergeben bekommt.

user

Author Jonas

Posted at 17:16 11. November 2012.

ich bekomme immer „java.lang.NullPointerException“ geworfen und mein actionListener funktioniert nicht.. jmd eine idee ?

user

Author Florian

Posted at 11:01 14. Januar 2013.

Hallo Jonas,
hast du alle imports drin? diese wurden hier im Artikel unterschlagen, auch musst du aufpassen in den Klassen findet sich hier und da ein -Tag der muss natürlich auch raus.

Viele Erfolg.

user

Author Elif

Posted at 10:00 8. März 2013.

Sehr schön und einfach verständlich verfasst, habe es auch endlich verstanden 🙂

Kommentar verfassen

View Comments (17) ...
Navigation