Swing MVC Architektur Umsetzung

babuschka

Top Contributor
Hallo,

ich habe eine GUI-Anwendung geschrieben, die ein JTabbedPane integriert. Um den Code übersichtlich zu halten und inhaltlich voneinander abzugrenzen, stellt jeder Tab eine neue View mit eigenem Controller und Model dar. Die aktuelle Umsetzung der MVC Architektur ist unglücklich, da ich mit einem Controller ausschließlich auf die damit assoziierte View und Model zugreifen kann. Ich möchte mit beliebigem Controller, auf beliebige Views zugreifen.

Beispielklasse "MainFrame":

Java:
public class MainFrame extends JFrame {

    private JTabbedPane tabbedPane;
    private JButton btnCancel;

    public MainFrame() {
        this.setTitle("Beispielanwendung");
        this.contentPane = new JPanel();
        this.tabbedPane = new JTabbedPane(JTabbedPane.LEFT);
        this.contentPane.add(this.tabbedPane, BorderLayout.CENTER);
        this.btnCancel = new JButton("Cancel");

        TabModelA tabModelA = new TabModelA();
        TabViewA tabViewA = new TabViewA();
        TabControllerA tabControllerA = new TabControllerA(tabModelA, tabViewA);
        this.tabbedPane.addTab("Tab A", new JScrollPane(tabViewA));
    
        TabModelB tabModelB = new TabModelB();
        TabViewB tabViewB = new TabViewB();
        TabControllerB tabControllerB = new TabControllerB(tabModelB, tabViewB);
        this.tabbedPane.addTab("Tab B", new JScrollPane(tabViewB));

        // TODO Instanziierung weiterer Tabs.
    }

    public void addBtnCancelListener(ActionListener l) {
        this.btnCancel.addActionListener(l);
    }

}

Beispielklasse "MainController":

Java:
public class MainController {
	
    private MainModel mainModel;
    private MainFrame mainFrame;

    public MainController(MainModel mainModel, MainFrame mainFrame) {
        this.mainModel = mainModel;
        this.mainFrame = mainFrame;
        addListener();
    }

    public void addListener() {
        this.mainFrame.addBtnCancelListener(new BtnCancelListener());
    }

    class BtnCancelListener implements ActionListener {
        public void actionPerformed(ActionEvent e) { 
            mainFrame.dispose();
        }
    }

}

Beispielklasse "TabViewA" (analog "TabViewB"):

Java:
public class TabViewA extends JPanel {
    // TODO Initialisierung verschiedener GUI-Elemente (Labels, Buttons, ...).
}

Beispielklasse "TabControllerA" (analog "TabControllerB"):

Java:
public class TabControllerA {

    private TabModelA tabModelA;
    private TabViewA tabViewA;

    public TabControllerA(TabModelA tabModelA, TabViewA tabViewA) {
        this.tabModelA = tabModelA;
        this.tabViewA = tabViewA;
        addListener();
    }

    public void addListener() {
        // TODO Listener hinzufügen.
    }

    // TODO Interne Klassen, die das Interface "ActionListener" implementieren.

}

Die Controller der Tabs A und B können folglich ausschließlich auf GUI-Elemente der mit ihnen verknüpften View zugreifen. Wie ist es möglich, auf Views anderer Tabs oder auf den des MainFrame zuzugreifen, ohne diese dem jeweiligen Controller-Konstruktor zu übergeben (bei zehn Tabs würden zehn zusätzliche Views übergeben = unschön!)? Wie werden in einer größeren Anwendung die verschiedenen Views sowie Controller miteinander verknüpft? Ich habe im Internet kein Tutorial gefunden, dass MVC an mehreren (ineinander verschachtelten) Views erklärt.

Vielen Dank für eure Bemühungen!
 

Marc T.

Bekanntes Mitglied
Edit: Warte mal:

Warum willst du mit einem für die eine View spezialisierten Controller
auf eine andere View zugreifen? Genau weil du das nicht möchtest
hast du doch die Trennung drin oder nicht?
 

