Rückfrage zur Programmgestaltung (clean code)

Wuast

Bekanntes Mitglied
Hallo Leute,

ich bin unsicher, was besser ist.

Ich programmiere eine GUI und dort gibt es Pushbuttons. Diese haben einen EventHandler, der wiederum Methoden anderer Klassen aufruft (vornehmlich habe ich da eine Klasse, die die Pushbuttons verwaltet ("Verwaltung") und entsprechende Methoden bereithält).

Jetzt frage ich mich, ob ich dann jedes mal ein Objekt der Klasse erzeugen lasse oder ob ich das schon vorher irgendwann machen soll (und ggf. wo)? Intuitiv habe ich mich für ersteres entschieden - vor dem Hintergrund, dass ich das Objekt ggf. gar nicht benötige, sollte der Pushbutton nicht gedrückt werden. Es könnte ja z.B. sein, dass das Programm direkt wieder beendet wird über
Java:
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
und dann bräuchte ich ja keine Objekt der Klasse "Verwaltung". Sollten hingegen mehrere der Buttons betätigt werden, werden ggf. mehrere Objekte der gleichen Klasse erzeugt und das ist auch nicht gut soweit ich weiß.

Was ist sinnvoller?

:)
 

temi

Top Contributor
Hat denn die Klasse Verwaltung noch andere Abhängigkeiten (wie z. B. eine Datenbank). Die muss die Klasse Verwaltung bei der Erzeugung ja kennen. Es wäre nicht sinnvoll, überall neue separate Verwaltungsinstanzen zu erzeugen, die jedesmal auch die Informationen zur DB benötigen, bzw. zum Repository.

Üblicherweise geht man generell bei Klasse so vor, dass alle Abhängigkeiten, die die Instanz der Klasse unbedingt zum Arbeiten benötigt, über den Konstruktor bei der Erzeugung übergibt (Dependency Inversion). Das gilt auch für die GUI (hier, evtl. der Controller). Wenn der Controller eine Abhängigkeit zur sog. Geschäftslogik benötigt, dann erhält er diese als Abhängigkeit über den Konstruktor.

Man kann auch einen Eventbus (z. B. Google Guava) vorsehen. Dann benötigen die einzelnen Komponenten nur noch Abhängigkeiten zu diesem und tauschen nur noch Events untereinander aus ohne sich gegenseitig "zu kennen". Damit lassen sich Abhängigkeiten noch mal reduzieren und das ist es, was man am Ende möglichst erreichen möchte: wenig gegenseitige Abhängigkeiten, und wenn notwendig, dann klar ersichtlich (siehe der Absatz darüber).

Ich bin mir allerdings nicht sicher, ob deine Frage so gemeint gewesen ist.

EDIT: Ich hatte mal ein Thema, wo es auch um die Strukturierung einer Anwendung ging. Das war ziemlich bezogen auf die Prinzipien von DDD (Domain Driven Design, das gleichnamige Buch von Vaughn Vernon ist recht interessant. Leider nur auf Englisch, die deutsche Übersetzung ist nur ein Auszug daraus). Dazu findest du auch in Internet einiges.
 
Zuletzt bearbeitet:

Wuast

Bekanntes Mitglied
Hat denn die Klasse Verwaltung noch andere Abhängigkeiten (wie z. B. eine Datenbank). Die muss die Klasse Verwaltung bei der Erzeugung ja kennen. Es wäre nicht sinnvoll, überall neue separate Verwaltungsinstanzen zu erzeugen, die jedesmal auch die Informationen zur DB benötigen, bzw. zum Repository.

Üblicherweise geht man generell bei Klasse so vor, dass alle Abhängigkeiten, die die Instanz der Klasse unbedingt zum Arbeiten benötigt, über den Konstruktor bei der Erzeugung übergibt (Dependency Inversion). Das gilt auch für die GUI (hier, evtl. der Controller). Wenn der Controller eine Abhängigkeit zur sog. Geschäftslogik benötigt, dann erhält er diese als Abhängigkeit über den Konstruktor.

