# AbstractAction



## minzel (23. Jul 2006)

Ich weiß das Thema ist hier schon sehr oft aufgetaucht. Aber ich habe wohl bislang ein großen logischen Fehler begangen auf den man mich nach einer Klausur aufmerksam gemacht hat. Verstanden habe ich die richtige Alternative allerdings immer noch nicht.

Meine bisherige Vorgehensweise mit einem ActionListener: (Ausgehend von einer MenüBar)


```
private Action handler;
private JMenuBar menuBar;
...
menuBar = new JMenuBar();
this.setJMenuBar(menuBar);
...
JMenu fileMenu = new JMenu("Datei");
menuBar.add(fileMenu);
...
handler = new Action("Neues Spiel");
fileMenu.add(handler);

handler = new Action("Beenden");
fileMenu.add(handler);
...
class Action extends AbstractAction {
		
	public Action(String text) {
            super(text);
        }

	public void actionPerformed(ActionEvent e) {

		if ( getValue(NAME).equals("Neues Spiel")) {
		  // Starte neues Spiel
		}

		if ( getValue(NAME).equals("Beenden") ) {
		  // Beende Spiel
		}
}
```

Ich hoffe mir kann jemand weiter helfen das Problem zu verstehen waum man es so nicht macht und wie richtig.


----------



## foobar (23. Jul 2006)

So was habe ich ja noch nie gesehen. Also du mußt für jeden Button eine eigene Action erstellen, sonst macht das keinen Sinn und du kannst genauso gut setActionCommand verwenden.
Der Sinn von Actions ist es eben eine Aktion zu kapseln und wenn nötig an mehreren Stellen z.b. JMenu, JButton zu verwenden. D.h. du schreibst dir eine Action NewGame und fügst diese einem Button und einem JMenu hinzu. Jetzt haben das JMenuItem und der Button das slebe Verhalten, wenn sie angeklickt werden. Wenn du die Action jetzt auf setEnabled(false) setzt, werden beide Komponenten disabled ohne daß du dich darum kümmern mußt.
Das ist der Vorteil von Actions.

Guckst du hier: http://java.sun.com/products/jfc/tsc/articles/actions/index.html

Viele Grüße
foobar


----------



## minzel (23. Jul 2006)

Danke dir. Habe es halt bisher immer so gemacht. Kann aber aus der Seite die du gepostet hast nicht wirklich schlau werden. Hast du vieleicht ein kleines Beispiel was mir die Verknüpfung zeigt?


----------



## foobar (23. Jul 2006)

Hmm, da steht doch alles drin was man wissen muß. Ansonsten Javadocs und Boardsuche.


----------



## André Uhres (23. Jul 2006)

```
//package schnipsel2; 
/* 
* ActionDemo.java 
*/ 
import java.awt.event.*; 
import javax.swing.*; 
public class ActionDemo extends JFrame { 
    private JPanel mainPanel = new JPanel(); 
    private JButton btExit = new JButton(); 
    private JToggleButton btDisable = new JToggleButton(); 
    private JLabel actionLabel = new JLabel("Press F6 = Toggle Disable,     Press Shift+F2 = Exit"); 
    private Action exit; 
    private static final String EXIT_ACTION = "Exit"; 
    private static final String DISABLE_ACTION = "Disable"; 
    private boolean selectDisable; 
    public ActionDemo() { 
        super("Action Demo"); 
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        setSize(400, 300); 
        setLocationRelativeTo(null); 
        mainPanel.add(btDisable); 
        mainPanel.add(btExit); 
        mainPanel.add(actionLabel); 
        add(mainPanel); 
        exit = setAction(EXIT_ACTION, KeyEvent.VK_F2, InputEvent.SHIFT_MASK); 
//        setAction(DISABLE_ACTION); 
        setAction(DISABLE_ACTION, KeyEvent.VK_F6, 0); 
    } 
    private Action setAction(final String action){ 
        return setAction(action, 0, 0); 
    } 
    private Action setAction(final String action, final int key, final int modifier){ 
        Action a = new AbstractAction(action){ 
            public void actionPerformed(ActionEvent e) { 
                doAction(action); 
            } 
        }; 
        if(action.equals(DISABLE_ACTION)) 
            btDisable.setAction(a); 
        if (action.equals(EXIT_ACTION)) 
            btExit.setAction(a); 
        if(!(key == 0 && modifier == 0)){ 
            mainPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 
                    KeyStroke.getKeyStroke(key, modifier), action); 
            mainPanel.getActionMap().put(action, a); 
        } 
        return a; 
    } 
    private void doAction(final String action){ 
        if(action.equals(EXIT_ACTION)){ 
            dispose(); 
        }else if(action.equals(DISABLE_ACTION)){ 
            selectDisable = ! selectDisable; 
            btDisable.setSelected(selectDisable); 
            exit.setEnabled(!btDisable.isSelected()); 
        }else{ 
            assert false : "invalid Action"; 
        } 
    } 
    public static void main(String[] args) {new ActionDemo().setVisible(true);} 
}
```


