Funktion zu einem JButton hinzufügen

Keviehn

Mitglied
Hallo Leute,

bin ziemlich neu im Bereich JAVA. Ich habe einige Kenntnisse im EDV-Bereich, aber für JAVA hatte ich bis jetzt kein Interesse. Da ich nun für ein "kleines" Projekt JAVA benötige, werde und will ich mich natürlich mit JAVA beschäftigen.

Mein Ziel ist es nach einiger Zeit einen Bildbearbeitungsprogramm mit JAVA geschrieben zu haben.
Dieses Programm soll einige Funktionen können:

- Grafische Oberfläche
- Bild Laden
- Bild bearbeiten (Schwarz-Weiß-Funktion, Negativ, Bild drehen, Bild spiegeln, Bild invertieren)
- Bild speichern

Mein jetziges Wissen ist sehr beschrenkt und baut auf dieses Forum und einige Bücher auf.

Ich nutze JDK 6 Update 21 und den JavaEditor (falls ihr ein anderes Programm kennt, was für mein Vorhaben besser geeignet ist, dann nur raus damit.)

Jetzt zu meinen Problem:

Ich habe mit dem JavaEditor einen JFrame erstellt und dahin ein JButton erstellt und möchte diesen JButton die "Bild-Ladefunktion" zuweisen.

Java:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

/**
  *
  * Beschreibung
  *
  * @version 1.0 vom 05.01.2010
  * @author
  */

public class LADEN extends JFrame {
  // Anfang Attribute
  private JButton _LOAD = new JButton();
  // Ende Attribute

  public LADEN(String title) {
    // Frame-Initialisierung
    super(title);
    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    int frameWidth = 300;
    int frameHeight = 300;
    setSize(frameWidth, frameHeight);
    Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
    int x = (d.width - getSize().width) / 2;
    int y = (d.height - getSize().height) / 2;
    setLocation(x, y);
    Container cp = getContentPane();
    cp.setLayout(null);
    // Anfang Komponenten

    _LOAD.setBounds(40, 24, 177, 89);
    _LOAD.setText("BILD LADEN");
    _LOAD.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
        _LOAD_ActionPerformed(evt);
      }
    });
    _LOAD.setToolTipText("LÄDT EIN BILD");
    cp.add(_LOAD);
    // Ende Komponenten

    setResizable(false);
    setVisible(true);
  }

  // Anfang Methoden
  public void _LOAD_ActionPerformed(ActionEvent evt) {
    // TODO hier Quelltext einfügen
  }

  // Ende Methoden

  public static void main(String[] args) {
    new LADEN("LADEN");
  }
}

Später möchte ich natürlich weitere JButton's hinzufügen bezüglich des Speichern und andere Funktionen. Nun die Frage: In welchen Bereich des Quelltextes muss ich dann die Funktion hinzufügen?

Ich hoffe ich habe einigermaßen klar ausgedrückt, wenn nicht einfach fragen. :)


Danke und viele Grüße
Keviehn
 
M

Marcinek

Gast
Ich würde einen separaten Handler für ButtonEvents schreiben und diesen an jedem Button festmachen.

In unserem selbstgeschreibenen MVC Framework ist das so, dass jeder Button selbst ein Listener ist und entsprechend an seinen Controller seine Events verschickt.

Gruß,

Marcin
 

Marco13

Top Contributor
Dass ein JButton selbst ein Listener IST erscheint mir (zurückhaltend gesagt) "ungewöhnlich".

Der "geeignetste Platz" ist aber schwer zu benennen. Ganz abstrakt formuliert: Es gibt irgendein Datenmodell, das Funktionen anbietet. Und irgendein GUI-Element, mit dem man diese Funktion auslösen will. Und mit einem Listener (Controller) wird diese Verbindung hergestellt.

Im Moment gibt es die Methode "_LOAD_ActionPerformed" (die SO sehr den Naming conventions widerspricht, dass es schon fast beeindruckend ist ;) ). Wenn das Programm erstmal besser "ausstrukturiert" ist, gibt es vielleicht eine Klasse "ImageLoader" (oder so), und die bietet eine Methode zum Laden eines Bildes an. Diese Methode könnte dann z.B. vom ActionListener aufgerufren werden.

Für ein komplexeres, ausgefeilteres GUI würde man an dieser Stelle wohl "Actions" verwenden (siehe How to Use Actions (The Java™ Tutorials > Creating a GUI With JFC/Swing > Using Other Swing Features) ). Damit kann man sehr elegant eine Aktion (wie z.B. laden) mit mehreren GUI-Componenten (wie z.B. einem Button und einem Menüeintrag) verbinden.
 

Landei

Top Contributor
Ich würde auch zu Actions raten. Eine mögliche Grundstruktur sieht so aus:

Java:
public class MyClass {

   private Action sayHelloAction = new AbstractAction("Say Hello") {
       public void actionPerformed(ActionEvent ae) {
           System.out.println("hello");
       } 
   }

   public MyClass() {
       ...
       JButton sayHelloButton = new JButton(sayHelloAction);  
       somePanel.add(sayHelloButton);
       ...
   }
}
 

Keviehn

Mitglied
... so ich habe mich von meinen den JButtons bzw. von den automatisch gennerierten Quelltext verabschiedet und habe ein wenig getüfftelt. Mein jetztiger Code sieht zurzeit so aus:

