wie fängt man verschiedene Ereignisse ein mit MVC ?

ernst

Top Contributor
Hallo allerseits,
Will ein MVC-Pattern implementieren.
In der View gibt es verschiedene GUI-Elemente wie
Schieberegler, JTextfield, JButton, usw.
An diese GUI-Elemente werden nur Listener (Wanzen) angebracht, in der Form:
jButton1.addActionListener(controller);
jSchieberegler1.addActionListener(controller);
jTextfield1.addActionListener(controller);
jButton2.addActionListener(controller);
jSchieberegler2.addActionListener(controller);
jTextfield2.addActionListener(controller);
...
wobei
class Controller implements ActionListener{
...
}

das bedeutet, dass an diesen GUI-Elementen Ereignisse ausgelöst werden können.
Mein Problem ist, wie man diese _verschiedenen_ Ereignisse im Controler einfangen und identifizieren kann.

Ein Ereignis eines Schiebereglers identifiziert man anderes als das z.B. eines Buttons.
Schieberegler mit:
public void stateChanged(ChangeEvent e) {
JButton mit:
public void actionPerformed(ActionEvent e) {

Muß deshalb der Controler mehrere implements machen, wie z.B:
class Controller implements ChangeListener implements ActionListener implements ...

und mehrere Listener-Methoden haben, wie z.B:
public void stateChanged(ChangeEvent e) { // für einen Schieberegler
public void actionPerformed(ActionEvent e) { // für einen Button


mfg
ern
 

httpdigest

Top Contributor
Wenn du MVC ganz stringend implementieren willst, darf der Controller nicht von View-Spezifika abhängen. Also, die Tatsache, dass der Controller schon ein Swing-spezifisches ActionListener Interface implementiert, verletzt das MVC Design, dass nämlich theoretisch die View austauschbar sein soll, ohne den Controller zu ändern oder auszutauschen. Wenn du also irgendwann beschließt, nicht mehr Swing als View zu verwenden, sondern SWT, hättest du ein Problem.
Diese Stackoverflow-Antwort erläutert das alles nochmal im Detail: https://stackoverflow.com/questions...long-according-to-mvc-pattern#answer-26518274
Definiere also eine Schnittstelle zwischen Controller und View und nicht zwischen Controller und konkreter View-Implementierung (Swing).
P.S.: Und bitte übersetze "Listener" nicht mit "Wanze" :)
 

ernst

Top Contributor
Definiere also eine Schnittstelle zwischen Controller und View und nicht zwischen Controller und konkreter View-Implementierung (Swing).
Mit Schnittstelle meinst du sicherlich ein Interface.
Wenn man jetzt das Interface IF_Controler_View definiert, dann werden dort Methoden festgelegt, die die implementierenden Klassen - wie z.B. der Controler ausprogrammieren müssen.
Mir ist jetzt überhaupt nicht klar, warum deshalb der Controler bei einer veränderten View nicht geändert bzw. nicht ausgetauscht werden muss?
Irgendwo muss doch eine Änderung (bei einer anderen View) stattfinden!
Kannst du mir mal an einem einfachen Beispiel demonstrieren, wie du das gemeint hast ?

mfg (mit fragenden Grüssen)
ern
 

mihe7

Top Contributor
Mit Schnittstelle meinst du sicherlich ein Interface.
Schreibt der mit der Wanze :)

Ja, er meint ein Interface.

Wenn man jetzt das Interface IF_Controler_View definiert
So ein Interface definiert man nicht und mit Underscores schon gar nicht.

dann werden dort Methoden festgelegt, die die implementierenden Klassen - wie z.B. der Controler ausprogrammieren müssen.
Richtig.

Mir ist jetzt überhaupt nicht klar, warum deshalb der Controler bei einer veränderten View nicht geändert bzw. nicht ausgetauscht werden muss?
Ein ActionListener ist ein AWT/Swing-spezifisches Interface. Wenn Dein Controller dieses implementiert und Du irgendwann mal statt AWT/Swing etwas anderes verwenden willst, dann funktioniert Dein Controller nicht mehr. Wenn Du ein eigenes Interface hast, dann bist Du unabhängig.

Abgesehen davon: Listener hängen in AWT/Swing an den Komponenten. Du bewegst Dich aber auf View-Ebene. Wenn Du z. B. 10 Textfelder hast und einen Button, dann könntest Du beim Klick auf den Button Deinen Controller mit dem Inhalt der 10 Textfelder füttern.
 

httpdigest

Top Contributor
Mit Schnittstelle meinst du sicherlich ein Interface.
Mit Schnittstelle meine ich erstmal ganz allgemein das Konzept eines Vertrages zwischen zwei Beteiligten, also in deinem Fall dem Controller und der View. Dass man das dann implementierungstechnisch als Java-Interface realisieren würde (oder als abstrakte Klasse), ist dann ein Implementierungsdetail.

Mir ist jetzt überhaupt nicht klar, warum deshalb der Controler bei einer veränderten View nicht geändert bzw. nicht ausgetauscht werden muss?
Irgendwo muss doch eine Änderung (bei einer anderen View) stattfinden!
Kannst du mir mal an einem einfachen Beispiel demonstrieren, wie du das gemeint hast ?
Du musst dich immer (bei allem, was man in Software implementiert) fragen, wer hängt jetzt von wem ab? In deinem Fall hängt dann dein Controller von Swing ab (wie es @mihe7) schon sagte. Das willst du aber nicht. Du willst nicht, dass der Controller von View-spezifischen Implementierungsklassen abhängt. Du willst stattdessen eine View-implementierungs-unabhängige Schnittstelle schaffen. Das ist dann der Vertrag zwischen Controller und View. Und der Controller braucht dann nicht ausgetauscht zu werden, nur, weil sich die View-Technologie ändert. Die View-Implementierung muss dann einfach nur den Kontrakt zum Controller einhalten/implementieren, eben mit den Mechanismen, die die konkrete View-Implementierung bereitstellt. Im Falle von Swing dann also per Swing/AWT-spezifischen ActionListenern, die dann delegieren an die View/Controller-Schnittstelle, die vermutlich auch eine Art Listener sein wird.
 

mrBrown

Super-Moderator
Mitarbeiter
Auf die Frage, was bei MVC am leichtesten austauschbar sein soll, hab ich in diesem Forum bisher drei Antworten gelesene: Model, View und Controller...irgendwie amüsant...
 
Zuletzt bearbeitet:

ernst

Top Contributor
Die View-Implementierung muss dann einfach nur den Kontrakt zum Controller einhalten/implementieren, eben mit den Mechanismen, die die konkrete View-Implementierung bereitstellt. Im Falle von Swing dann also per Swing/AWT-spezifischen ActionListenern, die dann delegieren an die View/Controller-Schnittstelle, die vermutlich auch eine Art Listener sein wird.
Mir fehlt leider jegliche Vorstellung wie das konkret gemacht werden kann.
Kann mir jemand das an einem einfachen Beispiel demonstrieren?
Nochmals:
Mir ist jetzt überhaupt nicht klar, warum deshalb der Controler bei einer veränderten View nicht geändert bzw. nicht ausgetauscht werden muss, denn:
dann werden dort Methoden festgelegt, die die implementierenden Klassen - wie z.B. der Controler ausprogrammieren müssen.
Wenn diese aber im Contoler ausprogrammiert werden müssen, dann ist der Controler doch kein unveränderliches Element, sondern von der View abhängig!
Das aber wollt ihr verhindern!

mfg
ern
 

ernst

Top Contributor
>>
>>dann werden dort Methoden festgelegt, die die implementierenden Klassen
>>- wie z.B. der Controler ausprogrammieren müssen.
>>
>
>Richtig.
>
Dann ist der Controler doch kein konstantes Teil im MVC-Pattern.
Das verstehe ich nicht.

mfg
ern
 

httpdigest

Top Contributor
Hier ist ein ganz einfaches Beispiel, wie du deinen Controller unabhängig von einer View-Implementierung machen kannst:
Java:
import javax.swing.*;
public class Mvc {
  private static class View extends JFrame {
    View(ViewListener listener) {
      JButton btn = new JButton("Click me!");
      add(btn);
      pack();
      setVisible(true);
      setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      btn.addActionListener((e) -> listener.buttonClicked());
    }
  }
  private interface ViewListener {
    void buttonClicked();
  }
  private static class Controller implements ViewListener {
    public void buttonClicked() {
      System.out.println("Button clicked!");
      // Wahrscheinlich würde man hier auch ein Modell anpassen
    }
  }
  public static void main(String[] args) {
    new View(new Controller());
  }
}
Ab dem Controller ist es jetzt völlig egal, durch wen/was der Button nun gedrückt wurde. War es ein Swing-Button, ein SWT-Button, ein JavaFX-Button oder vielleicht sogar ein HTML-Button, dessen Click-Event per HTTP Request an ein Servlet ging? Egal!
Die Frage dabei, auf die wahrscheinlich auch @mrBrown hinaus wollte, ist nur, an welcher Stelle du Flexibilität durch Abstraktion tatsächlich benötigst.
 

ernst

Top Contributor
Du musst dich immer (bei allem, was man in Software implementiert) fragen, wer hängt jetzt von wem ab? In deinem Fall hängt dann dein Controller von Swing ab (wie es @mihe7) schon sagte. Das willst du aber nicht. Du willst nicht, dass der Controller von View-spezifischen Implementierungsklassen abhängt.
Du willst stattdessen eine View-implementierungs-unabhängige Schnittstelle schaffen.
Controler darf also nicht View abhängen.
Gilt dies auch für:
Controler - Model
Model - View
Oder wie sollen die in Beziehung zueinander stehen
 

httpdigest

Top Contributor
Der Controller kann schon von der View abhängen, aber in deinem Fall hängt der Controller ja von dem ab, wovon die View abhängt, nämlich Swing, was ich als "View-spezifische Implementierung" genannt hatte. Letztenendes ist es aber alles nicht so wild. Du musst nur überlegen, was einfach zu ersetzen/ändern sein soll. Und dafür schaffst du dann eine Abstraktion über z.B. eine Schnittstelle, von denen dann wieder die Dinge, die einfach zu ändern sein sollen, abhängen - in meinem Fall oben also das Mvc.ViewListener Interface.
 

ernst

Top Contributor
Du musst nur überlegen, was einfach zu ersetzen/ändern sein soll. Und dafür schaffst du dann eine Abstraktion über z.B. eine Schnittstelle, von denen dann wieder die Dinge, die einfach zu ändern sein sollen, abhängen - in meinem Fall oben also das Mvc.ViewListener Interface.
Habe im Interernet eine auch eine Demoversion für MVC gefunden (und diese ohne innere Klassen implementiert):
Es wird in einer GUI das Quadrat einer Zahl berechnet.

Ist dies auch eine gute Lösung oder braucht man unbedingt das Interface?
Warum hat diese Demo-MVC kein Interface verwendet ?
Ist diese Demo-MVC eurer Meinung nach auch ok ?

Java:
/*
siehe:
[URL]http://blog.bigbasti.com/tutorial-model-view-controller-mvc-struktur-in-java-projekten-nutzen/[/URL]
 */
package demoreines_mvc_21;

import java.awt.FlowLayout;
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.JTextField;

public class Startklasse {

    static QuadratController 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 QuadratController();

        controller.showView();
    }
}

/**
 * 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.
 */
class QuadratModel {

    long _value;

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

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

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

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

/**
 * 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
 */
class QuadratView extends JFrame {

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

    public QuadratView() {
        super("Quadrat 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 setQuadratBerechnenListener(ActionListener l) {
        this.cmdCalc.addActionListener(l);
    }

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

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

    private QuadratView _view;
    private QuadratModel _model;

    public QuadratController() {
        this._model = new QuadratModel();
        this._view = new QuadratView();

        addListener();
    }

    public void showView() {
        this._view.setVisible(true);
    }

    /**
     * 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.setQuadratBerechnenListener(new QuadratBerechnenListener(_model, _view));
        this._view.setResetFormListener(new ResetFormListener(_model, _view));
    }
}



class QuadratBerechnenListener implements ActionListener {
    private QuadratModel _model;
    private QuadratView _view; 

    public QuadratBerechnenListener(QuadratModel _model, QuadratView _view){
        this._model=_model;
        this._view=_view;
    }
 
    public void actionPerformed(ActionEvent e) {
        long wert = Long.valueOf(_view.getEingabe());
        _model.berechneQuadrat(wert);
        _view.setErgebnis(String.valueOf(_model.getQuadrat()));
    }
}

/**
 * Hier wird dem View und dem Model gesagt ihre gespeicherten Werte zu löschen.
 */
class ResetFormListener implements ActionListener {
    private QuadratModel _model; 
    private QuadratView _view;

    public ResetFormListener(QuadratModel _model, QuadratView _view){
        this._model=_model;
        this._view=_view;
    }
 
 
    public void actionPerformed(ActionEvent e) {
        _view.resetView();
        _model.zurückSetzen();
    }
}


mfg
ern
 

mrBrown

Super-Moderator
Mitarbeiter
Das ist ein eher schlechtes Beispiel für MVC, einerseits durch die Domäne (die nicht wirklich OO abgebildet ist) und durch die Umsetzung, welche kein reines MVC ist.

Bei reinem MVC aktualisiert nicht der Controller die View, sondern diese sich allein. Damit sie weiß, wann sie sich aktualisieren muss, wird das ObserverPattern verwendet. Die View ist dabei ein Observer des Models, wenn das Model dann aktualisiert wird, bekommt die View dies mit.
Umsetzbar in diesem Fall ist es z.B. dadurch, dass das Model ein "Taschenrechner" ist, in den man eine Zahl eingeben (setNumber(int)) und dann eine Rechnung mit dieser durchführen (berechneQuadrat()) kann. Nach jeder Aktion aktualisiert sich das Model und die View kann die neuen Daten anzeigen.

Die beiden Aktionlistener rufen dann nur noch Funktionen auf dem Model auf, müssen aber die View nicht mehr kennen. Die View dagegen kennt dann das Model, und zeigt dessen Daten an.


Auf ein Interface zwischen Controller und View kann man meist verzichten, muss dann aber in Kauf nehmen, beides neu zu programmieren, wenn man die Plattform wechselt (CLI, Swing, JavaFX, ...)
 

ernst

Top Contributor
Das ist ein eher schlechtes Beispiel für MVC, einerseits durch die Domäne (die nicht wirklich OO abgebildet ist)
was meinst du mit Domäne ?

...
Die beiden Aktionlistener rufen dann nur noch Funktionen auf dem Model auf, müssen aber die View nicht mehr kennen.
Was meinst du mit "müssen aber die View nicht mehr kennen" ?
Be meinem neuen Programm (siehe unten) hat der Listener:
AdditionBerechnenListener(...)
die zwei Attribute:
TRModel trModel, TRView trView
und kennt somit die View.
Oder meinst du damit etwas anderes ?

Die View dagegen kennt dann das Model, und zeigt dessen Daten an.
Ja, denn die View hat das Modell als Attribut.
Kann man bei MVC Folgendes behaupten:
- View kennt Modell: die View hat das Modell als Attribut.
- Controller kennt View und Model: Controller hat View und Model als Attribute
und sonst gibt es keine Bekanntschaften!

Auf ein Interface zwischen Controller und View kann man meist verzichten, muss dann aber in Kauf nehmen, beides neu zu programmieren, wenn man die Plattform wechselt (CLI, Swing, JavaFX, ...)
Gilt das auch (wenn ja, wo ?) für mein Programm ?

Wenn du MVC ganz stringend implementieren willst, darf der Controller nicht von View-Spezifika abhängen. Also, die Tatsache, dass der Controller schon ein Swing-spezifisches ActionListener Interface implementiert, ...
habe ich in meinem Programm nicht mehr!

verletzt das MVC Design, dass nämlich theoretisch die View austauschbar sein soll, ohne den Controller zu ändern oder auszutauschen. Wenn du also irgendwann beschließt, nicht mehr Swing als View zu verwenden, sondern SWT, hättest du ein Problem.
Gilt das auch (wenn ja, wo ?) für mein Programm ?

Java:
package demoreinesmvc_53;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.*;

public class Startklasse {

    public static void main(String[] args) {
        TRController trController = new TRController();
    }
}
// M O D E L
class TRModel extends Observable {

    private double speicherwert;

    public TRModel() {
        speicherwert = 0;
    }

    public double getSpeicherwert() {
        return speicherwert;
    }

    public void setSpeicherwert(double wert) {
        speicherwert = wert;
        // Dem Model das Feuern ermöglichen      
        setChanged();
        // der String dient zur Identifikation des Ereignisses
        notifyObservers("set");
    }

    public void addiereDazu(double wert) {
        speicherwert = speicherwert + wert;
        // Dem Model das Feuern ermöglichen
        setChanged();
        notifyObservers("+");
    }
}

// V I E W
class TRView extends JFrame implements Observer {

    private TRModel trModel;
    private JButton buttonPlus;
    private JButton buttonGleich;
    private JTextField tfdZahl;

    public TRView(TRModel trModel) {
        this.trModel = trModel;
        buttonsAnbringen();
    }

    public String getJTextField() {
        return this.tfdZahl.getText();
    }

    public void setJTextField(String str) {
        this.tfdZahl.setText(str);
    }

    public void buttonsAnbringen() {
        this.buttonPlus = new JButton("+");
        this.buttonGleich = new JButton("=");
        this.tfdZahl = new JTextField("  ", 30);

        // Beim Anklicken dieses Buttons wird ein Ereignis-
        // objekt geworfen, das durch den String "btn+"
        // identifiziert wird.
        this.buttonPlus.setActionCommand("btn+");
        // analaog
        this.buttonGleich.setActionCommand("btn=");

        getContentPane().add(buttonPlus, BorderLayout.EAST);
        getContentPane().add(buttonGleich, BorderLayout.WEST);
        getContentPane().add(tfdZahl, BorderLayout.CENTER);
        this.setSize(200, 100);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public void horcherAnButtonGleichAnbringen(ActionListener al) {
        buttonGleich.addActionListener(al);
    }

    public void horcherAnButtonPlusAnbringen(ActionListener al) {
        buttonPlus.addActionListener(al);
    }

    // wird automatisch aufgerufen (ereigniseinfangende Methode),
    // wenn in TRModel ein Ereignisobjekt geworfen wird.
    public void update(Observable m, Object o) {
        if (m == trModel) {
            if (o.toString().equals("+")) {

            }
            if (o.toString().equals("set")) {
                setJTextField(String.valueOf(trModel.getSpeicherwert()));
            }
        }
    }
}

// C O N T R O L L E R
class TRController {

    private TRView trView;
    private TRModel trModel;

    public TRController() {
        trModel = new TRModel();
        trView = new TRView(trModel);
        addListener();
    }

    private void addListener() {
        this.trView.horcherAnButtonGleichAnbringen(new AdditionBerechnenListener(trModel, trView));
        this.trView.horcherAnButtonPlusAnbringen(new AdditionBerechnenListener(trModel, trView));
        this.trModel.addObserver(trView);
    }
}

class AdditionBerechnenListener implements ActionListener {

    private TRModel trModel;
    private TRView trView;

    public AdditionBerechnenListener(TRModel trModel, TRView trView) {
        this.trModel = trModel;
        this.trView = trView;
    }

    public void actionPerformed(ActionEvent e) {
        String str;
        str = e.getActionCommand();
        // Ereignis wird identifiziert
        if (str.equals("btn=")) {
            // veranlasst, dass die set-Methode des Model angesprochen wird und
            // deshalb ein Ereignis zur View sendet.
            double wert = this.trModel.getSpeicherwert();
            this.trModel.setSpeicherwert(wert);
        }

        if (str.equals("btn+")) {
            double wert = Double.valueOf(this.trView.getJTextField());
            this.trModel.addiereDazu(wert);
            wert = this.trModel.getSpeicherwert();
        }
    }
}
 
Zuletzt bearbeitet von einem Moderator:

mihe7

Top Contributor
was meinst du mit Domäne ?
Das ist der Problembereich.

Was meinst du mit "müssen aber die View nicht mehr kennen" ?
Die Abhängigkeiten bestehen nur über Schnittstellen, die konkreten Implementierungen sind unbekannt.

Gilt das auch (wenn ja, wo ?) für mein Programm ?
Das gilt für alle Programme. In der Regel sind View und Controller so eng miteinander verbunden, dass man meist darauf verzichtet, den Controller per Interface vom verwendeten Framework zu entkoppeln.

Dein Controller ist z. B. ein ActionListener (Swing). Dort wird eine Methode im Model aufgerufen. Ein anderes Framework wird keinen javax.swing.ActionListener verwenden. Daher musst Du den Listener neu schreiben. Der Listener muss irgendeine Methode aufrufen und ob das nun die Methode eines entkoppelten Controllers oder des Models ist, spielt keine Rolle.
 

ernst

Top Contributor
Das gilt für alle Programme. In der Regel sind View und Controller so eng miteinander verbunden, dass man meist darauf verzichtet, den Controller per Interface vom verwendeten Framework zu entkoppeln.
Dein Controller ist z. B. ein ActionListener (Swing).
Nein!
Siehe mein neues Programm in meinem letzten Posting:
Die Klasse Controller erbt weder von einer anderen Klasse noch implementiert sie ein Interface.

Dort wird eine Methode im Model aufgerufen.
Ja, es ist:
private void addListener() {
this.trView.horcherAnButtonGleichAnbringen(new AdditionBerechnenListener(trModel, trView));
this.trView.horcherAnButtonPlusAnbringen(new AdditionBerechnenListener(trModel, trView));
this.trModel.addObserver(trView);
}

Dort implementiert aber nur die Klasse AdditionBerechnenListener das Interface ActionListener und macht sich damit vom Framework Swing abhängig.
Von einem Framework mache ich mich aber immer abhängig. Wenn es nicht Swing ist, ist es eben ein anderes.
Was bringt es mir deshalb, wenn ich ein ein eigenes Interface bastle, wie z.B. das hier vorgeschlagene ViewListener?

Meine Hauptfrage:
Erfüllt das in meinem letzten Posting von mir vorgestellte Java-Programm die
MVC-Kriterien bzw. was kann man eurer Meinung daran noch verbessern bzw. was habt ihr daran auszusetzen?
Bevor ich ein weiteres Projekt mache, muss ich vorher eben wissen, ob ich "in der richtigen Spur bin".
Vielen Dank für euer feedback.

mfg
ern
 

mrBrown

Super-Moderator
Mitarbeiter
Nein!
Siehe mein neues Programm in meinem letzten Posting:
Die Klasse Controller erbt weder von einer anderen Klasse noch implementiert sie ein Interface.
„Controller“ ist bei MVC nicht genau eine Klasse, sondern eher als eine Art Package zu verstehen, genau wie Model und View.
Und in welches davon gehören wohl deine ActionListener? ;)
Außerdem: deine Controller-Klasse hängt direkt von deinen Swing-Actionlistenern ab, und damit natürlich auch von Swing.

Dort implementiert aber nur die Klasse AdditionBerechnenListener das Interface ActionListener und macht sich damit vom Framework Swing abhängig.
Doch, deine Listener implementieren ein Swing-Interface und machen sich damit von Swing abhängig.

Von einem Framework mache ich mich aber immer abhängig. Wenn es nicht Swing ist, ist es eben ein anderes.
Was bringt es mir deshalb, wenn ich ein ein eigenes Interface bastle, wie z.B. das hier vorgeschlagene ViewListener?
Aktuell sind View und Controller bei dir von dem verwendeten View-Framework abhängig. Wechselst du von Swing zu SWT, musst du alles außer das Model wegschmeißen.

Wenn du den Controller über ein eigenes Interface unabhängiger von der View machst, kannst du diesen mit verschiedenen View-Frameworks verwenden.
 
Zuletzt bearbeitet:

mihe7

Top Contributor
Die Klasse Controller erbt weder von einer anderen Klasse noch implementiert sie ein Interface.
Ich meinte allgemein folgende, regelmäßig zu findende Situation: button.addActionListener(e -> model.doSomething());

Hier spielt der Listener die Rolle eines Controllers und dabei wird nur eine Methode im Model aufgerufen. Du kannst jetzt natürlich hergehen und einen vom Framework unabhängigen Controller bauen, um diesen vom Framework zu entkoppeln:
Java:
class DoSomethingController {
    public void doSomething() {
        model.doSomething();
    }
}
Jetzt würde der von Swing abhängige ActionListener statt model.doSomething halt controller.doSometing aufrufen. Was hat man dadurch gewonnen? In dem Fall nichts, außer, dass man eine zusätzliche Klasse erstellt hat. Wenn Du von Swing auf ein anderes Framework wechselst, bleibt der Aufwand gleich groß, denn wenigstens eine Methode musst Du immer aufrufen.

Etwas anderes ist es, wenn der Controller mehr macht. Dann kann man sich zumindest überlegen, ob man den Controller vom Framework entkoppelt.
 

ernst

Top Contributor
Ich meinte allgemein folgende, regelmäßig zu findende Situation: button.addActionListener(e -> model.doSomething());
Leider kenne ich in Java nicht die Schreibweise mit dem Pfeil -->, also:
e -> model.doSomething())
Vermutlich hat das was mit dem Lambda-Kalkül zu tun.
Kannst du das bitte in "reinem" Java (ohne Lambda-Kalkül) formulieren ?

mfg
ern
 

mihe7

Top Contributor
Äquivalent zu:
Java:
button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        model.doSomething();
    }
});
 