Man kann auch einen Eventbus (z. B. Google Guava) vorsehen. Dann benötigen die einzelnen Komponenten nur noch Abhängigkeiten zu diesem und tauschen nur noch Events untereinander aus ohne sich gegenseitig "zu kennen". Damit lassen sich Abhängigkeiten noch mal reduzieren und das ist es, was man am Ende möglichst erreichen möchte: wenig gegenseitige Abhängigkeiten, und wenn notwendig, dann klar ersichtlich (siehe der Absatz darüber).

Ich bin mir allerdings nicht sicher, ob deine Frage so gemeint gewesen ist.

EDIT: Ich hatte mal ein Thema, wo es auch um die Strukturierung einer Anwendung ging. Das war ziemlich bezogen auf die Prinzipien von DDD (Domain Driven Design, das gleichnamige Buch von Vaughn Vernon ist recht interessant. Leider nur auf Englisch, die deutsche Übersetzung ist nur ein Auszug daraus). Dazu findest du auch in Internet einiges.
Ich denke im Großen und Ganzen hast du meine Frage erfasst, ja :)
Die Klasse Verwaltung soll ich nur um die Buttons kümmern, einfach, damit das etwas weniger "voll" aussieht und übersichtlicher wird in der GUI-Klasse und bei der Initialisierung. Aber es werden ja noch weitere Klassen dazu kommen, z.B. eine, die eine Arraylist verwaltet und da wird die GUI über die EventHandler ja auch drauf zugreifen müssen.

Wäre eine Vererbung nicht auch ggf. möglich? Nur da in der GUI auch die Mainmethode ist wirkt das auf mich im ersten Moment etwas... komisch. Aber wenn diese Klasse "GUI" von der Klasse "Verwaltung" erben würde, könnte ich ja ohne Probleme die Methoden der Verwaltung einfach aufrufen.
Oder ist das nicht elegant? Und macht ja auch keinen Sinn, wenn ich wie beschrieben nicht nur auf "Verwaltung", sondern auch auf die "Arraylist" zugreifen muss im Verlauf des Programms. Kann ja nur eine Klasse vererben (oder?)..

Im Konstruktor der Methode initialize würde ich dann einfach
Java:
private void initialize (Verwaltung verwaltung, Arraylist >irgendwas< Arraylist){
}
schreiben?!

Das mit dem Eventbus ist mir grad zu viel, da bin ich noch nicht :D

Danke für deine Antwort!
 

KonradN

Super-Moderator
Mitarbeiter
Vererbung ist bei sowas nie eine gute Idee. Bei der Vererbung wirklich immer fragen: gibt es eine „ist ein“ oder „verhält sich wie“ Beziehung?

Wenn sowas nicht sicher mit Ja beantwortet werden kann, dann sollte man keine Vererbung nutzen.

Bei der UI Entwicklung sieht man das z.B. bei dem Fenster - statt von JFrame zu erben hat eine andere Klasse dann ein JFrame.

(Favor) Composition over Inheritance - das wäre dann das, wonach du suchen könntest und wozu du sehr viele Treffer von Tante Google bekommen kannst.
 

Wuast

Bekanntes Mitglied
Vererbung ist bei sowas nie eine gute Idee. Bei der Vererbung wirklich immer fragen: gibt es eine „ist ein“ oder „verhält sich wie“ Beziehung?

Wenn sowas nicht sicher mit Ja beantwortet werden kann, dann sollte man keine Vererbung nutzen.

Bei der UI Entwicklung sieht man das z.B. bei dem Fenster - statt von JFrame zu erben hat eine andere Klasse dann ein JFrame.

(Favor) Composition over Inheritance - das wäre dann das, wonach du suchen könntest und wozu du sehr viele Treffer von Tante Google bekommen kannst.
Komposition, da klingelt etwas.

Also ich brauche eine Klasse als Sammelbecken für alles mögliche an Methoden und über die rufe ich dann je nach Bedarf das auf, was ich brauche. Habe ich das soweit richtig verstanden?
 