Java:
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class Menüleiste {
  public Menüleiste () {
    Frame f = new Frame ("Test der Menüleiste ");
    f.addWindowListener(new WindowAdapter () {
      public void windowClosing (final WindowEvent e) {
        System.exit(0);
      }
    });
    f.setMenuBar(this.getMenubar ());
    f.setSize(300,140);
    f.setVisible(true);
  }
//111111111111111111111111111111   MENÜ LEISTE   1111111111111111111111111111111


  protected MenuBar getMenubar () {                          // Anlegen der Menüleiste
    MenuBar menueLeiste = new MenuBar ();                    // Das Menü anlegen


//22222222222222222222222222   MENÜ LEISTE - DATEI   222222222222222222222222222

    Menu datei = new Menu ("Datei ");                                                 // Menüeintrag anlegen
    
    MenuItem _NEW = new MenuItem ("Neu ");                                            // Menü zum Menüeintrag hinzufügen (NEU)
    datei.add (_NEW);                                                                 // Hinzufügen
    
    MenuItem _LOAD = new MenuItem ("Laden ");                                         // Menü zum Menüeintrag hinzufügen (Laden)
    datei.add (_LOAD);                                                                // Hinzufügen
    
    MenuItem _SAVE = new MenuItem ("Speichern ");                                     // Menü zum Menüeintrag hinzufügen (Speichern)
    datei.add (_SAVE);                                                                // Hinzufügen
    
    MenuItem _CLOSE = new MenuItem ("Beenden ");                                      // Menü zum Menüeintrag hinzufügen (Beenden)
    datei.add (_CLOSE);                                                               // Hinzufügen

    menueLeiste.add(datei);                                                           // Hinzufügen abschliessen

//22222222222222222222222222   MENÜ LEISTE - DATEI   222222222222222222222222222

//3333333333333333333333   MENÜ LEISTE - Transformation   3333333333333333333333



   Menu extra = new Menu ("Transformation ");                                         // Menüeintrag anlegen

   Menu _SPIN = new Menu ("Drehen ");                                                 // Untermenü1 hinzufügen
   Menu _REFLECT = new Menu ("Spiegeln ");                                            // Untermenü2 hinzufügen
   Menu _FILTER = new Menu ("Filter ");                                               // Untermenü2 hinzufügen

////////////////////////////////////   Untermenü1   //////////////////////////////////////////////
   extra.add(_SPIN);                                                                  // !
   _SPIN.add("90° Links ");                                                           // Menü zum Untermenüeintrag hinzufügen (90° nach Links drehen)
   _SPIN.add("90° Rechts ");                                                          // Menü zum Untermenüeintrag hinzufügen (90° nach Rechts drehen)
   
   _SPIN.addSeparator();                                                              // Trennung

   _SPIN.add("180° Links ");                                                          // Menü zum Menüeintrag hinzufügen (180° nach Links drehen)
   _SPIN.add("180° Rechts ");                                                         // Menü zum Menüeintrag hinzufügen (180° nach Rechts drehen)
////////////////////////////////////   Untermenü2   //////////////////////////////////////////////
   extra.add(_REFLECT);                                                               // !
   _REFLECT.add("Vertikal ");                                                         // Menü zum Untermenüeintrag hinzufügen (Vertikal Spiegeln)
   _REFLECT.add("Horizontal ");                                                       // Menü zum Untermenüeintrag hinzufügen (Horizontal Spiegeln)
////////////////////////////////////   Untermenü3   //////////////////////////////////////////////
   extra.add(_FILTER);                                                                // !
   _FILTER.add("Verrauschen ");                                                       // Menü zum Untermenüeintrag hinzufügen (Verrauschen Spiegeln)
   _FILTER.add("Unscharf ");                                                          // Menü zum Untermenüeintrag hinzufügen (Unscharf Spiegeln)
   _FILTER.add("Invertieren ");                                                       // Menü zum Untermenüeintrag hinzufügen (Invertieren Spiegeln)
   _FILTER.add("Schwarz / Weiß ");                                                    // Menü zum Untermenüeintrag hinzufügen (Schwarz/Weiß Spiegeln)
////////////////////////////////////   Untermenü1   //////////////////////////////////////////////
////////////////////////////////////   Untermenü2   //////////////////////////////////////////////
////////////////////////////////////   Untermenü3   //////////////////////////////////////////////

  menueLeiste.add(extra);                                   // Hinzufügen abschliessen
              return menueLeiste;
                     }

//111111111111111111111111111111   MENÜ LEISTE   1111111111111111111111111111111

  public static void main (String[] args) {
    Menüleiste menusample = new Menüleiste ();
  }
}

Ein klassiches Frame mit 2 Menüeinträgen und weiteren Untermenüs. Da ich selbst mit den Actions zurzeit noch nicht zurecht kommen, würde ich Euch bitte meinen Code so zu verändern das die Funktion "schließen" funktioniert.

Ich möchte somit sehen, wie der Code auszusehen hat, wenn man auf Datei --> Schließen drückt.

Ich hoffe das ist mit meinen Code möglich.

Ich hoffe mir darauf aus meinen eigenen Code zu lernen und die restlichen Funktionen auch einzubinden.


Danke für Eure Unterstützung und viele Grüße
Keviehn
 

w0ddes

Bekanntes Mitglied
Also habs grad mal aus dem Bauch herraus gemacht. Hab jetzt mal nur den "betroffenen" Abschnitt hier reingenommen:

Java:
    MenuItem _CLOSE = new MenuItem ("Beenden ");  // Menü zum Menüeintrag hinzufügen (Beenden)
    _CLOSE.addActionListener(new java.awt.event.ActionListener(){
		public void actionPerformed(java.awt.event.ActionEvent e) {
			System.exit(0);   //Hier deine Action einfügen! In diesem Fall halt "Beenden"
		}
    });
    datei.add (_CLOSE);

P.S: Die Namen wie _CLOSE sind leicht stressig, solltest du vielleicht noch ändern.

Edit:

Sollten die Einträge wie
Code:
90° links
etc. nicht auch MenuItems sein?!

Also so:
Java:
   extra.add(_SPIN);                                                                  // !
   
   MenuItem links90 = new MenuItem("90° Links ");
   _SPIN.add(links90);      // Menü zum Untermenüeintrag hinzufügen (90° nach Links drehen)
   
   MenuItem rechts90 = new MenuItem("90° rechts");
   _SPIN.add(rechts90);

Edit 2: Außerdem (auf deine Kommentare bezogen):
Du fügst nicht dem Menüeintrag das Menu hinzu sondern umgekehrt.
Code:
//Unterenüeintrag zum Menü hinzufügen
 
Zuletzt bearbeitet:

Keviehn

Mitglied
Also habs grad mal aus dem Bauch herraus gemacht. Hab jetzt mal nur den "betroffenen" Abschnitt hier reingenommen:

Dein Bauch ist gut. :) Funktioniert jetzt und ich kenn jetzt den Aufbau der Actions. Danke!

Die Namen wie _CLOSE sind leicht stressig, solltest du vielleicht noch ändern.

Sowas lernte ich (glaube ich) bei Youtube, aber ich werde sie noch ändern.

Ich melde mich sobald ich weitergekommen bin. :applaus:
 

Marco13

Top Contributor
Ja, sowas wie
Code:
    MenuItem _NEW = new MenuItem ("Neu ");                                            // Menü zum Menüeintrag hinzufügen (NEU)
    datei.add (_NEW);                                                                 // Hinzufügen
braucht man auch nicht zu kommentieren.

Aber mal was ganz anderes: An welchem Punkt bist du von Swing (JFrame, JButton, JMenu(!)) auf AWT (Frame, Button, Menu(!)) umgestiegen?
 

Keviehn

Mitglied
Da ich mich erst seit geraumer Zeit mit JAVA beschäftige Kommentier ich eigentlich jede Funktion die ich einbaue. (Dient für mich als "Sicherheit").

Ich bin auf AWT umgestiegen, da es für mich den anscheind hat es am "leichtesten" zu erlernen.
 

Landei

Top Contributor
AWT ist tot, es ist häßlich, umständlich, wird nicht weiterentwickelt, und seine Trümmer sind gerade noch gut genug, um als Swing-Fundament Verwendung zu finden (das allerdings selbst mal wieder eine Generalüberholung nötig hätte).
 

AmunRa

Gesperrter Benutzer
AWT ist zwar tot, ist aber um einige Grundlagen (ala. DoubleBuffering) zu lernen noch immer nützlich.
Ein Anfänger sollte ruhig einmal ein bisschen mit AWT spielen, da er wahrscheinlich mit swing sonst nie mit einem WindowListener in kontakt kommt(DISPOSE_ON_CLOSE),
 

w0ddes

Bekanntes Mitglied
Was ich dir zu Swing wirklich sehr empfhelen kann sind die Java Tutorials zu Swing (Solange du einigermaßen gut Englisch verstehst). Ich habe erst vor einem Jahr mit Java bzw mit "richtigem" Programmieren überhaupt begonnen und die Tutorials haben mir sehr geholfen. ;)
 

Keviehn

Mitglied
Ich bin langsam echt an zweifeln ob ich überhaupt mit AWT an mein gewünschtes Ziel komme.
Swing bietet (soweit ich weiß) ja den JFileChooser mit dem es sicherlich einfacher ist, ein Bild zu laden und zu speichern, scheint übersichtlicher zu sein, leicht erweiterbar etc.

Im Endeffekt soll es am Ende so aussehen:
300810224458_Bildbetrachter.jpg


Im Menü soll es die Menüpunkte:

  • Neu - (Soll kein Bild anzeigen bzw. einen weißen Hintergrund)
  • Laden - (Auswahl einer Bilddatei und anzeigen im selben "Panel")
  • Speichern - (Das Bild speichern)

... dazu müssen noch einige Funktionen eingebunden werden, ob es in einen Menü ist oder mittels Buttons ist eigentlich egal, ich muss sie nur nutzen können.

Anscheinend ein kleiner und harter Brocken für einen Anfänger. :eek:

Somit denke ich werde ich wohl doch "wieder" auf Swing umsteigen. Gibt ja tatsächlich gute Tuts. dafür und die meisten Bücher scheinen auch darauf aufzubauen.
 

AmunRa

Gesperrter Benutzer
Naja du könntest dir den FileChooser selbst in AWT schreiben

Natürlich geht alles einfacher mit Swing als mit AWT und wenn du wirklich ein Programm schreiben möchtest, dass du nacher auch veröffentlichen willst solltest du zumindest Swing verwenden. Aber
du lernst eine ganze menge wenn du versuchst dein Programm in AWT zu schreiben. (Wie man ordentlich mit einen Tree umgeht, doublebuffering, ...)
 

w0ddes