ernst

Top Contributor
Aktuell sind View und Controller bei dir von dem verwendeten View-Framework abhängig. Wechselst du von Swing zu SWT, musst du alles außer das Model wegschmeißen.

Jetzt nicht mehr. Siehe Programm unten.
Ich muss den Controller und das Model nicht mehr ändern.
Und alles ohne Verwendung eines Interface.
Was gibt es da zu bemängeln ?

Java:
package demoreinesmvc_55;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.*;

public class Startklasse {

    public static void main(String[] args) {
        TRController trController = new TRController();
    }
}
// M O D E L
class TRModel extends Observable {

    private double speicherwert;

    public TRModel() {
        speicherwert = 0;
    }

    public double getSpeicherwert() {
        return speicherwert;
    }

    public void setSpeicherwert(double wert) {
        speicherwert = wert;
        // Dem Model das Feuern ermöglichen      
        setChanged();
        // der String dient zur Identifikation des Ereignisses
        notifyObservers("set");
    }

    public void addiereDazu(double wert) {
        speicherwert = speicherwert + wert;
        // Dem Model das Feuern ermöglichen
        setChanged();
        notifyObservers("+");
    }
}

// V I E W
class TRView extends JFrame implements Observer {

    private TRModel trModel;
    private JButton buttonPlus;
    private JButton buttonGleich;
    private JTextField tfdZahl;

