# (Action)Listener auslagern?



## 0001001 (26. Mrz 2008)

Hi,

würde mein Programm gerne von Anfang an sauber aufbauen. D.h. keine 1000-Zeilen-Klassen und keine ActionListener als intere Klassen.

Hier mal ein simples Beispiel und verschiedene Implementierungsmöglichkeiten;

*1. Möglichkeit *

```
public class FileMenu extends JMenu implements ActionListener{
	private JMenuItem exit;
	
	public FileMenu(){
		this.setText("File"); 		
		exit = new JMenuItem("Exit");
		exit.setActionCommand("exit");
		exit.addActionListener(this);
		this.add(exit);
	}
	
	public void actionPerformed(java.awt.event.ActionEvent e){
		if(e.getActionCommand().equalsIgnoreCase("exit")){
			System.exit(0);
		}
	}
}
```

*2. Möglichkeit*

```
public class FileMenu extends JMenu{
	private JMenuItem exit;
	
	public FileMenu(){
		this.setText("File"); 		
		exit = new JMenuItem("Exit");
		exit.setActionCommand("exit");
		exit.addActionListener(new ActionListener(){
			public void actionPerformed(java.awt.event.ActionEvent e){
				if(e.getActionCommand().equalsIgnoreCase("exit")){
					System.exit(0);
				}
			}
		});
		this.add(exit);
	}
}
```
*
3. Möglichkeit*

```
public class FileMenu extends JMenu{
	private JMenuItem exit;
	
	public FileMenu(){
		this.setText("File"); 		
		exit = new JMenuItem("Exit");
		exit.setActionCommand("exit");
		exit.addActionListener(new MyActionListener());
		this.add(exit);
	}
}

class MyActionListener implements ActionListener{
	public void actionPerformed(java.awt.event.ActionEvent e){
		if(e.getActionCommand().equalsIgnoreCase("exit")){
			System.exit(0);
		}
	}
}
```



*Was stellt ihr mit den (Action)Listenern an? Auslagern in eine extra Datei und aufufende Datei per Konstruktor übergeben?*


----------



## L-ectron-X (26. Mrz 2008)

Du kannst auch mit Actions arbeiten.
Dazu schreibst du dir eine Klasse, die von AbstractAction erbt, überschreibst darin die actionPerformed()-Methode und übergibst dann die Action deinem JButton, JMenuItem etc.


----------



## 0001001 (26. Mrz 2008)

Das wäre eine Möglichkeit, danke! 
Welche Vorteile hat es, mit Actions zu arbeiten?


----------



## Marco13 (26. Mrz 2008)

Alles subjektiv: 

Viel entscheidender, als die Frage, wo er steht, ist IMHO die Frage, was er macht. Ich finde es nicht so schön, das ActionCommand abfragen zu müssen. D.h. man sollte IMHO insbesondere NICHT 20 verschiedene Buttons haben, und dort EINEN ActionListener dranhängen, der dann entscheidet, was zu tun ist. 

Ich finde es "schön", wenn ActionListener (oder auch andere Listener, die nicht über sowas wie eine "Action" abgebildet werden können) nichts anderes machen, als NUR eine (private) Methode aufzurufen. Dann kann man mit einem anonymen Listener (also mit der 2. Möglichkeit) die Verbinung zwischen dem _Erstellen_ des Buttons und dem, was der button _macht_ ganz elegant und praktisch herstellen (Ähnlich wie bei den "Slots" in QT, falls du das kennst). 

Die 1. Möglichkeit gefällt mir nicht, weil das dazu verleitet, eine 1000-Zeilen-Mega-ActionPerformed zu schreiben.
Die 3. Möglichkeit gefällt mir nicht, weil man für jeden Scheiß (also z.B. diese kleine, billige Verbindung zwischen "button" und "System.exit") eine eigene Klasse erstellen muss.
Die 2. Möglichkeit finde ich (richtig angewendet) am einfachsten, elegantesten und flexibelsten.


----------



## L-ectron-X (26. Mrz 2008)

0001001 hat gesagt.:
			
		

> Das wäre eine Möglichkeit, danke!
> Welche Vorteile hat es, mit Actions zu arbeiten?