----------



## minzel (24. Jul 2006)

Ich danke dir ... ich werd mir den Quelltext mal verinnerlichen. Sieht irgendwie komplizierter aus als wie ich es immer gemacht habe.


----------



## Beni (24. Jul 2006)

Naja, Andrés Lösung unterscheidet sich nicht allzusehr von minzels Variante :wink:
Was foobar meinte, dürfte eher in diese Richtung gehen:


```
package forum;

import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class Beispiel extends JFrame{
    private JLabel info = new JLabel( "Nix zu melden" );
    
    public Beispiel(){
        // Ein Frame mit einem Menü, und ein paar Buttons zusammenbasteln
        JMenu menu = new JMenu( "Menü" );
        JMenuBar menuBar = new JMenuBar();
        menuBar.add( menu );
        setJMenuBar( menuBar );
        
        // Die Aktionen erzeugen
        Action newGame = new NewGameAction( this );
        Action exit = new ExitAction();
        
        // Dem Menü "New Game" und "Exit" hinzufügen
        menu.add( new JMenuItem( newGame ));
        menu.add( new JMenuItem( exit ));
        
        // Ein Panel mit zwei Knöpfen "New Game" und "Exit" erzeugen
        JPanel buttons = new JPanel( new GridLayout( 1, 2 ));
        buttons.add( new JButton( newGame ));
        buttons.add( new JButton( exit ));
        
        Container content = getContentPane();
        content.setLayout( new GridLayout( 2, 1 ));
        content.add( info );
        content.add( buttons );
        
        pack();
    }
    
    public void tellInfo( String text ){
        info.setText( text );
    }
    
    public static void main( String[] args ) {
        Beispiel beispiel = new Beispiel();
        beispiel.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        beispiel.setLocationRelativeTo( null );
        beispiel.setVisible( true );
    }
}

// Für die erste Aktion eine Klasse
class ExitAction extends AbstractAction{
    public ExitAction(){
        // Das Aussehen festlegen
        putValue( NAME, "Beenden" );
        putValue( SHORT_DESCRIPTION, "Das Spiel beenden" );
    }
    
    public void actionPerformed( ActionEvent e ) {
        // Die Logik der Aktion implementieren (bzw. aufrufen)
        System.exit( 0 );
    }
}

// Für die zweite Aktion eine Klasse
class NewGameAction extends AbstractAction{
    private Beispiel beispiel;
    
    public NewGameAction( Beispiel beispiel ){
        this.beispiel = beispiel;
        putValue( NAME, "Neues Spiel");
        putValue( ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_F6, 0 ));
    }
    
    public void actionPerformed( ActionEvent e ) {
        beispiel.tellInfo( "Spiel gestartet" );
        setEnabled( false );
    }
}
```


----------



## foobar (24. Jul 2006)

Also ich finde Andres Beispiel auch etwas verwirrend.

@Beni genau so habe ich das gemeint


----------



## muckelzwerg (27. Jul 2006)

Ich habe zwei Fragen :

