|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Member
Iscritto dal: Jan 2004
Messaggi: 173
|
[Java] pattern Model-View-Controller e Swing
Sto provando a mettere in pratica il pattern Model-View-Controller in un'applicazione basata su Swing. Il dilemma è questo:
Come faccio in pratica a notificare alla View qualsiasi modifica al Model? Più semplicemente: Esiste una tecnica consolidata per aggiornare tutti i componenti di una gui che visualizzano un dato, quando quest'ultimo viene aggiornato? |
|
|
|
|
|
#2 | |
|
Senior Member
Iscritto dal: Oct 2006
Messaggi: 1105
|
Quote:
|
|
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
MVC è una sigla parziale. Il pattern MVC è in verità MNVC, dove N sta per Notifiable. La N è quello che permette al modello di comunicare con le viste senza che le viste possiedano un riferimento al modello.
Riassumento in due righe pseudo-Javesche, lo schema è: Model: +addNotifiable(Notifiable n); Notifiable: +qualcosaNotify(...dati notificati) View implements Notifiable, usa Controller Controller usa Model Quando il modello subisce una mutazione ad opera del controllo altro non fa che notificare la mutazione agli oggetti registrati come candidati alle notifiche (da cui il nome Notifiable). La vista o è un Notifiable o usa un Notifiable, in ogni caso è indirettamente collegata al modello.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#4 |
|
Member
Iscritto dal: Jan 2004
Messaggi: 173
|
Molte grazie per le risposte.
Ho scartato il modello "observer" perchè impone che l'oggetto osservato estenda Observable. Tutto questo mi limiterebbe nel creare una mia gerarchia di classi. PGI-Bis è molto interessante quello che mi hai scritto. Potresti farmi un esempio pratico su come implementare il tutto? |
|
|
|
|
|
#5 | |
|
Senior Member
Iscritto dal: Oct 2006
Messaggi: 1105
|
Quote:
inoltre quanto detto da PGI-bis credo vada proprio in quella direzione: quello che lui chiama Notifiable è il mio Observer, mentre il Model è l'observable |
|
|
|
|
|
|
#6 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Interessante per modo di dire. L'MVC sa un po' di stantìo
Supponiamo che i dati contenuti nel modello siano... una stringa. Chi volesse ricevere una notifica sull'intervenuta mutazione dei dati del modello potrebbe semplicemente dire d'essere un: Codice:
public interface ModelNotifiable {
void dataChangeNotify(String value);
}
Il modello è una cosa che ha una stringa e un registro di oggetti "ModelNotifiable". Quando la stringa cambia, il modello piglia e invoca il metodo dataChangeNotify di ogni ModelNotifiable registrato. Per farlo basta una lista. Codice:
import java.util.*;
public class Model {
private LinkedList<ModelNotifiable> notifiables = new LinkedList<ModelNotifiable>();
private String data;
public void addNotifiable(ModelNotifiable n) {
notifiables.add(n);
}
public void setData(String value) {
this.data = value;
notifyDataChange();
}
private void notifyDataChange() {
for(int i = 0; i < notifiables.size(); i++) {
notifiables.get(i).dataChangeNotify(data);
}
}
}
Model model = new Model(); model.addNotifiable(la vista); e fine della storia: a questo punto ogni volta che il modello rileva una mutazione del suo dato, spara un dataChangeNotify, la vista lo riceve e aggiorna la presentazione dei dati. Nulla di trascendentale. Una vista degna di cotanto modello potrebbe essere: Codice:
import java.awt.*;
import javax.swing.*;
public class FakeView implements ModelNotifiable {
private JFrame window = new JFrame("FakeView Frame");
private JLabel dataLabel = new JLabel("", JLabel.CENTER);
public FakeView() {
window.add(new JLabel("Valore", JLabel.CENTER), BorderLayout.NORTH);
window.add(dataLabel, BorderLayout.CENTER);
window.setPreferredSize(new Dimension(300, 200));
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void show() {
EventQueue.invokeLater(new Runnable() { public void run() {
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
}});
}
public void dataChangeNotify(final String value) {
EventQueue.invokeLater(new Runnable() { public void run() {
dataLabel.setText(value);
}});
}
}
Manca il controller. Originariamente (MVC nasce nel campo delle interfacce grafiche con il proposito di separare la presentazione dei dati dal dispositivo di input usato dall'utente) il controllo era un ponte tra l'utente ed il modello. Ad esempio: Codice:
public class ConsoleController {
private Model model;
public ConsoleController(Model model) {
this.model = model;
}
public void start() {
java.io.Console console = System.console();
console.printf("Benvenuto nel 1967!%n");
console.printf("Inserisci una stringa e premi invio%n");
console.printf("La stringa quit chiude il programma%n");
String line;
while(!(line = console.readLine()).equals("quit")) {
model.setData(line);
}
console.printf("Fine dell'interazione%n");
}
}
Bisogna stare attenti perchè le due interpretazioni sembrano simili ma non lo sono. Un esempio di controller per Modello, che usa la console come periferica di input (ai tempi andati il controller avrebbe interagito direttamente con la tastiera), è: Codice:
public class ConsoleController {
public void link(Model model) {
java.io.Console console = System.console();
console.printf("Benvenuto nel 1967!%n");
console.printf("Inserisci una stringa e premi invio%n");
console.printf("La stringa quit chiude il programma%n");
String line;
while(!(line = console.readLine()).equals("quit")) {
model.setData(line);
}
console.printf("Fine dell'interazione%n");
}
}
Codice:
public class Main {
public static void main(String[] args) {
Model model = new Model();
FakeView view = new FakeView();
model.addNotifiable(view);
view.show();
ConsoleController controller = new ConsoleController();
controller.link(model);
}
}
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#7 |
|
Member
Iscritto dal: Jan 2004
Messaggi: 173
|
PGI-Bis sei stato chiarissimo e gentilissimo.
Grazie di cuore… |
|
|
|
|
|
#8 |
|
Member
Iscritto dal: Jan 2004
Messaggi: 173
|
Sto provando a mettere in pratica il tutto, per realizzare un programma che accede ad un database, visualizza i dati in diverse Jtable, in modo che possano essere modificati.
Per realizzare tutto questo ho creato tre package: Model, View e Controller.
Dove vanno inseriti i renderers delle celle? Chiedo questo perché sono componenti grafici, ma comunque specifici di un modello. Di conseguenza mi riesce difficile immaginare un modo per separarli completamente. Dove vanno inseriti gli editor di cella? Al loro interno incapsulano un Component, ma al tempo stesso devono funzionare da controller, perché non tutti i dati inseriti dall'utente possono essere accettati. Dove bisogna controllare l'input dell'utente? Nel modello o nel controller? Come gestire le modifiche dei dati? In alcune circostanze, potrebbe essere necessario che per accettare una modifica di un determinato dato, ci debba essere una determinata condizione. Dove gestire il tutto: nel modello o nel controller? Sto pensando a tutto questo perché immagino a come possa essere convertito un programma standalone in “altro”. Questo comporterebbe il rifacimento di tutto il controller e di tutte le viste. Giusto? |
|
|
|
|
|
#9 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
L'input utente è gestito dal controller. Intendo per input l'interazione dell'utente e non la validità delle informazioni immesse dall'utente.
La verifica dell'input è gestita dal modello o dal controllo. Dal modello se la validità dell'immissione dipende da un valore noto al modello. Altrimenti deve essere gestita dal controllo. Tale verifica assume le vesti di una convalida a posteriori solo nel caso di gestione da parte del modello. Nel caso in cui essa sia gestita dal controllo (in quanto non dipendente da uno stato del modello), la verifica è una semplice formattazione dell'input. Due esempi astratti. 1. Verifica da parte del modello. Il modello contiene delle chiavi a cui possono essere associati dei valori. Un valore può essere inserito solo se esiste una chiave a cui associare quel valore. L'utente inserisce coppie chiave (esistente) valore (nuovo). Chi controlla che la chiave esista prima di inserire il valore? Il modello. Lo fa il modello perchè la validità dell'input dipende da uno stato del modello (la presenza di una certa chiave). 2. Verifica da parte del controllo. Il modello contiene numeri interi maggiori di 35. L'utente inserisce 30. Chi controlla che il numero sia maggiore di 35? Il controllo, perchè la validità dell'input non dipende da uno stato del modello. In questo caso il controllo opera come semplice "formattatore" dell'input. E' come se il modello richiedesse una sequenza di caratteri ed il controllo dovesse produrla a partire da una tastiera virtuale gestita col mouse. Il controllo si occupa di ricondurre una varietà di input ad un insieme predefinito di valori ciò che può essere fatto dal controllo in autonomia se e solo se i "limiti" di quell'insieme siano noti a priori. E' chiaro che questo può accadere solo se quei limiti non siano variabili. Nel caso in cui siano variabili, quella variabilità, in quanto informazione, sarà gestita dal modello.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#10 | |
|
Senior Member
Iscritto dal: Oct 2002
Città: Como
Messaggi: 708
|
Quote:
__________________
Cristian ,il nOUS che invoglia i5-2500 (Sandy Bridge) Quad-Core 3,30 Ghz - Asus P8Z68-V PRO / GEN3 - 2x4GB DDR3 - GeForce GTX 550 Ti Pci-E 4 Gb ddr3 - Barracuda 7200.12 1 Tb Sata 600 Buffer 32 Mb 7200 Rpm |
|
|
|
|
|
|
#11 |
|
Member
Iscritto dal: Jan 2007
Messaggi: 189
|
Ti propongo un esmpio semplice di un pattern molto simile al MVC, il pattern Model-View-Presenter(MVP).
In questo pattern Model e View sono gli stessi del MVC e il Controller è chiamto Presenter e si occupa anche di aggirnare la view. Ci sono 2 varianti quella che ti presento è la Passive View. Supponiamo di avere un form con 1 campi di testo dove puoi inserire un nome e cliccado su un pulsante esegui una ricerca in una rubrica telefonica, visualizzando il numero di telefono in una label. Non uso java da troppo tempo per cui l'esempio sarà in pseudocodice simile a java. Il Model è molto semplice Codice:
public class Model{
private String name;
private String number;
public void SetName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
// set e get per number
}
Codice:
public interface ISearchView {
public void setNumber(String number);
public void displayError(String error);
}
public class SearchView implements ISearchView {
private SearchPresenter presenter;
private JTextField = new JTextField();
private JLabel numberLabel = new Jlabel();
private JLabel errorLabel = new Jlabel();
private JButton searchButton = new Jbutton();
public SearchView(){
// ogni view crea un presenter e si inietta nello stesso
presenter = = new SearchPresenter(this);
}
public void setNumber(String number){
numberLabel.setText(number);
}
public void displayError(String error){
errorLabel.setText(error);
}
public void buttonClick(){
// al click del bottone la view richiama il metodo search del presenter
// che si occupa di validare l'input e effettuare le operazioni richieste
presenter.search(nameTextField.getText());
}
}
Il presenter non conosce direttamente la view (intesa come implementazione specifica di una view) ma conosce l'interfaccia che essa implementa. Per cui può agire sulla stessa richiedendo delle oprazioni, il modo in cui le operazioni sarranno implementate dipende esclusivamente dalla view. Se devo mostrare un messaggio di errore il presenter esegue la richiesta, ma sarà la view a decidere come visualizzare l'errore (per esempio può visualizzare un testo in una label, oppure inserire un * vicino al campo non validato). Codice:
public class SearchPresenter{
private ISearchView view;
// supponiamo che ModelDao sia una classe che che effettua operazioni su database
ModelDao dao;
Model model;
public SearchPresenter(ISearchView view){
this.view = view;
}
public void search(String name){
// questo metodo vaiilda l'input
// esegue le operazioni di ricerca e aggiorna la view
// altrimenti notifica alla view di visualizzare un errore
if (controllo se name è valido){
model = dao.search(name);
view.setNumber(model.getNumber());
} else
view.displayError("Inserisci un nome");
}
}
Codice:
public class Main {
public static void main(String[] args) {
SearchView view = new SearchView();
view.Show();
}
}
|
|
|
|
|
|
#12 |
|
Member
Iscritto dal: Jan 2004
Messaggi: 173
|
Grazie per i vostri suggerimenti.
Ho tuttavia i seguenti dubbi:
|
|
|
|
|
|
#13 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Le meccaniche della gui si gestiscono nel modello.
La vista fa vedere e stop. Il pulsante è attivo solo se un elemento della lista è selezionato: significa che nel modello c'è un'informazione "elementoSelezionato" e un'informazione "stato del pulsante", che la mutazione del valore "elementoSelezionato" causa una mutazione del valore "stato del pulsante". Queste mutazioni sono comunicate alla vista tramite Notifiable e la vista aggiorna il suo aspetto in conseguenza della notifica ricevuta.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#14 | |
|
Member
Iscritto dal: Jan 2007
Messaggi: 189
|
Nel Passive view tutte le dinamiche della view sono gestite dal presenter (la vista è passiva, come dal nome)
all'evento della selezione della lista richiami il presenter notificandogli l'evento e di conseguenza il presenter richiederà l'aggiornamento della view attivando il bottone. Quote:
|
|
|
|
|
|
|
#15 |
|
Member
Iscritto dal: Jan 2004
Messaggi: 173
|
Scusatemi per il ritardo con il quale sto rispondendo.
Ho cercato di mettere in pratica il tutto, creando una semplice rubrica con il pattern MVP. Il programma non è di alcuna utilità, mi è servito solo per esercitarmi. Tecnicamente consente di aggiungere o rimuovere un contatto ad una collection, attraverso una JTable. Mi piacerebbe molto avere qualsiasi vostro commento, perchè non sono un professionista o uno studente, ma un semplice hobbysta. Volevo chiedere: Nel caso in cui un presenter dovesse gestire più di una finestra, non sarebbe più possibile crearlo nel costruttore della finestra. Come fare? |
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 20:20.












,il nOUS che invoglia