Bekanntes Mitglied
Da er wohl überhaupt Java-Anfänger ist, wäre das ganze selbst zu schreiben wohl etwas zu viel für ihn ;) Das kann er dann ja in Angriff nehmen, wenn er mal mehr Erfahrung mit Java und Programmieren hat ;)
 

AmunRa

Gesperrter Benutzer
Könnte sein,

ich glaub, dass das vom Individuum abhängt ob er es als anfännger schafft oder nicht (ich hab nicht gesagt dass es einfach ist so etwas selbst zu schreiben). Aber ween er sich anstrengt und es ihm Spass macht sich da durch zu beißen dann kann er dass schon versuchen.

Der TO muss sich selbst überlegen wie er es machen kann und will. Hängt ja auch ab was er wirklich vor hat
 

Keviehn

Mitglied
Ich habe mich heute mal mit SWING beschäftigt und man versteht es eigentlich leichter als anderes. Ich finde die Tuts. für SWING auch angenehmer als AWT.

Werde das Projekt für mich persönlich auf Eis legen. Ich werde und muss um JAVA zu verstehen aufjedenfall bei Kapitel 1 anfangen und nicht probieren aus anderen Quellcodes zu lernen. Kann nur schief gehen.

Sicherlich die unverschämteste Frage die ihr hier bekommt, aber gibt es die Möglichkeit das einer von euch mir so ein Programm schreibt bzw. mit den Quellqode gibt. . . wobei jetzt nicht auf die Sauberkeit geachtet werden müsste.

Falls kein Interesse besteht, brauch auch nicht weitergelesen werden:

Hier nochmal eine kleine Übersicht was ich eigentlich möchte:

  • Ich benötige eine JAVA - Applikation die ein Bild laden und anzeigen kann. Ob das Bild mittel JFilechooser oder im Quelltext angegeben wird wäre mir relativ egal.

  • Das geladene Bild muss manipuliert werden. (Schwarz-Weiß-Funktion, Negativ, Bild drehen, Bild spiegeln, Bild invertieren)

  • Das geänderte Bild muss gespeichert werden.

Würde mich um eine Antwort freuen.


Grüße
Keviehn
 

Keviehn

Mitglied
Da habe ich keine wirklichen Ansprüche. Wenn es die Zeit und Lust erlaubt wird es sicherlich irgendwann fertiggestellt.


Grüße
Keviehn
 

Landei

Top Contributor
Hier mal ein mögliches Grundgerüst (mit Bild laden und Spiegeln an der X-Achse):

Java:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;

public class ImageEditor {

    private BufferedImage image;
    private JPanel panel = new JPanel() {

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (image != null) {
                int w = Math.min(getWidth(), getHeight() * image.getWidth() / image.getHeight());
                int h = Math.min(getHeight(), getWidth() * image.getHeight() / image.getWidth());
                g.drawImage(image, 0, 0, w, h, null);
            }
        }
    };

    private Action loadAction = new AbstractAction("Load") {
        public void actionPerformed(ActionEvent ae) {
            JFileChooser fc = new JFileChooser(".");
            fc.setFileFilter(new FileFilter() {
                public boolean accept(File f) {
                    String name = f.getName().toLowerCase();
                    return name.endsWith(".png") || name.endsWith(".gif") ||
                           name.endsWith(".jpg") || name.endsWith(".jpeg") || f.isDirectory();
                }
                public String getDescription() {
                    return "Images (*.png, *.gif, *.jpg, *.jpeg)";
                }
            });
            if(fc.showOpenDialog(panel) == JFileChooser.APPROVE_OPTION) try {
                image = ImageIO.read(fc.getSelectedFile());
                panel.invalidate();
                panel.repaint();
            } catch(IOException ex) {
                ex.printStackTrace();
            }
        }
    };

    private Action flipXAction = new AbstractAction("Flip X") {
        public void actionPerformed(ActionEvent e) {
            if (image != null) {
                BufferedImage bi = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
                Graphics g = bi.getGraphics();
                g.drawImage(image, image.getWidth(), 0, -image.getWidth(), image.getHeight(), null);
                g.dispose();
                image = bi;
                panel.invalidate();
                panel.repaint();
            }
        }
    };

    public ImageEditor() {
        JFrame frame = new JFrame("ImageEditor");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(50, 50, 800, 600);
        frame.getContentPane().add(panel);
        Box box = Box.createVerticalBox();
        box.add(new JButton(loadAction));
        box.add(new JButton(flipXAction));
        frame.getContentPane().add(box, BorderLayout.EAST);
        frame.setVisible(true);
    }

    public static void main(String... args) {
        new ImageEditor();
    }
}
 
M

Marcinek

Gast
Ich weiß nicht, ob der TO tatsächlich die Funktinalität braucht oder das nur nutzen möchte um sich mit Java zu beschäftigen...

Wenn man die Funktionalität braucht, dann kann man bestehende Programme besser nutzen.

Wenn man sich mit Java vertraut machen möchte, dann ist das die denkbar schlechteste Methode.

Außerdem bedarf es mehr als 1 Tag um die Prinzipien nicht nur von Java sondern auch noch von Swing zu erlerenen. - @TO: Wenn du nach dem ersten (kleinen) Problem "aufhörst", dann wird das nix.

--------

@ Topic:

Das o.g. Beispiel eigent sich in meinen Augen nicht so gut um einen zu Zeigen, wie er das machen soll,d a hier etliche "sonderformen" genutzt worden sind ;D

