# Konzeptionelle Frage: Gui <-> Operationsklasse zusamme



## SBS (18. Sep 2006)

Hallo,

ich habe folgende konzeptionelle Frage:

Ich habe eine Gui Klasse, und eine Agentenklasse.

In der Gui klasse möchte ich nur Gui Elemente haben, in der Agentenklasse sind die Funktionen meines Programms abgebildet.

Wo muss ich nun z.B. einen ActionListener oder KeyListener implementieren, damit dieser auf Reaktionen aus der Gui mit den MEthoden aus der Agentenklasse reagiert?

Möglichkeit 1: Listener in der Gui Klasse:
Funktioniert an sich recht gut, ich erzeuge mir ein Objekt meines Agenten und greife auf die Methoden zu. Der Agent wiederum erzeugt ein Gui Objekt damit die Gui erscheint, da er ausführende Klasse ist. Stellt sich mir die Frage ob dies so sein soll, da ich "das GEfühl" kriege dass die Gui nun zu meiner "Hauptklasse" mutiert, und ob der Agent (der ja eigentlich der Kern des Programmes ist) dann noch ordnungsgemäß funktioniert wenn ich mir ein Objekt von ihm in meiner Gui erzeuge, denn der Agent erzeugt sich bei Bedarf z.B. auch mal selber.

Möglichkeit2: Listener in der Agenten Klasse:
In meiner Gui Klasse greife ich auf den Listener der Agentenklasse zu, ich füge in der Guiklasse z.b. Buttons den Listener über etwas wie button.addActionListener(Agent.Listener) hinzu - Listener ist in diesem Fall ein Objekt der inneren Klasse des ActionsListeners in der Agenten Klasse.
Dann habe ich meinen Agenten voll mit ListenerCode, stellt sich mir die Frage ob DAS Sinn der Sache ist, da der Agent sich übers Netzwerk bewegt, und erfahrungsgemäß (anscheinend) keine Objekte eigener Klassen (ich wollte eine seperate Funktionsklasse für den Agenten erstellen) mit übers Netz transportieren kann (auch nicht wenn diese serializable sind). Nun weiß ich nicht ob es klappt dass der Agent Listener Objekte mit sich nimmt...

Möglichkeit3: Listener in der Agenten Klasse Version 2:
Listener wieder in der Agenten Klasse jedoch nicht aufgerufen aus der Gui sondern Gui-Elemente aufgerufen aus der Agentenklasse und deren Listener hinzugefügt: gui.button1.addAction...()..
Geht natürlich nur wenn diese Gui Elemente visible sind...ob das so gut ist? 




Kann mir jemand ne Möglichkeit nennen wie man sowas "normalerweise" anständig und sauber machen würde?
Bis jetzt bin ich dieses Problem nahezu immer dadurch umgangen, dass ich Funktionen in die Guiklasse mit eingebaut habe und diese dann einfach direkt dadrinnen mit den eigenen Listenern aufgerufen habe - das ist natürlich nicht schön, denn dann ist die Guiklasse keine Guiklasse mehr, sondern eine Allround-Klasse...

hm ja, danke schonmal


----------



## Beni (20. Sep 2006)

Ich würde den Agenten möglichst so designen, dass er nichts von der GUI weiss (Schichtenmodel). Wenn die Agenten total unabhängig von der GUI sind, ist die Logik einfacher, und dementsprechend auch weniger fehleranfällig.
Gehst du so vor, gehören die Listener zur GUI.

Die GUI könnte Listeners bei den Agenten registrieren um über Veränderungen informiert zu werden (das gleiche Konzept wie z.B. ein ActionListener, nur werden die Events von den Agenten verschickt). Wenn z.B. ein neuer Agent generiert wird, wird ein "NewAgentEvent" an den "AgentListener" verschickt, und die GUI (welche einen AgentListener implementiert hat) kann ein neues Fenster aufmachen.