babuschka

Top Contributor
Edit: Warte mal:

Warum willst du mit einem für die eine View spezialisierten Controller
auf eine andere View zugreifen? Genau weil du das nicht möchtest
hast du doch die Trennung drin oder nicht?

Genau, aber es gibt Ausnahmen. Für das korrekte Placement eines Dialogs beispielsweise ist ein Verweis auf das Hauptfenster (= anderer View, hier: mainFrame) obligatorisch:

Java:
JOptionPane.showMessageDialog(mainFrame, "Beispielnachricht");

Anderes Beispiel ist ein Panel, dass in Textfeldern Usereingaben speichert (Verbindungsdaten bspw.), die von anderen Controllern verarbeitet werden.

Zudem habe ich das Gefühl, dass die Initialisierung von anderen Controllern (hier: TabControllerA und TabControllerB) in einer View (hier: MainFrame) unsauber gelöst ist. Alternativvorschläge?
 

bERt0r

Top Contributor
MVC ist eine Architektur für eine Komponente. Es spricht aber nix dagegen, Komponenten aus weiteren Komponenten (wieder mit MVC) zu bauen. Sogar ein Textfeld hat intern ein MVC Pattern von dem du nur normalerweise nicht viel mitkriegst. Du kannst aber sehr wohl von oben heraus auf das Document (das Model) zugreifen.
Bei meinen Controllen kennt der Controller meist die View gar nicht. Der bekommt alle infos die er braucht übergeben und bearbeitet dann das Model. Das wiederum updated durch Observer Pattern die View.
Wenn deine Komponente irgendetwas an eine Komponente oberhalb in deiner GUI Hierarchie melden muss, würde einfach ein Event werfen, das dein Mainframe dann per Listener (Controller) abfangen kann.
 

babuschka

Top Contributor
Wenn deine Komponente irgendetwas an eine Komponente oberhalb in deiner GUI Hierarchie melden muss, würde einfach ein Event werfen, das dein Mainframe dann per Listener (Controller) abfangen kann.

Hey danke für deine Antwort! Genau das ist es, was ich nicht weiß, wie ich es umsetzen soll. Wie werfe ich ein Event, dass von einem anderen Controller abgefangen werden kann? Wäre cool, wenn du deinen Vorschlag mit Programmcode am Beispiel des Message Dialogs veranschaulichen könntest!
 

babuschka

Top Contributor
Danke für den Link! Das eigentliche Problem ist damit nicht behoben, da ein Verweis auf das mainFrame Objekt benötigt wird. Einzige und in meinen Augen unschöne Möglichkeit ist es, Views in der Klasse, welche die App initialisiert, über statische Getter-Methoden bereitzustellen.

Java:
public class App {

    private static MainFrame mainFrame;

    // TODO Initialisierung von Controller und Model sowie Bereitstellung entsprechender Getter-Methoden.

    public static MainFrame getMainFrame() {
        return mainFrame;
    }

    public static void main(String[] args) {
        mainModel = new MainModel();
        mainFrame = new MainFrame();
        mainController = new MainController(mainModel, mainFrame);
        mainFrame.setVisible(true);
    }

}
 

bERt0r

Top Contributor
Dann hast du s nicht verstanden. Zuerstmal brauchst du irgendeine Klasse die dir deine Komponenten nach aussen hin kapselt. Bei einem Textfeld erzeugst du ja auch nicht jedesmal eigens ein Document und einen DocumentListener und führst dann für M, V und C jeweils die Referenzen mit. Dann kannst du sowas machen:
Java:
class GUI
{

public GUI()
{
JFrame frame=new JFrame();
TabA tabA=new TabA();
TabB tabB=new TabB();
tabA.addSomethingHappenedListener(new SomethingHappenedListener()
{
public void somethingHappened(SomethingHappenedEvent e)
{
doSomethingInTabB(e.getSomeObject);
}
});
}

private void doSomethingInTabB(SomeObject object)
{
}
 

Ähnliche Java Themen


Oben