Um auf mehrere Buttons reagieren zu können, dann macht man das genau wie im WEB auch:

1. Button / Menüpunkt bennen:
Code:
.setName("Close");

2. Einen zentralen ActionListener Proggen:

Java:
public MyGlobalActionListener extends ActionListener {

public void actionPerformed ( Action a ) {
    if ( a.getSource().getName().equals ( "Close")) {
     // CLOSE
    
    } else if ( a.getSource().getName().equals ( "Andere Aktion") ) 
      // Andere Aktion
    }


}
}

3. Man muss nun seinen ActionListener an den Button befestigen.
Code:
addActionListener ( new MyGlobalActionListener() );

Man kann nach beliben referenzen und so an den Actionlistener weiter leiten und die einzelenen Funktionen auslagern in andere Klassen etc...

Gruß,
 

Landei

Top Contributor
Ich persönliche finde einen einzelnen "MonsterListener" mit seitenlanger if-else-Kaskade jedenfalls unschön und unflexibel. Einzelne Actions können dagegen wiederverwendet werden (z.B. in einer ToolBar oder im Menü), wie auch sehr schön in der Insel dargelegt.
 
M

Marcinek

Gast
Das ist ja nur ein Anfang.

In einem weiteren Schritt würde ich einen Namen zu einer Klasse in einer HashMap zuordnen.

Dann könnte man abhängig von einem Button eine bestimmte Klasse laden und eine Methode darin ausführen.

Damit lassen sich schön Case Blöcke vermeiden, ohne, dass man inline Klassen und Methoden verwendet.
 
Zuletzt bearbeitet von einem Moderator:

AmunRa

Gesperrter Benutzer
Wobei bitte bei einem ActionListener sollte man dann statt dem namen auf den actionCommand gehen

also