Vielleicht lohnt es sich zwischen GUI und Agenten ein einziges Objekt zu setzen, welches die "Übersicht" wahrt. Dieses Objekt weiss z.B. wieviele Agenten es gibt... die GUI würde dann nicht mehr direkt mit den Agents kommunizieren sondern mit diesem Objekt.

Wenn du Angst hast, die GUI werde zu wichtig: das ist ähnlich wie mit einem Computer: der Prozessor ist das wichtigste Bauteil, aber bei weitem nicht das Grösste...


----------



## SBS (21. Sep 2006)

Hi,

vielen Dank für die Antwort! Hat mir bei meinem Konzept geholfen


----------



## Guest (21. Sep 2006)

Hallo

da ich auch öfters solche Probleme antreffe, würde ich gerne auch noch einige Fragen dazu stellen.

Wenn ich die Interaktion zwischen GUI und Logik so designe wie du vorschlägst Beni, resultiert daraus dann eine bidirektionale Beziehung zwischen GUI-Klasse und dem Agenten, wobei beide nach dem Observer Pattern miteinander kommunizieren?

Da ich nicht sicher bin, ob ich deinen Vorschlag ganz verstanden habe, habe ich folgendes einfaches Beispiel geschrieben:

```
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class GUI implements AgentListener {
    private JFrame mainFrame;
    private JButton button1, button2;
    private Agent agent;
    
    public GUI(Agent agent) {
        this.agent = agent;
        agent.addAgentListener(this);
        buildGUI();
    }
    
    private void buildGUI() {
        mainFrame = new JFrame("main frame");
        button1 = new JButton("button1");
        button1.addActionListener(new Button1Listener());
        button2 = new JButton("button2");
        button2.addActionListener(new Button2Listener());
        mainFrame.setLayout(new FlowLayout());
        mainFrame.add(button1);
        mainFrame.add(button2);
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(null);
        mainFrame.setVisible(true);
    }
    
    public void eventFired() {
        JDialog dialog = new JDialog(mainFrame);
        dialog.add(new JLabel("test"));
        dialog.setLocationRelativeTo(mainFrame);
        dialog.pack();
        dialog.setVisible(true);
    }
    
    class Button1Listener implements ActionListener {

        public void actionPerformed(ActionEvent e) {
            agent.performAction1();
        }      
    }
    
    class Button2Listener implements ActionListener {

        public void actionPerformed(ActionEvent e) {
            agent.performAction2();
        }      
    }
}
```


```
public interface AgentListener {
    
    public void eventFired();
    
}
```


```
import java.util.ArrayList;

public class Agent {
    private ArrayList<AgentListener> listeners;
    
    public Agent() {
        listeners = new ArrayList<AgentListener>();
    }
    
    public void addAgentListener(AgentListener listener) {
        listeners.add(listener);
    }
    
    public void removeAgentListener(AgentListener listener) {
        listeners.remove(listener);
    }
    
    public void fireEvent() {
        for (AgentListener listener : listeners) {
            listener.eventFired();
        }
    }

    public void performAction1() {
        System.out.println("action1 performed");
        fireEvent();
    }
    
    public void performAction2() {
        System.out.println("action2 performed");
    }
    
    public static void main(String[] args) {
        new GUI(new Agent());
    }
}
```

Der Agent meldet sich beim GUI als Listener für die Buttons an und gleichzeitig nimmt das GUI vom Agent gefeuerte Events auf.

Hast du das so gemeint oder lieg ich flasch?


----------



## SBS (21. Sep 2006)

Hm so ganz den Durchblick hab ich da noch nicht wie man solche Sachen aufbaut muss ich gestehen...

Ich habe meinen Fall jetzt so gelöst, dass die GUI die Listener implementiert (als innere Klassen) und sich dann davon objekte erzeugt - Listener also in Gui klasse.
Anschließend Erzeugt sich der Agent (er ist ausführende Klasse) ein Objekt der Gui. Die Gui wiederum hat ein Agenten Objekt, und an dieses liefert der Agent, wenn er die Gui erzeugt, sich selbst.