KonradN

Super-Moderator
Mitarbeiter
Also ich brauche eine Klasse als Sammelbecken für alles mögliche an Methoden und über die rufe ich dann je nach Bedarf das auf, was ich brauche. Habe ich das soweit richtig verstanden?
Nein, das ist so formuliert Unsinn.

Du musst es sinnvoll strukturieren. Du erstellst mehrere Klassen die jede für sich genau spezifiziert, für was diese da ist. Diese dienen dann in der Regel dazu, Instanzen zu erzeugen.

Jede Funktionalität hat dann in der Regel einen klaren Ort, wo diese hin gehört.
 

temi

Top Contributor
Wäre eine Vererbung nicht auch ggf. möglich? Nur da in der GUI auch die Mainmethode ist wirkt das auf mich im ersten Moment etwas... komisch. Aber wenn diese Klasse "GUI" von der Klasse "Verwaltung" erben würde, könnte ich ja ohne Probleme die Methoden der Verwaltung einfach aufrufen.
Das ist keine gute Idee.

Du fragst ja häufiger nach Clean Code und Prinzipien der Softwareentwicklung. Hierbei gibt es mehrere Ebenen, die man betrachten kann. In aller Kürze hat man auf der untersten Ebene den Code selbst und darüber die Prinzipien für objektorientierte Entwicklung (deren wichtigsten mit SOLID zusammengefasst werden). Dazu gehören (neben anderen), was @KonradN und was ich in unseren Antworten schon geschrieben haben.

Darüber hinaus geht es um die Architektur der Software. Häufig verwendet man ein Schichtenmodell. Die oberste Schicht kann hier die GUI sein, darunter liegt die Logikschicht (Domain Logic, Geschäftslogik) und wiederum darunter die Datenschicht (die evtl. noch einmal geteilt ist in generische Repositories und darunter liegende Mapper zur eigentlichen DB, ...). Diese kommunizieren zwar miteinander, sollten aber möglichst wenige Abhängigkeiten untereinander haben, damit man sie u. U. auch austauschen kann. So könnte die Datenschicht irgendwann nicht mehr einfache Dateien verwenden, sondern eine verteilte DB. Oder die Desktop-GUI wird gegen eine Web-GUI ausgetauscht. Das ist um so einfacher, je weniger Verbindungen zwischen den einzelnen Schichten vorliegen.

Unterstützend gibt es die Entwurfsmuster, die in allen Schichten vorkommen können und auch schon öfter erwähnt wurden.

Gerade für Anfänger (vor allem, wenn man autodidaktisch unterwegs ist) ist das ziemlich viel und kann ziemlich verwirrend sein. Davon solltest du dich allerdings nicht entmutigen lassen, sondern viel lesen (es wurde in deinen Themen bereits viel Literatur genannt) und auch gerne Fragen dazu stellen. Es gibt auch kein schwarz und weiß, also "die einzige wahre Lösung". Eher bewährte Lösungen, die man für seine Anwendung passend adaptieren muss.
 
Zuletzt bearbeitet:

Wuast

Bekanntes Mitglied
Klingt einleuchtend. Wenn man das jetzt alles etwas plakativ vereinfacht ausdrückt, ist es dann "richtig" von oben (also bei der GUI) anzufangen? So habe ich es jetzt gemacht. Also erstmal einen Plan (UML kann ich noch nicht richtig aber so in die Richtung), dann die GUI mit den entsprechenden Elementen und jetzt versuche ich, diese mit Leben zu füllen indem ich passende Klassen formuliere, in den Eventhandlern dann die Methoden aufrufe usw. (--> geht doch dann eigentlich auch in die Richtung von Komposition, was @KonradN sagte, oder?)

Und nochmal kurz zurück zu dem Konstruktor als mögliche Lösung für die Ausgangsfrage: Wie sieht der dann aus?
 

temi

