PDA

View Full Version : [JAVA] architettura multistrato e ActionListener


foxmolder5
23-09-2006, 18:53
devo realizzare un programma in Java avente architettura multistrato ( tipo MVC ma con diverse semplificazioni) e sto riscontrando qualche grattacapo con la suddivisione delle competenze tra i vari strati. ho un strato "applicazione" in cui saranno presenti tutte le classi di "controllo" e poi uno strato superiore che costituisce l'interfaccia del software.
ho realizzato tale interfaccia, però non so come gestire l'ActionListener associato alla finestra che ho creato.
infatti quando viene attivato l'actionListener, viene solitamente eseguito il codice al suo interno, però tale codice, contenendo tutta la logica di gestione delle azioni, nel mio caso dovrà trovarsi all'interno della classe di controllo e non in quella di interfaccia.
una possibile soluzione sarebbe quella di inserire l'actionListener nella classe dell'interfaccia e, al momento della decisione dell'azione da fare, far ritornare il controllo alla classe di controllo (che ha chiamato quella di interfaccia) passando una struttura con tutti i dati al suo interno.
questa soluzione mi sembra 1 pò sporca e quindi vorrei sapere se esiste qualcosa di + pulito o se mi consigliate altro visto che sono abbastanza inesperto in questo campo. grazie

PGI-Bis
24-09-2006, 13:21
La soluzione è unica, se parliamo di architettura Modello-Vista-Controllo. Dati gli elementi:

Modello modello = new Modello(); //Usa Notificabile
Vista vista = new Vista(); //concretizza Notificabile
Controllo controllo = new Controllo();

l'applicazione deve funzionare con queste quattro (sole) relazioni:

modello.setNotificabile(vista);
vista.setControllo(controllo);
controllo.setModello(modello);

vale a dire Vista è un Notificabile, Vista usa Controllo, Modello usa Notificabile, Controllo usa Modello.

Nel caso specifico, l'ActionListener di cui parli è un pezzo di Controllo ed è connesso al pulsante posseduto dall'interfaccia nel momento in cui il Controllo è collegato alla Vista.

Le operazioni di gestione dei dati che comportino un riflesso sull'interfaccia (se premo X il pulsante Y si abilita) sono condotte dal controllo sul modello e riflesse sulla vista in quanto soggetto di notifiche da parte del modello sulle transizioni di stato.

Faccio un esempio del percorso logico che nell'architettura MVC porta da un evento di interazione subito da un elemento dell'interfaccia utente ad un riflesso su un altro elemento dell'interfaccia utente:

Dalla vista, premendo pulsanteX...
//Vista.java
public void setControllo(Controllo c) {
pulsanteX.addActionListener(c.getAscoltatorePerAzioneX());
pulsanteY.addActionListener(c.getAscoltatorePerAzioneY());
//eccetera...
}

si passa al controllo che gestisce la situazione...

//Controllo
private void riflessoCollegatoAdAzioneX() {
modello.setStatoY(false);
}

private ActionListener xListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
riflessoCollegatoAdAzioneX();
}
};

public ActionListener getAscoltatorePerAzioneX() {
return xListener;
}

provocando una mutazione del modello, comunicata a tutti i notificabili...

//Modello.java
public void setStatoY(boolean valore) {
valoreY = valore;
for(Notificabile n : elencoNotificabili) {
notificabile.gesticiNotifica(Notificabile.Y, true);
}
}

e quindi alla vista stessa.

//Vista.java
public void gestisciNotifica(int state, Object value) {
if(state == Y_CHANGED) {
buttonY.setEnabled((Boolean)value);
}
}

In questo pseudo-codice ho considerato un ipotetico JButton come vista pura ma in realtà l'uso supposto sfrutta anche il modello di JButton (ButtonModel). Nel caso in cui la tua interfaccia deva interpretare in senso radicale la MVC allora il tuo Modello dovrà includere anche il (o i) ButtonModel e tale ButtonModel dovrà essere associato al JButton presentato nella vista all'atto della registrazione di quest'ultima come Notificabile per il modello.

redcloud
24-09-2006, 14:54
Ottimo esempio ma non mi è chiara solo una cosa. Il modello dove prende la lista dei notificabili?

PGI-Bis
24-09-2006, 15:52
Sopra ho scritto "setNotificabile" ma semanticamente è più corretto rimpiazzarlo con "addNotificabile" perchè possono esserci più Viste che presentino i dati di un unico modello.

Più che un modello che "prende la lista di" parlerei di modello a cui "è rifilata una lista di" notificabili. In pseudo (molto pseudo) JavUML:

interface Notifiable {}

class Model {
-Lista di Notificabili
+aggiungiNotificabile(Notificabile n)
+rimuoviNotificabile(Notifiacabile n)
}

class Vista implements Notifiable {}

//collega vista e modello
Modello m = new Modello();
Vista v = new Vista();
m.aggiungiNotificabile(v);//<- qui la vista inizia a presentare i dati

A onor del vero dovrei dire che il modello MVC non corrisponde esattamente a quanto da me riportato per la parte relativa al controllo. La versione originale prevede sì modello, notificabile e vista come riportati ma tratta in modo diverso il controllo che ha una doppia dipendenza:

controllo usa vista
controllo usa modello

anzichè la dipendenza singola (e contraria):

vista usa controllo