if (a.getActionCommand().equals("Close"){
 

Landei

Top Contributor
Je länger ich darüber nachdenke, um so klarer wird mir, dass dein Vorschlag ein echtes Antipattern ist:

MonsterListener-Antipattern

Erkennungsmerkmal: Der ActionListener-Code für alle Buttons, Menüs u.s.w wird in einem einzigen ActionListener zusammengefasst, der in einer if-else-Kaskade die richtige Komponente über den Namen identifiziert und den zugehörigen Code ausführt.

Kritikpunkte:
- Verletzung des Separation of Concerns-Prinzips
- Der Ansatz skaliert nicht. Bei einer Anwendung mit vielen Komponenten (etwa ein Taschenrechner mit vielen Funktionen als MenuItems) müssen viele (eigentlich unnötige) Tests durchgeführt werden. Die zentrale Methode wird total unübersichtlich.
- Schlecht mehrfach verwendbar: Während Eigenschaften wie "enabled" in einer Action zentral geändert werden können, muss hier der Status jeder Komponente einzeln gesetzt werden.
- Anfällig für Tippfehler: Die Identifikation erfolgt über Strings. Vertippt man sich, führt das nicht zu Compiletime-Fehlern, sondern Problemen zur Laufzeit
- Probleme beim Refactoring: Wird eine Komponente gelöscht, kann ungenutzer Code im MonsterListener zurückbleiben, ohne dass man es merkt. Wird die Funktionalität zweier Fenster zusammengeführt, können sich Komponenten gleichen Namens ungewollt überdecken (d.h. der falsche Code wird aufgerufen)

Es gibt keinen Grund, sich diesen Besen auf den Buckel zu binden. Eine Lösung, die für das "Web" irgendwie funktioniert, muss noch lange nicht die beste für Desktop-Entwicklung sein. Und "macht man so" ist keine gute Begründung. Wie ich schon ganz zu Anfang geschrieben habe, ist meine Lösung nur eine von mehreren Realisierungsmöglichkeiten, aber sie hat einige Vorteile: Jede Aktion ist eine abgeschlossene, separate und wiederverwendbare Einheit, die Lösung basiert auf zu Compilezeit überprüfbaren Variablen und nicht auf (aus Compilersicht nichtssagenden) Strings und es gibt keine Probleme beim Refactoring. Deine Lösung fällt hingegen aus meiner Sicht auf das Niveau vor Einführung der strukturierten (geschweige denn objektorientierten) Programmierung zurück.
 
Zuletzt bearbeitet:
T

Tomate_Salat

Gast
Dann könnte man abhängig von einem Button eine bestimmte Klasse laden und eine Methode darin ausführen.

Damit lassen sich schön Case Blöcke vermeiden, ohne, dass man inline Klassen und Methoden verwendet.

Dann hast du es nur verlagert. Schließe mich Landei an, das bringt dir keinen Vorteil und Wartungsfreundlich ists auch nicht. Definierst du die Aktionen auch im Globalen listener? Für kleinere Projekte "ok". Wenn ich da an unseres im Geschäft denke, die Klasse wäre mehr als 100.000 Zeilen Code lang! Du kommst mit den Idendifizerungen ja garnicht mehr nach.
 
M

Marcinek

Gast
Hi ;)

Hast du dir das Antipatern selbst überlegt, oder gibt es eine Quelle dafür?

Leider kann ich einige Punkte nicht anchvollziehen:

Verletzung des Separation of Concerns-Prinzips

Meiner Ansicht nach: Nein - Ich habe einen oder mehrere Dispatcher ( Listener ), die folgende Eigenschaften erfüllen:
- Skallierbar: Ich kann soviele Funktionen dort unterbringen, wie ich möchte, denn ich muss meiner Map nur sagen, welcher Command von welcher Klasse gehandelt wird. Der Handler sollte so gewählt werden, so dass er keinen orthogonalen Code enthält.
- Kaskadierbar: Ich kann leicht das Grundgerüst anpassen, so dass ich pro ActionCommand mehrere Aktionen ausführe, indem ich die Hashmap als OpenHash implementiere
- Polymorphy.. Ich kann semtliche Features nutzen, wie Vererbung, überlagerung...

Im Prinzip genau, wie deine Methode, jedoch gibt es vorher einen oder mehrere Dispatcher.

Der Ansatz skaliert nicht. Bei einer Anwendung mit vielen Komponenten (etwa ein Taschenrechner mit vielen Funktionen als MenuItems) müssen viele (eigentlich unnötige) Tests durchgeführt werden. Die zentrale Methode wird total unübersichtlich.

Skalierbarkeit siehe oben. Die zentrale Methode sieht aus wie Folgt:

Java:
actionPerformed () {
  if(Map.contains (ActionCommnad) {
     Map.get(ActionCommand).execute(Context, Source)
} else {
  // Hier könnte die Anfrage eine Exception auslösen, oder hinweis, oder einen anderen (kaskadierenden) Dispatcher für die Action suchen.
}

}

Sieht in meinen Augen nicht so komplex aus.

Schlecht mehrfach verwendbar: Während Eigenschaften wie "enabled" in einer Action zentral geändert werden können, muss hier der Status jeder Komponente einzeln gesetzt werden.

Eine Action, wie copy, paste, delete, close, kann man einmal implementieren und dann dem Dispatcher unter Applikationsweiten einheitlichen Namen hinterlegen.

- Anfällig für Tippfehler: Die Identifikation erfolgt über Strings. Vertippt man sich, führt das nicht zu Compiletime-Fehlern, sondern Problemen zur Laufzeit

Dies kann man meiner Einsicht nach nicht wirklich als Argumentationsgrund nehmen. Normalerweise würde man mit Konstanten arbeiten und das minimiert diesen Fehler auf quasi null.

Probleme beim Refactoring: Wird eine Komponente gelöscht, kann ungenutzer Code im MonsterListener zurückbleiben, ohne dass man es merkt. Wird die Funktionalität zweier Fenster zusammengeführt, können sich Komponenten gleichen Namens ungewollt überdecken (d.h. der falsche Code wird aufgerufen)

Es gibt in der Tat keine Bindung zwischen Ausführender Klasse und dem ActionCommand (String). Nichtverwendeter Code bleibt stehen.

Das Problem der Zusammenführung kann leicht gelöst werden, indem geeignete Prefixes für die ActionCommands genutzt werden, die dann eine Prefixfreiheit für alle anderen UIs garantieren.

Fazit: Ich finde diesen Ansatz nicht schlecht und er kann bestimmt in der einen oder anderen Anwenung relevant sein.
Nämlich dann, wenn ich zu einer UI genau einen Controller habe und der Controller für mich alle Events der UI handelt.

Weiterhin bietet es eine Basis dafür um ellenlange if-else Konstruktionen auch in anderen Anwendungen, als dem Eventhandeling, abhilfe zu schaffen.

In unseren Programmen haben wir Controller, die für genau eine UI zuständig sind. Der Controller wacht über den kompletten Lebenszyklus einer UI und führt lediglich Operationen zur visualisierung seines Models aus. Obwohl mittlerweilse der Contoller auf Typen von Aktionen, wie Listen, Buttons, Trees, separat handeln kann, bleibt bei mehreren Aktionen eines Typs noch eine if/else verzweigung.

~~~

Ich möchte noch dazu sagen, dass ich nicht willentlich gesagt habe "macht man so". Sollte dies irgentwoe so verstanden worden sein, so möchte ich micht korrekt fassen: "Könnte man so machen".
Ich würde mir niemals ermessen zu sagen "so und nicht anders" ;) Bei der Beurteilung des, wie man es machen sollte, spielen sehr viele Faktoren eine Rolle und ich denke, dass sich das ab hier jeder selbst denken kann ^^

Zu guter letztz:

Deine Lösung fällt hingegen aus meiner Sicht auf das Niveau vor Einführung der strukturierten (geschweige denn objektorientierten) Programmierung zurück.

Wie ich oben gezeigt habe, ist dies nicht der Fall. Lediglich die Stringzuweisung ist in dem Sinne lose gekoppelt und kann nicht ohne weiteres von dem Compiler geprüft werden.

Gruß,

Marcin

P.S.: Ich hoffe, dass du dich auch auf meinen Beitrag zur Ergänzung der von mir genannten if else Konstruktion gestützt hast =)
 
Zuletzt bearbeitet von einem Moderator:

Marco13

Top Contributor
Ich wurde selbst schon mit dem MonsterListener-Antipattern konfrontiert. Und deswegen propagiere ich im allgemeinen entweder anonyme Listener (z.B. für ChangeListener oder einen kleinen MouseListener, ggf. in kleinen Beispielen auch mal für einen ActionListener), und verweise darauf, dass bei "richtigen" Anwendungen für Aktionen [c]Action[/c]s (sic) das Mittel der Wahl sind. Spätestens wenn die gleiche Action an mehreren Components hängen soll hat das unschätzbare Vorteile, und wenn dann noch Mnemonics und Tooltips dazukommen ist alles andere nur unübersichtliches Gefrickel.