Top Contributor
Und nochmal kurz zurück zu dem Konstruktor als mögliche Lösung für die Ausgangsfrage: Wie sieht der dann aus?
Die Frage lässt sich nicht beantworten, weil sie zu allgemein ist. Da müsste ich auf den Beitrag #7 von @mihe7 verweisen.

Man könnte sich ein Beispiel ausdenken, falls es das ist, was du meinst (dann melde dich halt noch mal). Die Erklärung oben sollte eigentlich allerdings ausreichend gewesen sein.
 

Wuast

Bekanntes Mitglied
Die Frage lässt sich nicht beantworten, weil sie zu allgemein ist. Da müsste ich auf den Beitrag #7 von @mihe7 verweisen.

Man könnte sich ein Beispiel ausdenken, falls es das ist, was du meinst (dann melde dich halt noch mal). Die Erklärung oben sollte eigentlich allerdings ausreichend gewesen sein.
Üblicherweise geht man generell bei Klasse so vor, dass alle Abhängigkeiten, die die Instanz der Klasse unbedingt zum Arbeiten benötigt, über den Konstruktor bei der Erzeugung übergibt (Dependency Inversion). Das gilt auch für die GUI (hier, evtl. der Controller). Wenn der Controller eine Abhängigkeit zur sog. Geschäftslogik benötigt, dann erhält er diese als Abhängigkeit über den Konstruktor.
Also z.B. dann sowas?
Java:
public class EineGUI {

    private JFrame frmGui;

    EineGui(Verwaltung verwaltung, ArrayList arraylist){        //So den Konstruktor?
    }
    
    
    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    GUI_Beispiel window = new GUI_Beispiel();
                    window.frmGui.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public GUI_Beispiel() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        //usw.

Oder wie?
 

KonradN

Super-Moderator
Mitarbeiter
Da ist ja kein Aufruf von irgendwas drin ....

Schon deine Klasse GUI_Beispiel braucht ja irgendwas. Bei Applikationen nutzt man bei dem User Interface in der Regel ein Pattern wie MVC, MVVM oder so. Da solltest Du evtl. einmal schauen, was Du für Beispiele finden kannst.

Dann hättest Du mehrere Klassen:
  • Eine Klasse, die nur die reine UI darstellt + klarem Interface nach außen.
  • Eine Klasse, die dann nur das Model darstellt - also sozusagen die Daten, die von der UI angezeigt werden und so.
  • Eine letzte Klasse wäre dann der Controller. Der steuert dann alles.

Machen wir einfach eine kleines, minimales Beispiel:

Model ist dann einfach eine Adresse, sprich man hat etwas wie:
Java:
package de.kneitzel;

public class Address {
    private String name;
    private String street;
    private String zipCode;
    private String city;
    private String country;
    
    // Konstruktoren, Getter, Setter, ...
    
}

Dann hast Du eine View die dann einfach dazu dienen soll, dass ein Adresse angezeigt werden kann und geändert werden kann ... Dazu hast Du dann z.B. ein JFrame mit Controls für alle Daten. Die Daten sind in einer Address Instanzvariable ... Und es gibt dann Methoden um Werte in die Controls oder anders herum zu übernehmen (Das ist etwas, das man bei JavaFX nicht groß schreiben muss - da nutzt man ein sogenanntes Binding ... Hat swing aber nicht, also machen wir es manuell).
Und dann haben wir einen Knopf, der mit den Daten irgendwas macht.
Und damit ein Controller sich da verbinden kann, habe ich einfach ein PropertyChangeLister genutzt. Da kannst Du aber auch eigene Events beliebig erstellen und nutzen - siehe z.B. http://www.java2s.com/Tutorial/Java/0260__Swing-Event/CreatingaCustomEvent.htm

Java:
package de.kneitzel;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

public class AddressView {
    private Address address;

    // Controls - hier nur angedeutet
    private JFrame frame;
    private JTextField nameField;
    private JButton saveButton;

    private List<PropertyChangeListener> eventListeners;