Du kannst für verschiedene Komponenten, die Actions unterstützen, einfach an beliebigen Stellen die gleiche Aktion abarbeiten lassen.
Wenn du z.B. ein JMenuItem und in einen JButton in einer JToolBar hast, die die gleiche Funktion anbieten, kannst du durch Übergabe einer vorher erstellten Action die gleiche Aktion ausführen lassen. Das kannst du so weit treiben, dass du auf beiden Komponenten die gleiche Aufschift und das gleiche Icon zu sehen bekommst.
Außerdem bleibt dein Code übesichtlich und ist daher auch recht gut zu warten.


----------



## 0001001 (26. Mrz 2008)

Vielen Dank! 

Eine Frage noch: Wenn ich die 2. Möglichkeit verwende, dann möchte ich, so wie du sagst, nur eine Methode aufrufen, in der dann alles geregelt wird. Was aber, wenn die Methode beispielsweise einen Text in einem Textfeld ändern soll. Übergibst du dann das Textfeld der privaten Methode?


----------



## Marco13 (26. Mrz 2008)

Da gibt's eigentlich nur 2 Alternativen: Entweder, man übergibt das TextField, oder das TextField liegt als Field in der Klasse - was man macht, kommt wohl auf den jeweiligen Fall an...


----------



## 0001001 (26. Mrz 2008)

Prima, genau das wollte ich wissen! Danke!

Ist es eigentlich generell besser, GUI Elemente als Klassenvariablen zu definieren, also so:


```
public class Test{
   private JLable label;
   public Test(){
      label = new JLabel("foo");
   } 
}
```

oder hängt das von der Situation ab?


----------



## Marco13 (26. Mrz 2008)

Naja... Der einzige Grund, wesegen man eni Label als Klassenvariable speichern sollte, ist, dass man später noch den Text ändern will - z.B. nach einem ActionEvent oder so :wink: 

Aber es gibt immer viele Möglichkeiten, WIE man sowas schreiben kann. Nochmal zu der Frage mit dem Textfield: Wenn in der ActionPerformed-Methode z.B. NUR der Text gesetzt werden soll, dann kann das ja auf verschiedene Arten passieren...

Das TextField wird an eine Methode übergeben, und dort gefüllt

```
class Foo
{
    void bar()
    {
        JTextField textField = new JTextField();
        ...
        button.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                 setTheText(textField);
            }
        });
    }

    void setTheText(JTextField textField)
    {
         String text = ...
         textField.setText(text);
    }
}
```
Meistens gibt es geschicktere Lösungen für sowas, aber sowas KÖNNTE sinnvoll sein, wenn man z.B. noch an vielen anderen Stellen den gleichen Text in ANDERE TextFields setzen muss - dann ruft man eben überall die setTheText-Methode mir dem jeweiligen TextField auf.

Eine andere Möglichkeit wäre (entgegen meiner Empfehlung, in der actionPerformed NUR eine Methode aufzurufen :wink: ) das TextField final machen und den Text direkt zu setzen...

```
class Foo
{
    void bar()
    {
        final JTextField textField = new JTextField();
        ...
        button.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                 textField.setText(computeTheText());
            }
        });
    }

    private String computeTheText()
    {
         String text = ...
         return text;
    }
}
```
Das wäre sinnvoll (und etwas allgemeiner anwendbar als das erste) wenn man den Text an mehreren Stellen braucht, z.B. ihn ins Textfeld UND in eine Datei schreiben will.

Und natürlich könnte man es auch so machen, wie angedeutet

```
class Foo
{
    private JTextField theTextField = new JTextField();


    void bar()
    {
        button.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                 setTheText();
            }
        });
    }

    void setTheText()
    {
         String text = ...
         theTextField .setText(text);
    }

}
```
Das wäre wohl sinnvoll, wenn in "setTheText" noch mehr gemacht wird (z.B. 5 TextFields und 4 Labels geändert - die sollte man ja nicht alle als Parameter übergeben...). Natürlich könnten von dort aus dann wieder setTheText(theTextField) oder computeTheText() aus dem 1. bzw. 2. Beispiel aufgerufen werden ... Hm.
Die Freiheiten, die einem die Gödelnummerierungen über dem UNICODE-Zeichensatz bieten, sind beschränkt durch die Frage, was gültige Java-Syntax ist und was die gewünschte Aufgabe erfüllt - aber dann bleibt immernoch verdammt viel übrig... :wink:


----------



## L-ectron-X (27. Mrz 2008)

Nur noch mal der Hinweis: Eine Klassenvariable ist immer static. Wovon ihr hier redet, sind Instanzvariablen.


----------