    public TRView(TRModel trModel) {
        this.trModel = trModel;
        buttonsAnbringen();
        this.horcherAnButtonGleichAnbringen(new AdditionBerechnenListener(trModel, this));
        this.horcherAnButtonPlusAnbringen(new AdditionBerechnenListener(trModel, this));
    }

    public String getJTextField() {
        return this.tfdZahl.getText();
    }

    public void setJTextField(String str) {
        this.tfdZahl.setText(str);
    }

    public void buttonsAnbringen() {
        this.buttonPlus = new JButton("+");
        this.buttonGleich = new JButton("=");
        this.tfdZahl = new JTextField("  ", 30);

        // Beim Anklicken dieses Buttons wird ein Ereignis-
        // objekt geworfen, das durch den String "btn+"
        // identifiziert wird.
        this.buttonPlus.setActionCommand("btn+");
        // analaog
        this.buttonGleich.setActionCommand("btn=");

        getContentPane().add(buttonPlus, BorderLayout.EAST);
        getContentPane().add(buttonGleich, BorderLayout.WEST);
        getContentPane().add(tfdZahl, BorderLayout.CENTER);
        this.setSize(200, 100);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public void horcherAnButtonGleichAnbringen(ActionListener al) {
        buttonGleich.addActionListener(al);
    }

    public void horcherAnButtonPlusAnbringen(ActionListener al) {
        buttonPlus.addActionListener(al);
    }

    // wird automatisch aufgerufen (ereigniseinfangende Methode),
    // wenn in TRModel ein Ereignisobjekt geworfen wird.
    public void update(Observable m, Object o) {
        if (m == trModel) {
            if (o.toString().equals("+")) {

            }
            if (o.toString().equals("set")) {
                setJTextField(String.valueOf(trModel.getSpeicherwert()));
            }
        }
    }
}

// C O N T R O L L E R
class TRController {