    // Ein eigenes Event
    public AddressView(Address address) {
        this.address = address;

        // eventListeners
        eventListeners = new ArrayList<>();
                
        // Aufbau des Frames und Anzeige
        frame = new JFrame("Adresse editieren");
        nameField = new JTextField();
        saveButton = new JButton();
        saveButton.addActionListener(this::handeSaveButton);
        // ... Halt die UI Aufbauen!
    }

    protected void copyDataToModel() {
        address.setName(nameField.getText());
        // ...
    }
    
    protected void copyDataToControls() {
        nameField.setText(address.getName());
    }
    private void handeSaveButton(ActionEvent actionEvent) {
        copyDataToModel();
        firePropertyChangeEvent("saveAddress", null, address);
    }

    public void addPropertyChangeLister(PropertyChangeListener listener) {
        synchronized(eventListeners) {
            eventListeners.add(listener);
        }
    }
    
    protected void firePropertyChangeEvent(String property, Object oldValue, Object newValue) {
        synchronized (eventListeners) {
            for (PropertyChangeListener listener : eventListeners) {
                listener.propertyChange(new PropertyChangeEvent(this, property, oldValue, newValue));
            }
        }
    }
}

Der Controller kann dann der "Master" sein - Der kann dann z.B. eine neue Address Instanz erzeugen, ein AddressView erzeugen (mit der Address Instanz) und sich dann eintragen um auf PropertyChangeEvents zu reagieren.

Der Controller weiss dann auch, was sonst so wichtig ist: Address Instanzen werden ggf. gespeichert oder geladen oder was auch immer ...

Address ist aber einfach nur eine Datenklasse (Könnte auch ein Record sein bei aktuellerem Java!) Und die AddressView ist dumm. Die weiss fast nichts. Nur das, was notwendig ist um eben Daten anzuzeigen bzw. Eingaben entgegen zu nehmen. Aber das ist abgekapselt vom Rest.

Also erstmal einen Plan (...), dann die GUI
Ja, der Plan kommt immer zuerst. Aber man startet dann nicht bei der GUI! Das ist beim UI/UX Design zwar auch etwas, das sofort kommen kann, aber man Entwickelt immer erst die Basis:
  • Address Klasse würde hier zuerst kommen.
  • Dann Klassen zum speichern / laden ... Incl. Unit Tests.
  • Wenn ich diese Grundlagen habe, dann kommt die GUI. Und die ist losgelöst! Mir ist also egal, ob dies eine Swing, JavaFX, SWT, Spring MVC, ... Applikation wird, wenn ich die ersten Schritte mache. Die sind immer gleich. Das sollte man also durchaus getrennt betrachten - das ist dann sowas wie eine Modul Sicht. Du teilst da Dinge in Module auf um da dann auch saubere Abhängigkeiten zu haben. So kennen die Klassen, die zum Model gehören (Datenklassen, Klassen zum lesen / schreiben der Daten, ...) nicht die Klassen, die diese Daten irgendwo anzeigen.

Das einfach einmal auf die Schnelle als kleine Übersicht, wie man sowas machen könnte. Controller habe ich noch nicht aufgezeigt - dazu fehlt mir gerade die Zeit.
 

Wuast

Bekanntes Mitglied
Erstmal lieben Dank für deine ausführliche Antwort!

Mir war schon klar, dass mein Code sehr unvollständig ist; mir ging es ja bei meiner letzten Rückfrage um den Konstruktor, weil ich nicht mehr sicher bin/war (oder es vllt. auch nie richtig kapiert habe), wo der Unterschied ist zu den Konstruktoren von Klassen und den Methoden, die ich ja auch überladen kann.

D.h. ich meinte eigentlich nur
Da ist ja kein Aufruf von irgendwas drin ....

Schon deine Klasse GUI_Beispiel braucht ja irgendwas. (...)
Java:
package de.kneitzel;

public class Address {
    private String name;
    private String street;
    private String zipCode;
    private String city;
    private String country;
 
    // Konstruktoren, Getter, Setter, ...
 
}

(...)

Java:
package de.kneitzel;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

public class AddressView {
    private Address address;