Die Punkte, die Landei aufgezählt hat stimmen meines Erachtens. Es gibt sicher Grenzfälle. In manchen Fällen könnte es OK sein, den ActionListener nur als Dispatcher zu verwenden, und dort wirklich nur einzelne Methodenaufrufe reinzuschreiben (was du wohl mit "orthogonalem Code" meintest)
Java:
if      (command.equals(COM_A)) execute(new CommandA());
else if (command.equals(COM_B)) execute(new CommandB());
else if (command.equals(COM_C)) execute(new CommandC());
else if (command.equals(COM_D)) execute(new CommandD());
aber ich glaube, auch da gibt es meistens elegantere und flexiblere Lösungen. Dazu zählt aber nicht die angesprochene Map, die IMHO nach einem Workaround aussieht.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
M Wie kann ich eine Methode aus einem Interface in eine Klasse implementieren, so dass sie ihre Funktion ausführt? Java Basics - Anfänger-Themen 7
N Abfragen eines Textes aus einem JTextField in Java, Funktion, CardLayout, Java Basics - Anfänger-Themen 2
T Klassen Funktion in einem Funktionsaufruf definieren Java Basics - Anfänger-Themen 3
G Pause-Funktion in einem mp3-Player Java Basics - Anfänger-Themen 2
P Funktion einer anderen Klasse aus einem Dialog aufrufen Java Basics - Anfänger-Themen 8
E Hilfe bei rekursiver Funktion Java Basics - Anfänger-Themen 3
FunkyPhil94 Wert in einer Lambda Funktion erhöhen Java Basics - Anfänger-Themen 3
M Eine Funktion zuweisen Java Basics - Anfänger-Themen 3
V Die Funktion des neuen Schlüsselworts in Java Java Basics - Anfänger-Themen 1
M variable in anderer funktion aufrufen Java Basics - Anfänger-Themen 10
_user_q Wie eine Methode/Funktion aus einer Klasse mit Constructor aufrufen? Java Basics - Anfänger-Themen 20
nelsonmandela Problem bei Ausgabe einer Switch - Case Funktion Java Basics - Anfänger-Themen 5
W GUI - JButton ohne Funktion? Java Basics - Anfänger-Themen 24
J Rekursive Funktion und return statement Java Basics - Anfänger-Themen 3
F Wie kann ich eine Funktion schreiben, die nur in bestimmten Fällen einen Wert zurückgibt? Java Basics - Anfänger-Themen 5
R Buttons ohne Funktion Java Basics - Anfänger-Themen 2
I Generische Funktion Java Basics - Anfänger-Themen 3
E Pervasive PSQL insert funktion geht nicht Java Basics - Anfänger-Themen 9
C Java Funktion: externe Daten vom Internet einbinden Java Basics - Anfänger-Themen 2
T Log Funktion erstellen Java Basics - Anfänger-Themen 1
H Den Wert einer rekursiven Funktion bestimmen Java Basics - Anfänger-Themen 5
J In main() Datei geöffnet, von anderer Funktion beschreiben Java Basics - Anfänger-Themen 3
M toDouble Funktion Java Basics - Anfänger-Themen 3
Tino1993 Ellipse über draw Funktion ohne spur wandern lassen Java Basics - Anfänger-Themen 6
X Ackermannsche Funktion Java Basics - Anfänger-Themen 32
F Arrays: Mathematische Funktion Java Basics - Anfänger-Themen 19
P Dezimal zu Hexadezimalzahl Funktion Java Basics - Anfänger-Themen 5
S Verwenden von throw Exception an der Funktion Java Basics - Anfänger-Themen 2
M Arrays in Funktion Kopieren und Bearbeiten Java Basics - Anfänger-Themen 4
B Funktion mit mehreren Rückgabewerten aka Prozeduren? Java Basics - Anfänger-Themen 12
J Dynamisches Array durch split()-Funktion? Java Basics - Anfänger-Themen 3
D Funktion nur 1 Rueckgabewert Java Basics - Anfänger-Themen 9
M Wie lang eine Funktion/Methode? Java Basics - Anfänger-Themen 51
N den inhalt eines array per funktion ausgeben Java Basics - Anfänger-Themen 8
R Ackermann Funktion Java Basics - Anfänger-Themen 2
B Treetable (rekursive Funktion) aufbauen von Datenbank Java Basics - Anfänger-Themen 4
D Funktion zwei Arraylisten zu verleichen ob gleich funktioniert nicht Java Basics - Anfänger-Themen 26
T Rekursion Warum bricht meine Funktion nicht ab Java Basics - Anfänger-Themen 4
N Funktion funktioniert nicht immer Java Basics - Anfänger-Themen 6
E Contain-funktion überlisten Java Basics - Anfänger-Themen 4
J Division ohne Arithmetische Funktion Java Basics - Anfänger-Themen 2
S Funktion in Klasse auslagern Java Basics - Anfänger-Themen 4
J Problem mit Boolean bei Funktion! Java Basics - Anfänger-Themen 5
S Gibt es eine Funktion, die gewissermaßen eine Reihe von instanceOf() vereinheitlicht? Java Basics - Anfänger-Themen 19
D Nullstellen einer Funktion 3. Grades mit Horner Schema Java Basics - Anfänger-Themen 6
Aprendiendo Gibt es in der JAVA-API eine Funktion, die eine Dezimalzahl in eine binäre Zahl umwandelt? Java Basics - Anfänger-Themen 8
D Funktion gibt Dimension zurück Java Basics - Anfänger-Themen 11
A Rekursion Funktion in eine Iterativ Funktion umwandeln Java Basics - Anfänger-Themen 9
T static String Variable wird nur beim ersten aufruf durch eine Funktion geändert. Java Basics - Anfänger-Themen 16
B Zugriffe in einer Klasse / Funktion Java Basics - Anfänger-Themen 9
T Koordinatensystem zeichnen - Variablen merken? Quadratische Funktion zeichnen? Java Basics - Anfänger-Themen 5
J Array innerhalb einer Funktion mehrfach iniatilisieren Java Basics - Anfänger-Themen 4
T Lambda-Funktion bei Binärbäumen Java Basics - Anfänger-Themen 13
J Wie lässt sich der Konstruktor einer Klasse einer Funktion einer Klasse nutzen. Java Basics - Anfänger-Themen 4
M Thread.sleep() Funktion Java Basics - Anfänger-Themen 1
B OOP Wie benutze ich die Main Funktion richtig? Java Basics - Anfänger-Themen 10
H Nicht Static Funktion ohne Objekt aufrufen? Java Basics - Anfänger-Themen 6
K Methoden mit den Namen accept. Welche Funktion haben diese? Java Basics - Anfänger-Themen 2
E Compare-Funktion bei eigener Klasse Java Basics - Anfänger-Themen 4
S Threads run - Funktion wird nur einmal ausgeführt. Java Basics - Anfänger-Themen 8
B Anwender soll mathematische Funktion eingeben können, Einfachster Fnktionsplotter Java Basics - Anfänger-Themen 4
R If Funktion funktioniert nicht :P Java Basics - Anfänger-Themen 3
H Funktion in Hintergrund und Vordergrund ausführen Java Basics - Anfänger-Themen 11
S Funktion die mir fuer einen String eine Zahl zwischen 0.0 und 1.0 zurueckliefert..? Java Basics - Anfänger-Themen 9
S Funktion eines Stacks Java Basics - Anfänger-Themen 4
T Integer-Objekt über Hash-Funktion in Array ablegen Java Basics - Anfänger-Themen 1
S Separate Funktion für JUnit-Test Java Basics - Anfänger-Themen 3
D Keine Funktion bei "else" Java Basics - Anfänger-Themen 5
S timer funktion mit javax panel Java Basics - Anfänger-Themen 3
F Funktion eines JButton in einen Vektor verlagern Java Basics - Anfänger-Themen 4
X Eval-Funktion mit Variable Java Basics - Anfänger-Themen 2
T Screenreader Funktion Java Basics - Anfänger-Themen 2
S Wertetabelle einer Funktion f : R -> R Java Basics - Anfänger-Themen 1
P Methoden suche funktion die char wert ausgibt wenn man numerischen wert und radix angibt Java Basics - Anfänger-Themen 1
1 repaint() Funktion erzeugt Flackern Java Basics - Anfänger-Themen 33
J Taschenrechner Funktion Java Basics - Anfänger-Themen 18
R if funktion ohne else - Bedingung trifft nicht zu, ausgabe nicht nachvollziehbar Java Basics - Anfänger-Themen 7
shiroX OOP Java Funktion implementieren Java Basics - Anfänger-Themen 3
O Debug-Funktion mit Slick - Kleines Problem Java Basics - Anfänger-Themen 5
F Funktion immer zur vollen Stunde? Java Basics - Anfänger-Themen 3
S ResultSet close() in funktion nich möglich. Java Basics - Anfänger-Themen 8
C Meine erste Funktion Java Basics - Anfänger-Themen 12
J Funktion um JSON per Post senden/emfangen Java Basics - Anfänger-Themen 3
G OOP Aus Objekt auf Funktion der erzeuger Klasse zugreifen? Java Basics - Anfänger-Themen 11
D Binäre Suche für Integerarray in rekursiver Funktion Java Basics - Anfänger-Themen 5
M Interface als Konstruktorparameter nutzen, um Funktion zu plotten Java Basics - Anfänger-Themen 14
NR_EIGHT Benutzereingabe in Funktion verpacken Java Basics - Anfänger-Themen 4
J Funktion definieren und ausfuehren Java Basics - Anfänger-Themen 27
D Loop Funktion für Robot Klasse Java Basics - Anfänger-Themen 5
N mathematische Funktion Java Basics - Anfänger-Themen 29
R Programm verstehen, Funktion Java Basics - Anfänger-Themen 4
C Automatisches Ausfuehren einer Funktion beim Laden eines Jar files Java Basics - Anfänger-Themen 3
O Nicht Standard Form boolesche Funktion in Standard Form parsen Java Basics - Anfänger-Themen 3
F Sleep Funktion Java Basics - Anfänger-Themen 12
S Euklid Funktion Java Basics - Anfänger-Themen 8
H Funktion mit Argumenten Java Basics - Anfänger-Themen 5
Q Random Funktion JButtons Java Basics - Anfänger-Themen 6
O Probleme mit der repaint-Funktion Java Basics - Anfänger-Themen 6
F Eine Frage über paint() Funktion Java Basics - Anfänger-Themen 2
S Parameterübergabe - identische Funktionen, aber falsche Funktion Java Basics - Anfänger-Themen 5

Ähnliche Java Themen

Neue Themen


Oben