Warum eine eigene Actionklasse ?
Reicht es nicht aus, eine einzelne AbstractAction zu erzeugen und
deren actionPerformed zu setzen ?
Wofür braucht man die Klasse, wenn man doch eigentlich nur eine Instanz haben will.
(evtl. irgendwleche Vorteile bei der Übergabe an die Auslöser oder sowas ?)

Sind Actions und eigene ActionListener in reines entweder-oder ?
Der ActionListener bietet doch eigentlich nur die Möglichkeit
Actions in einen bestimmten Kontext zu setzen.
Dann kann die Action ganz unabhängig ihren Code enthalten, 
und der Listener führt "Spezialaufgaben" aus, wenn diese eine Action
von seinen zu überwachenden Componenten ausgelöst wird.
Oder gibt es noch mehr Anwendungsfälle für eine Kombination ?

  --  --  muckelzwerg


----------



## Beni (27. Jul 2006)

muckelzwerg hat gesagt.:
			
		

> Warum eine eigene Actionklasse ?
> Reicht es nicht aus, eine einzelne AbstractAction zu erzeugen und
> deren actionPerformed zu setzen ?
> Wofür braucht man die Klasse, wenn man doch eigentlich nur eine Instanz haben will.
> (evtl. irgendwleche Vorteile bei der Übergabe an die Auslöser oder sowas ?)


Der Vorteil: *Trennung von Code, gegenseitige Unabhängigkeit, Konzentration von zusammengehörenden Daten an einer Stelle*
Deine Variante mag ja bei 2 Aktionen noch funktionieren, aber was glaubst du, wie übersichtlich es noch ist, wenn du 20 Aktionen hast?
Und auch eine neue Aktion einbauen, ist bei mehreren Action-Klassen wesentlich einfacher, als wenn du alles an einem Ort zusammenwurstelst.



			
				muckelzwerg hat gesagt.:
			
		

> Sind Actions und eigene ActionListener in reines entweder-oder ?


Grundsätzlich kann man sie wild durcheinander mischeln.


			
				muckelzwerg hat gesagt.:
			
		

> Der ActionListener bietet doch eigentlich nur die Möglichkeit
> Actions in einen bestimmten Kontext zu setzen.
> Dann kann die Action ganz unabhängig ihren Code enthalten,
> und der Listener führt "Spezialaufgaben" aus, wenn diese eine Action
> ...


Ich verstehe nicht, was du damit meinst. Actions sind ja selbst schon ActionListener, was willst du da noch weiter hinzufügen?


----------



## muckelzwerg (27. Jul 2006)

Beni hat gesagt.:
			
		

> Deine Variante mag ja bei 2 Aktionen noch funktionieren, aber was glaubst du, wie übersichtlich es noch ist, wenn du 20 Aktionen hast?
> Und auch eine neue Aktion einbauen, ist bei mehreren Action-Klassen wesentlich einfacher, als wenn du alles an einem Ort zusammenwurstelst.


Versteh ich nicht, sorry.
Wieso ist es unübersichtlich, wenn ich alles an einem Ort mache ?
Ich würde die Actions an einem zuständigen Ort erzeugen (Factory oder sowas) und dann den 
Auslösern in die Hand geben.
Jede Action wird am Ende der actionperformed() zurückgesetzt, damit kein Einfluss zwischen den 
Aufrufen ist.




			
				Beni hat gesagt.:
			
		

> Ich verstehe nicht, was du damit meinst. Actions sind ja selbst schon ActionListener, was willst du da noch weiter hinzufügen?


Trennung von Code.
Die Action enthält den Code, der für ihre allgemeine Ausführung nötig ist.
Unabhängig davon, wer sie aufruft etc. (es sei denn der Aufrufer oder andere parameter
sind für die Action essentiell, damit sie ihre Arbeit tun kann)
Der Actionlistener überwacht eine Menge von Komponenten und kann sich auf diese zugeschnitten
verhalten.
(blödes) Beispiel :
Die Action führt einen Arbeitsgang aus, für den es unwichtig ist, zu wissen,
wer in aufruft, und wie oft er ausgeführt wird.
Ein Listener lauscht auf alle Buttons eines Panels, ob sie diese Action ausführen und protokolliert das,
oder macht sonstwas ...
Damit bleibt die Action "unbelastet" von kontextabhängigem Code, und macht nur ihre Arbeit.