    // Controls - hier nur angedeutet
    private JFrame frame;
    private JTextField nameField;
    private JButton saveButton;

    private List<PropertyChangeListener> eventListeners;

    // Ein eigenes Event
    public AddressView(Address address) {
        this.address = address;
diesen Teil mit dem Konstruktor.

Bezogen auf mein Beispiel wäre es dann

Java:
[CODE=java]public class EineGUI {

    private JFrame frmGui;
    Verwaltung verwaltung;

    EineGui(Verwaltung verwaltung, {     
    this.verwaltung = verwaltung;
    }
 
 
    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    GUI_Beispiel window = new GUI_Beispiel();
                    window.frmGui.setVisible(true);
               //... usw

Hier kommt dann noch das UI wie es mir der WindowBuilder generiert und wo ich dann die Methoden der Klassen Verwaltung etc. aufrufe, wenn ein entsprechender Eventhandler installiert wird. Und darum ging es mir ja. Meine erste Situation und gedankliche Lösung war: Ich habe 2 JButtons und der eine soll ein Objekt erzeugen und einer Liste anfügen, der andere soll das Objekt oder die Liste löschen (oder was auch immer, ist ja nur ein Bsp). Die jeweiligen Methoden dazu habe ich in der Klasse Verwaltung programmiert. Deswegen war meine Frage anfangs, ob ich dann den Eventhandler so schreibe und jedes mal ein neues Objekt der Klasse Verwaltung erzeuge (weil es vorher ja nicht gebraucht wird) oder ob es besser an einer anderen Stelle passiert.

Code:
        JButton beispielObjekt1 = new JButton("Beispielobjekt anlegen");
        beispielobjektButton1.setFont(new Font("Lucida Grande", Font.BOLD, 12));
        beispielobjektButton1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
            Verwaltung btn = new Verwaltung();    //es geht um diese
            btn.beispielobjektErzeugen();        //beiden Zeilen
            }
        });
        // und
                JButton beispielObjekt2 = new JButton("Beispielobjekt anlegen");
        beispielobjektButton2.setFont(new Font("Lucida Grande", Font.BOLD, 12));
        beispielobjektButton2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
            Verwaltung btn = new Verwaltung();        //Hier wieder die
            btn.beispielobjektLöschen();            //beiden Zeilen
            }
        });

(und natürlich gibt es dann in der Klasse Verwaltung die Methoden
Code:
public void beispielobjektErzeugen(){
//blabla, Inhalte zur Objekterzeugung und Erweiterung der Liste
}

public void beispielobjektLöschen(){
//blubb; Inhalte zur Löschung des Objektes oder der Liste oder was auch immer
}
Dann war ja meine Überlegung noch, ob ich in der Klasse der GUI erstmal ein Objekt der Klasse Verwaltung erzeuge, auch, wenn ich es vllt. überhaupt nicht brauche weil das Programm z.B. direkt wieder geschlossen wird.

Aber es kam ja dann von @temi der Vorschlag mit dem Konstruktor, und da war ich mir nicht sicher, wie das aussehen muss. Weil wenn ich jetzt den Konstruktor aufrufe und dort die Verwaltung verwaltung überlade, kann ich dann nachfolgend einfach nach Lust und Laune innerhalb anderer Methoden (wie hier der initialize(); ) verwaltung.beispielobjektErzeugen(); aufrufen? Die Referenz ist ja so erstmal null, sprich ich bekomme ein Exception.

Dann war ja die Überlegung, ob ich in der Klasse der GUI erstmal ein Objekt der Klasse Verwaltung erzeuge, auch, wenn ich es vllt. überhaupt nicht brauche weil das Programm z.B. direkt wieder geschlossen wird.

Hoffe, das war verständlicher :)
Es hakt glaube ich mal wieder an so einem absoluten Basic-Ding was ich wieder nicht richtig auf die Reihe kriege.
 
Zuletzt bearbeitet:

Neue Themen


Oben