    private TRView trView;
    private TRModel trModel;

    public TRController() {
        trModel = new TRModel();
        trView = new TRView(trModel);
        addListener();
    }

    private void addListener() {
        this.trModel.addObserver(trView);
    }
}

class AdditionBerechnenListener implements ActionListener {

    private TRModel trModel;
    private TRView trView;

    public AdditionBerechnenListener(TRModel trModel, TRView trView) {
        this.trModel = trModel;
        this.trView = trView;
    }

    public void actionPerformed(ActionEvent e) {
        String str;
        str = e.getActionCommand();
        // Ereignis wird identifiziert
        if (str.equals("btn=")) {
            // veranlasst, dass die set-Methode des Model angesprochen wird und
            // deshalb ein Ereignis zur View sendet.
            double wert = this.trModel.getSpeicherwert();
            this.trModel.setSpeicherwert(wert);
        }

        if (str.equals("btn+")) {
            double wert = Double.valueOf(this.trView.getJTextField());
            this.trModel.addiereDazu(wert);
            wert = this.trModel.getSpeicherwert();
        }
    }
}

Wenn du den Controller über ein eigenes Interface unabhängiger von der View machst, kannst du diesen mit verschiedenen View-Frameworks verwenden.
Das kapiere ich einfach nicht.
Soll ich jetzt auch ein eigenes Interface ViewListener erstellen und dann
ViewListener implementieren?
Also:
Controller implements ViewListener

mfg
ern
 
Zuletzt bearbeitet von einem Moderator:

ernst

Top Contributor
Hier spielt der Listener die Rolle eines Controllers und dabei wird nur eine Methode im Model aufgerufen. Du kannst jetzt natürlich hergehen und einen vom Framework unabhängigen Controller bauen, um diesen vom Framework zu entkoppeln:
Java:
class DoSomethingController {
    public void doSomething() {
        model.doSomething();
    }
}
Das Mitglied httpdigest hat beim Controller noch ein implements benutzt:
Controller implements ViewListener.
Brauchst du für deinen - vom Framework entkoppelten Controller - kein implements oder wie machst du das ?
Ich kann mir leider nicht vorstellen, wie ich eure Ideen in mein Programm einbauen kann.

mfg
ern
 

mrBrown

Super-Moderator
Mitarbeiter
Ich muss den Controller und das Model nicht mehr ändern.
AdditionBerechnenListener *ist* ein Teil des Controllers.

und AdditionBerechnenListener *ist* ein Swing-ActionListener.

Deine Klasse, die du Controller genannt hast, ist dagegen in der Form überflüssig. Das Zuweisen des Listeners kann der Listener selber machen und die View als Observer hinzufügen kann die View selber machen.
 

mihe7

Top Contributor
Ich kann mir leider nicht vorstellen, wie ich eure Ideen in mein Programm einbauen kann.
Erstmal nochmals der Hinweis: das ist alles kein Muss und wird meist auch nicht gemacht, weil es oft nur zu Mehraufwand ohne Nutzen führt.

Worum es geht ist folgendes: Du kannst einen Controller als Swing-ActionListener implementieren, dann hat dieser Controller aber das "Problem", von Swing abhängig zu sein. Stattdessen kannst Du auch hergehen und einfach ein eigenes Interface deklarieren, das nichts mit dem Framework zu tun hat, z. B. (da evtl. Kinder mitlesen: nicht zu Hause nachmachen)
Java:
public interface ActionController {
    public void performed(String action);
}
Wie Du siehst, hat dieses Interface keine Abhängigkeit mehr zu Swing. Es spielt für den Controller keine Rolle mehr, ob es Swing gibt oder nicht.
 

ernst

Top Contributor
Hallo allerseits,
habe das Programm nochmals (siehe unten) geändert und hoffe damit euren Ansprüchen ans MVC-Entwurfsmuster zu genügen.
Ich beschreibe die Problematik nochmals mit meinen Worten:
1)
Durch das Interface
public interface IFActionController {
public void performed(String action);
public void updateController(String action);
}
wird der Programmierer gezwungen, die Logik, die sich sonst in den
Horcher-Methoden befindet, in den Controller auszulagern.
Dort gehört die Logik hin!

2)
class AdditionBerechnenListener implements ActionListener
gehört zwar formal nicht zum Controller (ist nicht als Code in der Klasse
enthalten). Doch inhaltlich gehört AdditionBerechnenListener zum
"Packet" Controller.
Damit (implements ActionListener) ist der Controller Teil des
Swing-View-Framework und damit abhängig vom Swing-View-Framework.
Um diese Abhängigkeit zu verringern, wird das selbst geschriebene
Interface IFActionController verwendet.
Da die die Logik, die sich sonst in den Horcher-Methoden befinden würde,
in den Controller ausgelagert wird und sich somit der Code in den
Methoden (actionPerformed(ActionEvent e) und update(Observable m, Object o))
verringert, macht man sich unabhängiger vom View-Framework (wie z.B. Swing),
da man weniger Code austauschen muss (weniger Wartung).

3)
Man hat jetzt die Assoziationen (durch ein Attribut in der Klasse charakterisiert):
Controller --> View
Controller --> Model
View --> Model
View --> Controller


Ist das alles korrekt beschrieben, bzw. was gibt es an meinem Programm und meiner Beschreibung noch zu bemängeln ?


Java:
package demoreinesmvc_55;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.*;

public class Startklasse {

    public static void main(String[] args) {
        TRController trController = new TRController();
    }
}
// M O D E L
class TRModel extends Observable {

    private double speicherwert;

    public TRModel() {
        speicherwert = 0;
    }

    public double getSpeicherwert() {
        return speicherwert;
    }

    public void setSpeicherwert(double wert) {
        speicherwert = wert;
        // Dem Model das Feuern ermöglichen        
        setChanged();
        // der String dient zur Identifikation des Ereignisses
        notifyObservers("set");
    }

    public void addiereDazu(double wert) {
        speicherwert = speicherwert + wert;
        // Dem Model das Feuern ermöglichen
        setChanged();
        notifyObservers("+");
    }
}

// V I E W
class TRView extends JFrame implements Observer {
    private IFActionController ifController;
    private TRModel trModel;

    private JButton buttonPlus;
    private JButton buttonGleich;
    private JTextField tfdZahl;

    public TRView(TRModel trModel, IFActionController ifController) {
        this.ifController=ifController;        
        this.trModel = trModel;
        buttonsAnbringen();
    }

    public String getJTextField() {
        return this.tfdZahl.getText();
    }

    public void setJTextField(String str) {
        this.tfdZahl.setText(str);
    }

    public void buttonsAnbringen() {
        this.buttonPlus = new JButton("+");
        this.buttonGleich = new JButton("=");
        this.tfdZahl = new JTextField("  ", 30);

        // Beim Anklicken dieses Buttons wird ein Ereignis-
        // objekt geworfen, das durch den String "btn+" 
        // identifiziert wird.
        this.buttonPlus.setActionCommand("btn+");
        // analaog 
        this.buttonGleich.setActionCommand("btn=");

        getContentPane().add(buttonPlus, BorderLayout.EAST);
        getContentPane().add(buttonGleich, BorderLayout.WEST);
        getContentPane().add(tfdZahl, BorderLayout.CENTER);
        this.setSize(200, 100);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public void horcherAnButtonGleichAnbringen(ActionListener al) {
        buttonGleich.addActionListener(al);
    }

    public void horcherAnButtonPlusAnbringen(ActionListener al) {
        buttonPlus.addActionListener(al);
    }

    // wird automatisch aufgerufen (ereigniseinfangende Methode), 
    // wenn in TRModel ein Ereignisobjekt geworfen wird.
    public void update(Observable m, Object o) {
        if (m == trModel) {
            ifController.updateController(o.toString());
        }
    }
}

// C O N T R O L L E R 
class TRController implements IFActionController {

    private TRView trView;
    private TRModel trModel;

    public TRController() {
        trModel = new TRModel();
        trView = new TRView(trModel, this);
        addListener();
    }

    private void addListener() {
        this.trView.horcherAnButtonGleichAnbringen(new AdditionBerechnenListener(this));
        this.trView.horcherAnButtonPlusAnbringen(new AdditionBerechnenListener(this));
        this.trModel.addObserver(trView);
    }