Das Beispiel ist nicht gut, mir fällt aber grad nichts besseres ein.
Ich dachte dabei an z.B. Statistikfunktionen für GUIs.
Wenn ich wissen will, wie wird eine Action am meisten aufgerufen (Buttons, Menü, Shortkey ...) etc.
Sowas gehört für mich nicht in die Action hinein.

Ich hab ja nicht behauptet, dass es Pflicht ist.
Ich suche eher nach möglichen Anwendungsfällen.

  --  --  muckelzwerg


----------



## Beni (27. Jul 2006)

muckelzwerg hat gesagt.:
			
		

> Wieso ist es unübersichtlich, wenn ich alles an einem Ort mache ?


In deinem ersten Code hast du einfach ein paar "if's" eingebaut. Jetzt musst du darauf achten, dass die if's das richtige machen (richtiger Test, richtige Methode aufrufen). Du musst darauf achten, dass zwei if's nicht dieselbe Bedingung haben. Wenn du was einfügst, musst du all diese Dinge von neuem testen. Du musst auf die richtige "Verdrahtung" zwischen Componenten und Listenern achten...

Wenn du das dezentral organisierst, verschwindet diese Verwaltung. Genauer gesagt, Swing übernimmt die Verwaltung durch seine interne Mechanismen, und im Endeffekt hast du weniger Arbeit.



> Jede Action wird am Ende der actionperformed() zurückgesetzt, damit kein Einfluss zwischen den
> Aufrufen ist.


Verstehe nicht ganz was das bedeuten soll. Eine Action verändert irgendwo einen Wert, schreibt was auf die Festplatte... was soll man da zurücksetzen?


> Beni hat gesagt.:
> 
> 
> 
> ...


Naja, möglich ist das... aber dein Beispiel scheint mir doch ein eher seltener Anwendungsfall zu sein :wink:


----------



## muckelzwerg (28. Jul 2006)

Hm, ich fürchte hab das blöd ausgedrückt.
Ich erzeuge für jede logische Aktion eine Java Action.
Aber die erzeuge ich als Instanz von AbstractAction und nicht
über eine Zwischenklasse.
Ich habe sicher nicht gemeint, eine EINZIGE Action für das komplette Programm zu erzeugen,
die dann bei Ausführung nen switch macht.
(dann kann ich ja gleich wieder den Listener nehmen)
Mir geht es um die Ableitung der AbstractAction.
Das macht in meinen Augen nur Sinn, wenn ich die AbstractAction erweitern muss, weil sie noch irgendwas zusätzliches braucht, und ich
von dieser erweiterten Action mehrere Instanzen brauche.
Ich hab hier eine kleine GUI, und ca 5 Actions.
Keine von denen braucht mehr, als die AbstractAction bereits liefert,
also instanziiere ich sie auch von da.
Irgendwie stört mich die Vorstellung, dass es für einen Arbeitsgang
beliebig viele Actions geben kann.
Ich hab lieber eine Action pro Arbeitsgang, die dann mehreren Aufrufern zugängig gemacht wird.
Daher auch die Resetfunktion, falls man der Action mehr beibringt,
und irgengwelche Attribute zum nächste Aufruf überleben könnten.

Actions besitzen von Haus aus keine Synchronisation oder sowas,
weil davon ausgegangen wird, dass alle aus dem Gleichen Thread abgefeuert werden ?


Ja das Beispiel ist etwas ... ungewöhnlich.
Eigentlich kam ich ja auch von der anderen Richtung,
weil ich unsicher war, ob es evtl. doch ein "unglaublich tolles" Pattern gibt, das Actions und zusätzliche Listener kombiniert,
und "immer" nützlich ist. 

greetz

  --  --  muckelzwerg


----------