So klappt es zumindest - ob das jetzt das oben beschriebene ist bin ich mir aber nicht sicher.


----------



## Beni (21. Sep 2006)

@Gast
Dein Code passt zu dem, was ich sagte. Dein Text passt aber nicht zum Code.
Schau dir deine Klasse Agent an. Wo hat der Agent irgendeine Referenz (und sei sie noch so versteckt) der GUI? Dein Agent hat keine Möglichkeit irgendwie auf die GUI zuzugreiffen (und das ist der wichtigste Aspekt). Anders gesagt: deine Klasse "Agent" kann kompiliert werden, selbst wenn die Klasse "GUI" noch garnicht geschrieben wurde.
Deine Agents registriert eben _nichts_ bei der GUI. Vielmehr ruft die GUI passende Methoden beim Agent auf.
Konzeptionell ist das Lichtjahre von einer bidirektionalen Verbindung entfernt :wink:

@SBS
Der Code von Gast ist ein gutes Beispiel. Der grosse Unterschied zwischen deiner Variante und Gasts Code ist: dein Agent kann nicht ohne GUI kompiliert werden. Bei dir existiert noch eine bidirektionale Beziehung.
Wenn du die Beziehung Agent->GUI lösen willst: dein Agent darf kein Gui-Objekt erzeugen, mehr ist IMHO nicht mehr notwendig.

Derzeit schreibe ich meine Programme folgendermassen: ich erstelle ein Projekt und implementiere die Logik (z.B. Agenten). Dann erstell ich ein zweites Projekt und schreibe die GUI. Das zweite Projekt hat das erste im Classpath, aber nicht umgekehrt. Und schon ist alles hübsch getrennt :wink:
Und bei uns in der Firma besteht ein Programm aus etwa 10 Unterprojekten (die sich nur in eine Richtung kennen).

Achja: das sind nur Vorschläge, man muss sich nicht sklavisch dran halten. Selbstverständlich können die Agenten so implementiert sein, dass die GUI es nicht allzu schwer hat :wink:


----------



## Guest (22. Sep 2006)

Beni hat gesagt.:
			
		

> Schau dir deine Klasse Agent an. Wo hat der Agent irgendeine Referenz (und sei sie noch so versteckt) der GUI? Dein Agent hat keine Möglichkeit irgendwie auf die GUI zuzugreiffen (und das ist der wichtigste Aspekt). Anders gesagt: deine Klasse "Agent" kann kompiliert werden, selbst wenn die Klasse "GUI" noch garnicht geschrieben wurde.
> Deine Agents registriert eben _nichts_ bei der GUI. Vielmehr ruft die GUI passende Methoden beim Agent auf.
> Konzeptionell ist das Lichtjahre von einer bidirektionalen Verbindung entfernt :wink:


Hmmm ok. Würde man mein Beispiel "loosely coupled" nennen, da der Agent lediglich über eine Liste von angemeldeten Listener verfügt, welche eine gemeinsame Schnittstelle implementieren?


> Deine Agents registriert eben _nichts_ bei der GUI.


Meinst du hiermit wiederum, dass der Agent über keine GUI-Referenz verfügt, oder sich Agents nicht bei der GUI als Listener anmelden (das tun sie doch...?)?

Jedenfalls danke für dein Feedback!


----------



## Beni (22. Sep 2006)

Der Vollständigkeit halber, und weil meine vorherigen Aussagen sonst falsch sind: zur Laufzeit kann dein Agent durch die Listener und mit etwas Reflection Dinge über die GUI erfahren. Es ging mir rein um den Quellcode.



			
				Anonymous hat gesagt.:
			
		

> Hmmm ok. Würde man mein Beispiel "loosely coupled" nennen, da der Agent lediglich über eine Liste von angemeldeten Listener verfügt, welche eine gemeinsame Schnittstelle implementieren?


Joa, ich denke das passt.