    public void performed(String action) {
        // Ereignis wird identifiziert
        if (action.equals("btn=")) {
            // veranlasst, dass die set-Methode des Model angesprochen wird und 
            // deshalb ein Ereignis zur View sendet.
            double wert = this.trModel.getSpeicherwert();
            this.trModel.setSpeicherwert(wert);
        }

        if (action.equals("btn+")) {
            double wert = Double.valueOf(this.trView.getJTextField());
            this.trModel.addiereDazu(wert);
            wert = this.trModel.getSpeicherwert();
        }
    }

    public void updateController(String action) {
        if (action.equals("+")) {
        }
        if (action.equals("set")) {
            trView.setJTextField(String.valueOf(trModel.getSpeicherwert()));
        }
    }
}

class AdditionBerechnenListener implements ActionListener {

    private IFActionController ifController;

    public AdditionBerechnenListener(IFActionController ifController) {
        this.ifController = ifController;
    }

    public void actionPerformed(ActionEvent e) {
        String str;
        str = e.getActionCommand();
        ifController.performed(str);
    }
}
 

mrBrown

Super-Moderator
Mitarbeiter
Was immer noch schlecht ist:
Dein Controller kennt die View direkt, und ist damit wieder von der abhängig, also nichts gewonnen durch das Interface.

Deine View extended JFrame, das ist Unsinn und solltest du lassen ;)

Nicht die View sollte den Controller informieren, wenn sich das Model updated (vor allem nicht, um dann die View upzudaten), sondern sowohl Controller als auch View sind Observer und reagieren selbstständig auf Änderungen.
 

ernst

Top Contributor
Was immer noch schlecht ist:
Dein Controller kennt die View direkt, und ist damit wieder von der abhängig, also nichts gewonnen durch das Interface.
Meinst du wegen des Zugriffs auf
AdditionBerechnenListener (implements ActionListener) in der Anweisung:
this.trView.horcherAnButtonGleichAnbringen(new AdditionBerechnenListener(this));
Aber irgendwo muss ich die Abhängigkeit von ActionListener bringen, also:
class AdditionBerechnenListener implements ActionListener
Oder wie willst du das vermeiden bzw. was meinst du mit
"Dein Controller kennt die View direkt" ?

Deine View extended JFrame, das ist Unsinn und solltest du lassen ;)
Warum? Die View muss ja von irgendwo irgendwelche GUI-erzeugende Fähigkeiten erben. Warum nicht von JFrame ?
Wie willst du es sonst machen ?

Nicht die View sollte den Controller informieren, wenn sich das Model updated (vor allem nicht, um dann die View upzudaten), sondern sowohl Controller als auch View sind Observer und reagieren selbstständig auf Änderungen.
Warum ist meine Idee nicht MVC-konform:
In der View wird (z.B. durch Klick auf einen Button) ein Ereignis ausgelöst.
Die dieses Ereignis einfangende und sich im Controller befindliche Methode verändert das Modell, das dadurch selbst wieder ein Ereignis auslöst, das die View ändert.
Was ist an dieser Philosophie schlecht?

mfg
ern
 

mihe7

Top Contributor
euren Ansprüchen ans MVC-Entwurfsmuster
LOL. Kann sein, dass ich mich täusche, aber ich glaube, hier hat keiner wirklich harte Ansprüche an MVC. Worum es mir persönlich bei MVC geht: Trennung von View und Model. Logik habe ich, soweit möglich, im Model. Die Listener spielen bei mir die Rolle der Controller und es ist mir völlig wurscht, ob ich die irgendwann jemals wieder woanders verwenden kann oder nicht.

Es gibt auch verschiedene Varianten von MVC. Ich bevorzuge für Swing:
View --> Controller
View --> Model
Model --> View

Hier kennt die View nur die Controller in Form von Swing-Interfaces. Das Model ist bei mir ebenfalls entweder ein Swing-Interface oder ein eigenes. Die View hängt am Model via Observer-Pattern.

Warum? Die View muss ja von irgendwo irgendwelche GUI-erzeugende Fähigkeiten erben. Warum nicht von JFrame ?
Wenn Du eine Liste brauchst, schreibst Du dann auch eine Klasse, die von ArrayList erbt?
 

mrBrown

Super-Moderator
Mitarbeiter
"Dein Controller kennt die View direkt" ?
Der Controller hat ein Feld TRView, und hängt damit von der View ab. das führt das extra Interface für die Trennung von View und Controller ad absurdum.

Warum ist meine Idee nicht MVC-konform:
In der View wird (z.B. durch Klick auf einen Button) ein Ereignis ausgelöst.
Die dieses Ereignis einfangende und sich im Controller befindliche Methode verändert das Modell, das dadurch selbst wieder ein Ereignis auslöst, das die View ändert.
Was ist an dieser Philosophie schlecht?
Daran nichts, das meinte ich aber auch nicht.
Ich meinte ganz explizit die update-Methode in der View, die ruft den Controller auf, welcher dann wieder die View aufruft, also Model->View->Controller->View.
Ausreichen würde Model->View. Wenn die View weiß, dass das Model sich geändert hat, kann sie das auch anzeigen, genau das ist ja ihre Aufgabe.
Wenn man das unbedingt den Controller steuern lassen will, reicht ein Model->Controller->View, ist aber in dem Fall nicht nötig.
 

ernst

Top Contributor
Der Controller hat ein Feld TRView, und hängt damit von der View ab. das führt das extra Interface für die Trennung von View und Controller ad absurdum.
Es gibt verschiedene Arten der Abhängigkeiten: (A sei eine Klasse, a sei ein Objekt der Klasse A, B sei eine Klasse, b sei ein Objekt der Klasse B) von A und B
1)
A hat b als Attribut

2)
A hat eine Methode f(B b) wobei b ein Parameter von f ist

3)
A hat eine Methode f(....) wobei b eine lokale Variable von f ist.

x)
A implementiert ein Interface IFX
Dann besteht eine Abhängigkeit zwischen A und IFX.
Das ist mir allerdings nicht recht klar.


Wie willst du View und Controller komplett voneinander trennen ?

In meinem Programm unten habe ich die folgende Abhängigkeit "Der Controller hat ein Feld TRView, und hängt damit von der View ab" beseitigt.
Jetzt gibt es nur noch eine "kleinere" Abhängigkeit:
trView ist jetzt lokale Variable im Konstruktor vom Controller

Daran nichts, das meinte ich aber auch nicht.
Ich meinte ganz explizit die update-Methode in der View, die ruft den Controller auf, welcher dann wieder die View aufruft, also Model->View->Controller->View.
Ausreichen würde Model->View. Wenn die View weiß, dass das Model sich geändert hat, kann sie das auch anzeigen, genau das ist ja ihre Aufgabe.
Wenn man das unbedingt den Controller steuern lassen will, reicht ein Model->Controller->View, ist aber in dem Fall nicht nötig.
Ich dachte, daß jegliche Intelligenz einer Methode in eine Extra-Methode im Controller ausgelagert werden soll. Deswegen die Extramethoden:
performed(...) und updateController(...)
Ist das nicht nötig?
Übrigens:
Könnte man diese Extramethoden nicht auch in das Model auslagern ?

Ich habe jetzt so viel probiert und rumgemurkst (siehe Programm unten) und nichts war zielführend.
Deswegen bitte ich euch, mein Programm so umzugestalten, dass es MVC like wird und ich es dann hoffentlich anhand des Codes nachvollziehen kann.

mfg
ern



Java:
package demoreinesmvc_56;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.*;

public class Startklasse {

    public static void main(String[] args) {
        TRController trController = new TRController();
    }
}
// M O D E L

class TRModel extends Observable {

    private double speicherwert;

    public TRModel() {
        speicherwert = 0;
    }

    public double getSpeicherwert() {
        return speicherwert;
    }

    public void setSpeicherwert(double wert) {
        speicherwert = wert;
        // Dem Model das Feuern ermöglichen      
        setChanged();
        // der String dient zur Identifikation des Ereignisses
        notifyObservers("set");
    }

    public void addiereDazu(double wert) {
        speicherwert = speicherwert + wert;
        // Dem Model das Feuern ermöglichen
        setChanged();
        notifyObservers("+");
    }
}

// V I E W
//class TRView extends JFrame implements Observer {
class TRView implements Observer {  
    private IFActionController ifController;
    private TRModel trModel;

    private JButton buttonPlus;
    private JButton buttonGleich;
    private JTextField tfdZahl;

    public TRView(TRModel trModel, IFActionController ifController) {
        this.ifController=ifController;      
        this.trModel = trModel;
        buttonsAnbringen();
        trModel.addObserver(this);      
    }

    public String getJTextField() {
        return this.tfdZahl.getText();
    }

    public void setJTextField(String str) {
        this.tfdZahl.setText(str);
    }

    public void buttonsAnbringen() {
   JFrame frame = new JFrame("JFrame mit Taschenrechner");      
        this.buttonPlus = new JButton("+");
        this.buttonGleich = new JButton("=");
        this.tfdZahl = new JTextField("  ", 30);

        // Beim Anklicken dieses Buttons wird ein Ereignis-
        // objekt geworfen, das durch den String "btn+"
        // identifiziert wird.
        this.buttonPlus.setActionCommand("btn+");
        // analaog
        this.buttonGleich.setActionCommand("btn=");

        frame.getContentPane().add(buttonPlus, BorderLayout.EAST);
        frame.getContentPane().add(buttonGleich, BorderLayout.WEST);
        frame.getContentPane().add(tfdZahl, BorderLayout.CENTER);
        frame.setSize(200, 100);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public void horcherAnButtonGleichAnbringen(ActionListener al) {
        buttonGleich.addActionListener(al);
    }

    public void horcherAnButtonPlusAnbringen(ActionListener al) {
        buttonPlus.addActionListener(al);
    }

    // wird automatisch aufgerufen (ereigniseinfangende Methode),
    // wenn in TRModel ein Ereignisobjekt geworfen wird.
    public void update(Observable m, Object o) {
        if (m == trModel) {
            ifController.updateController(o.toString());
        }
    }
}

// C O N T R O L L E R
class TRController implements IFActionController {