Nel senso originario, infatti, il controllo è la rappresentazione del dispositivo di input offerto all'utente per l'interazione con l'interfaccia grafica e deve avere a disposizione la mappa dei componenti presentati dalla vista per poter discriminare tra le diverse azioni possibili.

Pare una bizzarria ma è al modello originale che dobbiamo rifarci nel caso in cui, ad esempio, si voglia dotare una certa applicazione della possibilità di funzionare, alternativamente, con un controller a pulsanti piuttosto che con un mouse o un touch-screen.

Tuttavia, in ossequio all'architettura SMA, ogni componente Swing integra una meccanica predefinita di controllo. A quel punto la relazione Controllo->Vista non è più necessaria mentre è opportuno conservare l'indipendenza tra Vista e Modello delegando al Controllo la parte logica dell'interazione utente. Il che si traduce in una freccetta UML che fa una giravolta =).

redcloud
24-09-2006, 16:15
Quindi in pratica torniamo al punto di partenza? L'MVC descritto da te non è quello puro, è la variante con call back. Nell'MVC puro è il controllo che si occupa di lavorare sul modello e di inoltrarli alla vista in quanto il modello non è intelligente (tanto da notificare la vista o per esempio lavorare sulla persistenza dei dati). Visto quindi che il modello è un bean, è il controllo che gestisce le comunicazioni. E' questo che hai detto nell'ultimo post?

PGI-Bis
24-09-2006, 18:36
No. Il modello che definisco "originale" è quello che si chiama MVC ed è facilmente reperibile in qualsiasi manuale sulla piattaforma Smalltalk: il controllo usa la vista per ottenere le informazioni sulla distribuzione dei componenti. La versione che ho proposto è fondata sulla preesistenza del controllo nei componenti Swing che elimina la necessità di indagine del controllo sulla vista.

foxmolder5
24-09-2006, 21:07
mi complimento e ti ringrazio per la spiegazione, ma la struttura che devo implementare è leggermente diversa.
io ho semplicemente una classe interfaccia, una classe controllo e poi nello strato inferiore vi è il "middleware" ( mysql con relativo strato di gestione del db).
ora il mio prob è:
se ho la classe interfaccia, dove dovrò inserire l'actionListener?
se lo inserisco nella classe interfaccia come faccio a far "ritornare" il controllo alla funzione controllo per l'esecuzione delle varie operazioni?
in quanto per ora la mia applicazione avrà l'istanza della classe di controllo che crea un'istanza della classe di interfaccia, viene eseguita l'interfaccia, però poi nn so come far tornare il comando alla classe di controllo.

redcloud
25-09-2006, 08:05
Swing ci facilita le cose, puoi semplicemente istanziare (all'interno della vista) le classi anonime actionListner per ciascun elemento grafico che attiva il relativo controllo. I controlli possono essere anche metodi statici (dipende comunque da cosa devono fare).

PGI-Bis
25-09-2006, 12:16
Be', visto che non parliamo più di MVC, l'ActionListener lo puoi mettere sia nel Controllo che nell'Interfaccia. In entrambi i casi o l'interfaccia possiede un riferimento al controllo o il controllo possiede un riferimento all'interfaccia. La cosa è necessaria per ogni caso di comunicazione primitiva tra due oggetti. Vediamo cosa cambia. Quindi avrai o una cosa tipo:

public class InterfacciaUtente {
private Controllo controllo;
private JButton ilPulsante = new JButton(new AbstractAction("Salva") {
public void actionPerformed(ActionEvent e) {
if(controllo != null) {
controllo.salva();
}
}
});

public void setControllo(Controllo c) {
controllo = c;
}
}

public class Controllo {
public void salva() {/*...*/}
}

o, per il caso Controllo->InterfacciaUtente

public class Controllo {
private InterfacciaUtente interfaccia;

private ActionListener salvaListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
salva();
}
};

public void setInterfacciaUtente(InterfacciaUtente u) {
interfaccia = u;
AbstractButton salva = grabButton("salva");
salva.removeActionListener(salvaListener);
salva.addActionListener(salvaListener);
}

private AbstractButton grabButton(String key) {
AbstractButton b= interfaccia.getButton(key);
if(b== null) {
throw new RuntimeException("Unexpected null value for key "+key);
} else {
return b;
}
}

private void salva() {/*...*/}
}

public class InterfacciaUtente {
private Map<String, AbstractButton> buttonMap =
new HashMap<String, AbstractButton>();

private void init() {
JButton salva = new JButton("Salva");
buttonMap.put("salva", salva);
}

public AbstractButton getButton(String key) {
return buttonMap.get(key);
}
}

Nel primo caso InterfacciaUtente contiene i componenti UI e genera un rinvio, per quelli interagibili, agli opportuni comportamenti di Controllo. Controllo definisce le reazioni astrattamente associabili a dei componenti UI (e concretamente associate da InterfacciaUtente).

Nel secondo caso InterfacciaUtente contiene i componenti UI mentre Controllo definisce le reazioni e le associa all'atto della connessione con un InterfacciaUtente.

Tenendo conto che non siamo più nel campo MVC, alcune questioni pratiche mi farebbero preferire la seconda soluzione alla prima, in particolare quelle riguardanti l'introduzione di processi concorrenti che comportino un qualche effetto temporaneo sull'interfaccia utente. Non è detto tuttavia che tu abbia queste esigenze.