> Meinst du hiermit wiederum, dass der Agent über keine GUI-Referenz verfügt, oder sich Agents nicht bei der GUI als Listener anmelden (das tun sie doch...?)?


Letzteres, dass sich die Agents nicht bei der GUI als Listener anmelden. Du schreibst "das tun sie doch", ja wo tun sie es denn? Dass die GUI ihre Aufrufe einfach an die Agents weiterleitet, ist kein Problem der Agents, denn die kümmerts nicht, was in der GUI passiert (...solange der Input stimmt :wink: )


----------



## Gast (22. Sep 2006)

Ok, das Problem liegt wohl in meinem Verständnis von "sich bei der GUI registrieren". Die Agents melden sich nicht bei der GUI an, werden jedoch von der GUI als EventListener angemeldet, in dem der GUI eine Referenz im Konstruktor übergeben wird. Stimmt das soweit?
Würdest du das selber so lösen, via Konstruktor, oder über Accessor-Methoden in der GUI, worüber sich Agenten zur Laufzeit einklinken könnten? Fragt sich dann nur, wie diese an eine Referenz der GUI kommen...


----------



## Beni (22. Sep 2006)

Man kanns das mit dem Agent=EventListener so sehen, aber ich würde das Ganze als normale Methodenaufrufe ohne weitere Bedeutung betrachten. Das Hauptmerkmal eines Listeners ist, dass er eine Implementation "versteckt" (z.B. versteckt der AgentListener die GUI). Und in diesem Fall wird aber nichts vor der GUI versteckt.

Der Konstruktor mit dem Agent-Argument ist sicherlich eine brauchbare Lösung. 
Persönlich bevorzuge ich Methoden wie "setAgent", das Programm wird dynamischer wenn die GUI auchmal einen anderen - oder garkeinen, also "null" - Agent anzeigen kann.
Das Verlinken würde ich der GUI überlassen - sie weiss am besten welches Fenster zu welchem Agent passt. 
Wird dann z.B. auf den Knopf "New Agent" gedrückt, erstellt die GUI einen neuen Agent, verlinkt ihn, und lässt den Agent losrennen (den Agents kann es egal sein, wer sie erstellt).

Alternativ könnte eine Controller-Klasse GUI _und_ Agenten erstellen, und verlinken. Das dürfte Übersichtlicher sein. Dieser Controller steht zwischen GUI und Agents. 
Wird dann auf den "New Agent"-Knopf gedrückt, wird das an den Controller weitergeleitet. Der Controller kennt schon alle Agents (er hat sie ja hergestellt) und er kennt auch die GUI. Er wird dann sicherlich ein gutes Plätzchen für den neuen Agent finden.
Der Controller kann über den AgentListener auch herausfinden wann sich ein Agent vermehrt, und könnte daraufhin ein neues Fenster öffnen.


----------



## SBS (23. Sep 2006)

Ich habe festgestellt, dass sich bei meiner Konstruktion dass Problem zeigt, dass die Gui immer auf den Agenten "wartet" und leider auch, wenn der Agent zum anderen System dispatcht, die Gui kurz verschwindet -.-
Ich hab darüber schonmal hier irgendwo gelesen gehabt, finde es doch jetzt nicht mehr wieder..

Was kann ich da nochmal machen?

Also Agent erzeugt sich ein Gui Objekt und zwar in seiner Run Methode, welche analog wie bei Threads anzusehen ist, das muss leider auch so sein, sonst ist die Gui ja nicht die ganze Zeit zu sehen...aber irgendwie ist das auch nicht so der Hit.
Weiß jetzt nich ob jemand dafür Code braucht ?

Ausschnitt:

Sync_Agent.java

```
.
	public void run()
	{			
		if(getAgletContext().getHostingURL().equals(origin))
		{
			gui = new Gui("Aglet Synchronisation System");
			gui.setAglet(this);			
		}	
	}
.
```

sowie Gui.java

```
protected void setAglet(Sync_Agent aglet)
 {
	 this.aglet = aglet;
 }
```


----------