    private TRView trView;
    private TRModel trModel;

    public TRController() {
        trModel = new TRModel();
        trView = new TRView(trModel, this);
        addListener();
    }

    private void addListener() {
        this.trView.horcherAnButtonGleichAnbringen(new AdditionBerechnenListener(this));
        this.trView.horcherAnButtonPlusAnbringen(new AdditionBerechnenListener(this));
//        this.trModel.addObserver(trView);
    }

    public void performed(String action) {
        // Ereignis wird identifiziert
        if (action.equals("btn=")) {
            // veranlasst, dass die set-Methode des Model angesprochen wird und
            // deshalb ein Ereignis zur View sendet.
            double wert = this.trModel.getSpeicherwert();
            this.trModel.setSpeicherwert(wert);
        }

        if (action.equals("btn+")) {
            double wert = Double.valueOf(this.trView.getJTextField());
            this.trModel.addiereDazu(wert);
            wert = this.trModel.getSpeicherwert();
        }
    }

    public void updateController(String action) {
        if (action.equals("+")) {
        }
        if (action.equals("set")) {
            trView.setJTextField(String.valueOf(trModel.getSpeicherwert()));
        }
    }
}

class AdditionBerechnenListener implements ActionListener {

    private IFActionController ifController;

    public AdditionBerechnenListener(IFActionController ifController) {
        this.ifController = ifController;
    }

    public void actionPerformed(ActionEvent e) {
        String str;
        str = e.getActionCommand();
        ifController.performed(str);
    }
}
 

ernst

Top Contributor
>
>Warum? Die View muss ja von irgendwo irgendwelche GUI-erzeugende
>Fähigkeiten erben. Warum nicht von JFrame ?
>
Wenn Du eine Liste brauchst, schreibst Du dann auch eine Klasse, die von ArrayList erbt?
Du hast recht. Habe das in meinem neuen Programm geändert.

mfg
ern
 

mrBrown

Super-Moderator
Mitarbeiter
Es gibt verschiedene Arten der Abhängigkeiten: (A sei eine Klasse, a sei ein Objekt der Klasse A, B sei eine Klasse, b sei ein Objekt der Klasse B) von A und B
1)
A hat b als Attribut

2)
A hat eine Methode f(B b) wobei b ein Parameter von f ist

3)
A hat eine Methode f(....) wobei b eine lokale Variable von f ist.

x)
A implementiert ein Interface IFX
Dann besteht eine Abhängigkeit zwischen A und IFX.
Das ist mir allerdings nicht recht klar.


Wie willst du View und Controller komplett voneinander trennen ?
Dein ursprünglicher Controller hatte ein Feld vom Typ deiner View, und diese View ist von Swing abhängig, damit hängt Transitiv auch der Controller von Swing ab (aktuell übrigens auch, der Controller ist immer noch hart von der View abhängig, welche hart von Swing abhängt).

Trennen kann man das, wenn die View ein Interface bekommt. Der Controller kennt dann nur noch das Interface, und ob das eine Swing-, SWT- oder JavaFX-View ist, ist dem egal.
Ich dachte, daß jegliche Intelligenz einer Methode in eine Extra-Methode im Controller ausgelagert werden soll. Deswegen die Extramethoden:
Je nachdem wie das gemeint ist: Ja.
Aber deine update-Methode ist weder intelligent noch sonst irgendwas. Die einzige Aufgabe ist es, die View über Änderungen zu informieren, und dafür braucht es den Controller nicht.

performed(...) und updateController(...)
Ist das nicht nötig?
Übrigens:
Könnte man diese Extramethoden nicht auch in das Model auslagern ?
performed() ist sinnvoll, das ist genau das, was der Controller machen sollte.

Wenn du sie in das Model auslagerst, hast du wieder kein MVC, weil der Controller dann nicht existiert.




Hier einmal deine Version in etwas angepasst.

View und Controller sind über Interfaces getrennt, die View updated sich selbst, der Controller behandelt Ereignisse und updated das Model.

Java:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.*;

public class Startklasse {

    public static void main(String[] args) {
        TRModel trModel = new TRModel();
        TRView trView = new TRView(trModel);
        TRController trController = new TRController(trModel, trView);
    }
}
// M O D E L

class TRModel extends Observable {

    private double speicherwert;

    public TRModel() {
        speicherwert = 0;
    }

    public double getSpeicherwert() {
        return speicherwert;
    }

    public void setSpeicherwert(double wert) {
        speicherwert = wert;
        // Dem Model das Feuern ermöglichen
        setChanged();
        // der String dient zur Identifikation des Ereignisses
        notifyObservers("set");
    }

    public void addiereDazu(double wert) {
        speicherwert = speicherwert + wert;
        // Dem Model das Feuern ermöglichen
        setChanged();
        notifyObservers("+");
    }
}

// V I E W

interface ViewInterface {
    String getValue();

    void horcherAnButtonGleichAnbringen(ActionListener al);

    void horcherAnButtonPlusAnbringen(ActionListener al);
}

class TRView implements Observer, ViewInterface {

    private TRModel trModel;

    private JButton buttonPlus;

    private JButton buttonGleich;

    private JTextField tfdZahl;

    public TRView(TRModel trModel) {
        this.trModel = trModel;
        buttonsAnbringen();
        trModel.addObserver(this);
    }

    public String getJTextField() {
        return this.tfdZahl.getText();
    }

    public void setJTextField(String str) {
        this.tfdZahl.setText(str);
    }

    public void buttonsAnbringen() {
        JFrame frame = new JFrame("JFrame mit Taschenrechner");
        this.buttonPlus = new JButton("+");
        this.buttonGleich = new JButton("=");
        this.tfdZahl = new JTextField("  ", 30);

        // Beim Anklicken dieses Buttons wird ein Ereignis-
        // objekt geworfen, das durch den String "btn+"
        // identifiziert wird.
        this.buttonPlus.setActionCommand("btn+");
        // analaog
        this.buttonGleich.setActionCommand("btn=");

        frame.getContentPane().add(buttonPlus, BorderLayout.EAST);
        frame.getContentPane().add(buttonGleich, BorderLayout.WEST);
        frame.getContentPane().add(tfdZahl, BorderLayout.CENTER);
        frame.setSize(200, 100);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    @Override
    public void horcherAnButtonGleichAnbringen(ActionListener al) {
        buttonGleich.addActionListener(al);
    }

    @Override
    public void horcherAnButtonPlusAnbringen(ActionListener al) {
        buttonPlus.addActionListener(al);
    }

    /**
     * View updated sich selbstständig.
     */
    @Override
    public void update(Observable m, Object o) {
        if (m == trModel) {
            this.setJTextField(String.valueOf(trModel.getSpeicherwert()));
        }
    }

    @Override
    public String getValue() {
        return this.tfdZahl.getText();
    }
}

// C O N T R O L L E R

interface IFActionController {
    void performed(String action);
}

class TRController implements IFActionController {

    private ViewInterface trView;

    private TRModel trModel;

    public TRController(TRModel trModel, TRView trView) {
        this.trModel = trModel;
        this.trView = trView;
        addListener();
    }

    private void addListener() {
        this.trView.horcherAnButtonGleichAnbringen(new AdditionBerechnenListener(this));
        this.trView.horcherAnButtonPlusAnbringen(new AdditionBerechnenListener(this));
    }

    public void performed(String action) {
        // Ereignis wird identifiziert
        if (action.equals("btn=")) {
            // veranlasst, dass die set-Methode des Model angesprochen wird und
            // deshalb ein Ereignis zur View sendet.
            double wert = this.trModel.getSpeicherwert();
            this.trModel.setSpeicherwert(wert);
        }

        if (action.equals("btn+")) {
            double wert = Double.valueOf(this.trView.getValue());
            this.trModel.addiereDazu(wert);
        }
    }
}

class AdditionBerechnenListener implements ActionListener {

    private IFActionController ifController;

    public AdditionBerechnenListener(IFActionController ifController) {
        this.ifController = ifController;
    }

    public void actionPerformed(ActionEvent e) {
        String str = e.getActionCommand();
        ifController.performed(str);
    }
}
 

ernst

Top Contributor
Hallo allerseits,
habe wieder eine neue Version.
Ist dies nun ein sauberes MVC-Design bzw. was kann man besser machen ?

Java:
package demoreinesmvc_58;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.*;

public class Startklasse {

    public static void main(String[] args) {
        TRController trController = new TRController();
    }
}
// M O D E L
class TRModel extends Observable {

    private double speicherwert;

    public TRModel() {
        speicherwert = 0;
    }

    public double getSpeicherwert() {
        return speicherwert;
    }

    public void setSpeicherwert(double wert) {
        speicherwert = wert;
        // Dem Model das Feuern ermöglichen       
        setChanged();
        // der String dient zur Identifikation des Ereignisses
        notifyObservers("set");
    }

    public void addiereDazu(double wert) {
        speicherwert = speicherwert + wert;
        // Dem Model das Feuern ermöglichen
        setChanged();
        notifyObservers("+");
    }
}


class TRView implements Observer, IFView {
    private IFController trController;
    private TRModel trModel;
    private JButton buttonPlus;
    private JButton buttonGleich;
    private JTextField tfdZahl;

    public TRView(TRModel trModel, IFController trController) {
        this.trModel = trModel;
        this.trController=trController;
       
        buttonsAnbringen();
        this.trModel.addObserver(this);               
        this.horcherAnButtonGleichAnbringen(new AdditionBerechnenListener(trController));
        this.horcherAnButtonPlusAnbringen(new AdditionBerechnenListener(trController));
       
    }

    public String getJTextField() {
        return this.tfdZahl.getText();
    }

    public void setJTextField(String str) {
        this.tfdZahl.setText(str);
    }

    public void buttonsAnbringen() {
   JFrame frame = new JFrame("JFrame mit Taschenrechner");       
        this.buttonPlus = new JButton("+");
        this.buttonGleich = new JButton("=");
        this.tfdZahl = new JTextField("  ", 30);

        // Beim Anklicken dieses Buttons wird ein Ereignis-
        // objekt geworfen, das durch den String "btn+"
        // identifiziert wird.
        this.buttonPlus.setActionCommand("btn+");
        // analaog
        this.buttonGleich.setActionCommand("btn=");

        frame.getContentPane().add(buttonPlus, BorderLayout.EAST);
        frame.getContentPane().add(buttonGleich, BorderLayout.WEST);
        frame.getContentPane().add(tfdZahl, BorderLayout.CENTER);
        frame.setSize(200, 100);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public void horcherAnButtonGleichAnbringen(ActionListener al) {
        buttonGleich.addActionListener(al);
    }

    public void horcherAnButtonPlusAnbringen(ActionListener al) {
        buttonPlus.addActionListener(al);
    }

    // wird automatisch aufgerufen (ereigniseinfangende Methode),
    // wenn in TRModel ein Ereignisobjekt geworfen wird.
    public void update(Observable m, Object o) {
        if (m == trModel) {
            if (o.toString().equals("+")) {

            }
            if (o.toString().equals("set")) {
                setJTextField(String.valueOf(trModel.getSpeicherwert()));
            }
        }
    }
}

// C O N T R O L L E R
class TRController implements IFController {
    private IFView trView;   
    private TRModel trModel;

    public TRController() {
        trModel = new TRModel();
        trView = new TRView(trModel, this);
    }
   
   
    public void performed(String action) {
        // Ereignis wird identifiziert
        if (action.equals("btn=")) {
            // veranlasst, dass die set-Methode des Model angesprochen wird und
            // deshalb ein Ereignis zur View sendet.
            double wert = this.trModel.getSpeicherwert();
            this.trModel.setSpeicherwert(wert);
        }

        if (action.equals("btn+")) {
            double wert = Double.valueOf(this.trView.getJTextField());
            this.trModel.addiereDazu(wert);
            wert = this.trModel.getSpeicherwert();
        }
    }
   
}

class AdditionBerechnenListener implements ActionListener {
    private IFController trController;

    public AdditionBerechnenListener(IFController trController) {
        this.trController=trController;
    }
   
   
   
    public void actionPerformed(ActionEvent e) {
        String str;
        str = e.getActionCommand();
        trController.performed(str);
    }
   
}
 

mrBrown

Super-Moderator
Mitarbeiter
Der Controller kennt immer noch dieses eine View-Klasse (er instanziiert sie ja) ;)

Im Model kannst du es dir meist sparen, der notifyObservers-Methode irgendwas mitzugeben.

Ansonsten sieht es im wesentlichen passend aus
 

ernst

Top Contributor
Der Controller kennt immer noch dieses eine View-Klasse (er instanziiert sie ja) ;)
"Wenn du MVC ganz stringend implementieren willst, darf der Controller nicht von View-Spezifika abhängen. Also, die Tatsache, dass der Controller schon ein Swing-spezifisches ActionListener Interface implementiert, verletzt das MVC Design, dass nämlich theoretisch die View austauschbar sein soll, ohne den Controller zu ändern oder auszutauschen. Wenn du also irgendwann beschließt, nicht mehr Swing als View zu verwenden, sondern SWT, hättest du ein Problem."

1)
Warum muss ich den Controller ändern, wenn ich nicht mehr Swing als View verwende (sondern z.B. SWT) ?

2)
Programmausschnitt
================================
class TRController implements IFController {
private IFView trView;
....
==================================

Mein Controller kennt nicht die View-Klasse trView, sondern das Interface trView.
Aber selbst wenn dem so wäre:
Warum muss ich den Controller ändern, wenn ich nicht mehr Swing als View verwende (sondern z.B. SWT) ?

3)
Welches MVC Design wird bei meinem Programm verletzt ?

mfg
ern
 

mrBrown

Super-Moderator
Mitarbeiter
1)
Warum muss ich den Controller ändern, wenn ich nicht mehr Swing als View verwende (sondern z.B. SWT) ?
Wenn du von Swing zu SWT änderst, schreibst du eine neue View. Da der Controller grad die View kennt, musst du diesen auch ändern.

Mein Controller kennt nicht die View-Klasse trView, sondern das Interface trView.
Dein Controller ruft den View-Konstruktor auf.
Schieb mal alles in ein eigenes Package (ein view, ein swing_view, ein controller, ein controller_impl und ein model-Package) und schau dir die Imports an.

Welches MVC Design wird bei meinem Programm verletzt ?
Nichts Wesentliches.
Der oben genannte Punkt kann noch aufgelöst werden und ebenso kann man das erstellen des Models aus dem Controller nehmen.
 

ernst

Top Contributor
Wenn du von Swing zu SWT änderst, schreibst du eine neue View. Da der Controller grad die View kennt, musst du diesen auch ändern.
Dein Controller ruft den View-Konstruktor auf.
Der oben genannte Punkt kann noch aufgelöst werden und ebenso kann man das erstellen des Models aus dem Controller nehmen.
Zeig mir bitte, wie das konkret implementiert wird.
Ich kann es mir nicht vorstellen und werde es auch nicht ohne Hilfe lösen können.
Verändere bitte meinen Quellcode so, dass er deinen Ansprüchen genügt.
Vielen Dank dafür.

mfg
ern
 

ernst

Top Contributor
Dein ursprünglicher Controller hatte ein Feld vom Typ deiner View, und diese View ist von Swing abhängig, damit hängt Transitiv auch der Controller von Swing ab (aktuell übrigens auch, der Controller ist immer noch hart von der View abhängig, welche hart von Swing abhängt).

Trennen kann man das, wenn die View ein Interface bekommt. Der Controller kennt dann nur noch das Interface, und ob das eine Swing-, SWT- oder JavaFX-View ist, ist dem egal.
Außerdem Zitat in diesem Thread:
"Wenn du MVC ganz stringend implementieren willst, darf der Controller nicht von View-Spezifika abhängen."

Habe mir nochmals Gedanken zu dem Thema gemacht. Zu deinem Vorschlag im Spoiler:
Im Controller kommt folgender Code vor:
private void addListener() {
this.trView.horcherAnButtonGleichAnbringen(new AdditionBerechnenListener(this));
...

1) Wenn sich die View ändert und im Quellcode der View kommt nicht mehr die Methode horcherAnButtonGleichAnbringen(...) vor (bzw. die Anzahl der Parameter darin hat sich geändert), dann kann diese Methode nicht mehr im Cobtroller verwendet werden. Damit hängt der Controller von der View ab.
Das verstößt doch gegen die MVC-Regeln, oder ?

2) Im Quellcode des Controllers kommt die Methode AdditionBerechnenListener(...) vor.
Die Klasse AdditionBerechnenListener hat das Interface ActionListener. Dieses ist ein Element von Swing.
Damit hängt der Controller von der View ab (also speziell von Swing).
Das verstößt doch gegen die MVC-Regeln, oder ?

mfg
Ernst




Hier einmal deine Version in etwas angepasst.

View und Controller sind über Interfaces getrennt, die View updated sich selbst, der Controller behandelt Ereignisse und updated das Model.

Java:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.*;

public class Startklasse {

    public static void main(String[] args) {
        TRModel trModel = new TRModel();
        TRView trView = new TRView(trModel);
        TRController trController = new TRController(trModel, trView);
    }
}
// M O D E L

class TRModel extends Observable {

    private double speicherwert;

    public TRModel() {
        speicherwert = 0;
    }

    public double getSpeicherwert() {
        return speicherwert;
    }

    public void setSpeicherwert(double wert) {
        speicherwert = wert;
        // Dem Model das Feuern ermöglichen
        setChanged();
        // der String dient zur Identifikation des Ereignisses
        notifyObservers("set");
    }

    public void addiereDazu(double wert) {
        speicherwert = speicherwert + wert;
        // Dem Model das Feuern ermöglichen
        setChanged();
        notifyObservers("+");
    }
}

// V I E W

interface ViewInterface {
    String getValue();

    void horcherAnButtonGleichAnbringen(ActionListener al);

    void horcherAnButtonPlusAnbringen(ActionListener al);
}

class TRView implements Observer, ViewInterface {

    private TRModel trModel;

    private JButton buttonPlus;

    private JButton buttonGleich;

    private JTextField tfdZahl;

    public TRView(TRModel trModel) {
        this.trModel = trModel;
        buttonsAnbringen();
        trModel.addObserver(this);
    }

    public String getJTextField() {
        return this.tfdZahl.getText();
    }

    public void setJTextField(String str) {
        this.tfdZahl.setText(str);
    }

    public void buttonsAnbringen() {
        JFrame frame = new JFrame("JFrame mit Taschenrechner");
        this.buttonPlus = new JButton("+");
        this.buttonGleich = new JButton("=");
        this.tfdZahl = new JTextField("  ", 30);

        // Beim Anklicken dieses Buttons wird ein Ereignis-
        // objekt geworfen, das durch den String "btn+"
        // identifiziert wird.
        this.buttonPlus.setActionCommand("btn+");
        // analaog
        this.buttonGleich.setActionCommand("btn=");

        frame.getContentPane().add(buttonPlus, BorderLayout.EAST);
        frame.getContentPane().add(buttonGleich, BorderLayout.WEST);
        frame.getContentPane().add(tfdZahl, BorderLayout.CENTER);
        frame.setSize(200, 100);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    @Override
    public void horcherAnButtonGleichAnbringen(ActionListener al) {
        buttonGleich.addActionListener(al);
    }

    @Override
    public void horcherAnButtonPlusAnbringen(ActionListener al) {
        buttonPlus.addActionListener(al);
    }

    /**
     * View updated sich selbstständig.
     */
    @Override
    public void update(Observable m, Object o) {
        if (m == trModel) {
            this.setJTextField(String.valueOf(trModel.getSpeicherwert()));
        }
    }

    @Override
    public String getValue() {
        return this.tfdZahl.getText();
    }
}

// C O N T R O L L E R

interface IFActionController {
    void performed(String action);
}

class TRController implements IFActionController {

    private ViewInterface trView;

    private TRModel trModel;

    public TRController(TRModel trModel, TRView trView) {
        this.trModel = trModel;
        this.trView = trView;
        addListener();
    }

    private void addListener() {
        this.trView.horcherAnButtonGleichAnbringen(new AdditionBerechnenListener(this));
        this.trView.horcherAnButtonPlusAnbringen(new AdditionBerechnenListener(this));
    }

    public void performed(String action) {
        // Ereignis wird identifiziert
        if (action.equals("btn=")) {
            // veranlasst, dass die set-Methode des Model angesprochen wird und
            // deshalb ein Ereignis zur View sendet.
            double wert = this.trModel.getSpeicherwert();
            this.trModel.setSpeicherwert(wert);
        }

        if (action.equals("btn+")) {
            double wert = Double.valueOf(this.trView.getValue());
            this.trModel.addiereDazu(wert);
        }
    }
}

class AdditionBerechnenListener implements ActionListener {

    private IFActionController ifController;

    public AdditionBerechnenListener(IFActionController ifController) {
        this.ifController = ifController;
    }

    public void actionPerformed(ActionEvent e) {
        String str = e.getActionCommand();
        ifController.performed(str);
    }
}
[/QUOTE]
 

mrBrown

Super-Moderator
Mitarbeiter
1) Wenn sich die View ändert und im Quellcode der View kommt nicht mehr die Methode horcherAnButtonGleichAnbringen(...) vor (bzw. die Anzahl der Parameter darin hat sich geändert), dann kann diese Methode nicht mehr im Cobtroller verwendet werden. Damit hängt der Controller von der View ab.
Das verstößt doch gegen die MVC-Regeln, oder ?
Grundsätzlich hängt der natürlich immer von der View ab, da er ja auf Ereignisse aus dieser reagieren muss. Wenn in der View die Buttons und damit die möglichen Aktionen geändert werden, muss der Controller natürlich angepasst werden, dass ist ja auch absolut kein Problem.
Der wesentliche Punkt ist aber, dass der Controller nicht mehr von der Implementierung der View abhängt. Wie der Button aussieht, ob das überhaupt ein Button ist oder einfach durch einen Tastendruck ausgelöst wird, etc, weiß der Controller nicht - der weiß nur, das diese Aktion irgendwie ausgelöst werden kann. (Der Name ist natürlich noch sehr schlecht gewählt, den würde man abstrakter wählen.)

2) Im Quellcode des Controllers kommt die Methode AdditionBerechnenListener(...) vor.
Die Klasse AdditionBerechnenListener hat das Interface ActionListener. Dieses ist ein Element von Swing.
Damit hängt der Controller von der View ab (also speziell von Swing).
Das verstößt doch gegen die MVC-Regeln, oder ?
Da hast du Recht, im Idealfall würde man spezifische Interfaces dafür einführen :)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
B Swing setDefaultButton geht nicht - Komponente fängt Events ab AWT, Swing, JavaFX & SWT 5
P Erstellung eines GUI. Wie fängt man an? AWT, Swing, JavaFX & SWT 6
G listener repaint() - verschiedene Darstellung AWT, Swing, JavaFX & SWT 24
E verschiedene Cursor setzen AWT, Swing, JavaFX & SWT 1
I GUI, das verschiedene Panels hat (Von Seite zu Seite navigieren) AWT, Swing, JavaFX & SWT 7
RedEagle JavaFX Verschiedene Bereiche miteinander verknüpfen AWT, Swing, JavaFX & SWT 7
H 3 verschiedene Nachrichten in einer FXML View die Infos kommen aus DB AWT, Swing, JavaFX & SWT 4
S JavaFX Optimierung für verschiedene Auflösungen AWT, Swing, JavaFX & SWT 12
Bartertown JTable - Verschiedene Komponenten in einer Spalte unterbringen AWT, Swing, JavaFX & SWT 3
H Verschiedene JPanels aus eigenen Klassen in JFrame einfügen, nur ein Panel sichtbar AWT, Swing, JavaFX & SWT 4
V Swing Nimbus Look And Feel verschiedene Buttons AWT, Swing, JavaFX & SWT 8
W Verschiedene Foreground color in Combo SWT Auswahlliste AWT, Swing, JavaFX & SWT 5
L AWT Window, Dialog und verschiedene Betriebssysteme AWT, Swing, JavaFX & SWT 2
M 3D-Grafik verschiedene Texturen auf einen Würfel mappen AWT, Swing, JavaFX & SWT 15
T Swing verschiedene Auflösungen AWT, Swing, JavaFX & SWT 7
J 3D-Grafik JOGL - Verschiedene Perspektiven darstellen AWT, Swing, JavaFX & SWT 5
R JTree - verschiedene Verzeichnisse AWT, Swing, JavaFX & SWT 3
T AWT verschiedene Oberflächen programmieren AWT, Swing, JavaFX & SWT 5
D verschiedene Zeichenebenen AWT, Swing, JavaFX & SWT 10
M Verschiedene Layouts anzeigen AWT, Swing, JavaFX & SWT 5
Dit_ Verschiedene Icons in einer JTable-Zelle AWT, Swing, JavaFX & SWT 2
H Swing TableCellRenderer für verschiedene Spalten AWT, Swing, JavaFX & SWT 11
N Verschiedene FormLayout in einem Panelbuilder AWT, Swing, JavaFX & SWT 3
D Verschiedene ActionEvents für ein JButton-Array AWT, Swing, JavaFX & SWT 2
M Swing Swing, MVC über verschiedene Panel AWT, Swing, JavaFX & SWT 4
P verschiedene JPanels einblenden je nach auswahl AWT, Swing, JavaFX & SWT 2
L Focus für KeyListener auf verschiedene Panels AWT, Swing, JavaFX & SWT 2
K Verschiedene ComboBoxen in JTable AWT, Swing, JavaFX & SWT 9
D 2 verschiedene Frames mit einem Button schliessen AWT, Swing, JavaFX & SWT 2
D Mit der Maus in verschiedene Modi wechseln AWT, Swing, JavaFX & SWT 15
I JTable: "GLEICHZEITIG" zwei verschiedene TableMode AWT, Swing, JavaFX & SWT 2
F GridLayout: verschiedene Spaltenbreiten AWT, Swing, JavaFX & SWT 2
S verschiedene Menuleisten AWT, Swing, JavaFX & SWT 2
S In einem button Panel 2 verschiedene TiteledBoarder erzeugen AWT, Swing, JavaFX & SWT 5
T verschiedene Farben in einem Text/Editfeld AWT, Swing, JavaFX & SWT 11
R Fenster in verschiedene Bereiche teilen - aber wie? AWT, Swing, JavaFX & SWT 4
D Mehrere verschiedene Textfelder auslesen? AWT, Swing, JavaFX & SWT 10
Icewind Verschiedene Farben in einer JTextArea AWT, Swing, JavaFX & SWT 3
F Verschiedene Vordergrundfarben in einer TextArea AWT, Swing, JavaFX & SWT 4
G JFileChooser in verschiedene Sprachen? AWT, Swing, JavaFX & SWT 6
E Prinzip: wie man Ereignisse in einer GUI verarbeit. Frage zum Design? AWT, Swing, JavaFX & SWT 10
P JavaFX Kalender mit Kacheln für Ereignisse AWT, Swing, JavaFX & SWT 4

Ähnliche Java Themen

Neue Themen


Oben