MVC and application state

  • Follow


Hello,I just read http://java.sun.com/products/jfc/tsc/articles/architecture/ which was recommended in another thread and a question remains as how to separate the GUI and data.Suppose you have a boolean value in your application (for example, "Check for new messages at startup", in an email app), you will need this value for the app to work correctly. This value may be set by a checkbox in some Preferences window, but is usually hidden from the user.Would you:- Initialize the GUI without showing it, and get the state by accessing the GUI component's value (eg. checkbox.isSelected())- Use directly, or subclass, ButtonModel and use this ButtonModel to hold this data everywhere in your code, passing it to the JCheckbox upon construction of the GUI- Implement your own update mechanism: i.e. storing the data in a simple boolean somewhere and when the GUI is shown, create a checkbox with the correct selection state. Update the boolean value only on close of the Preferences window.- Any other sensible way I did not think of?What's your day-to-day way of doing this for GUI apps?Thanks for your answersPhil
0
Reply sicsicsic (164) 2/13/2008 9:59:34 AM

Philipp wrote:
> Hello,
> I just read http://java.sun.com/products/jfc/tsc/articles/architecture/ 
> which was recommended in another thread and a question remains as how to 
> separate the GUI and data.
> 
> Suppose you have a boolean value in your application (for example, 
> "Check for new messages at startup", in an email app), you will need 
> this value for the app to work correctly. This value may be set by a 
> checkbox in some Preferences window, but is usually hidden from the user.
> 
> Would you:
> - Initialize the GUI without showing it, and get the state by accessing 
> the GUI component's value (eg. checkbox.isSelected())
> - Use directly, or subclass, ButtonModel and use this ButtonModel to 
> hold this data everywhere in your code, passing it to the JCheckbox upon 
> construction of the GUI
> - Implement your own update mechanism: i.e. storing the data in a simple 
> boolean somewhere and when the GUI is shown, create a checkbox with the 
> correct selection state. Update the boolean value only on close of the 
> Preferences window.
> - Any other sensible way I did not think of?

     I'd keep the "authoritative" boolean somewhere in the
main part of the app, possibly using a Preferences to preserve
the value from run to run.  If the user opens the part of the
GUI that controls this boolean, I'd initialize a checkbox from
the current authoritative value and update that value if the
user changes the checkbox.

     I'd try to avoid having the app query the checkbox state
directly, because this would tie the app to the specifics of
the GUI design.  Rather, I'd use the GUI as a "knob" to show
and control the real value.  This gives me the flexibility to
change the GUI, to implement a non-GUI command-line interface
for use in batch scripts, and so on.

     YMMV.

-- 
Eric Sosman
esosman@ieee-dot-org.invalid
0
Reply esosman2 (2945) 2/13/2008 1:57:56 PM


Philipp <sicsicsic@freesurf.ch> writes:>a question remains as how to separate the GUI and data.  (If replying to this post, please do not quote all of it, but  only small parts you directly refer to [where �you� does not  refer to Philipp, but to anyone replying to this post].)  Usually, the GUI is observing the model via the observer pattern.>checkbox in some Preferences window, but is usually hidden from the user.>- Initialize the GUI without showing it, and get the state by accessing >the GUI component's value (eg. checkbox.isSelected())  Usually, it would not be part of the GUI, but of the  application model. Especially, when it usually is �hidden  from the user�.  Swing does not use the original MVC. For example, it uses  UI-delegates, not controllers and views.  Here is an example code by me to show how to use the original  MVC with Swing in Java as truly as possible. But since this  was my main intention, the code has become larger than  necessary in Java alone, without this intention.  As in your question, it keeps a single-bit state.  The class �Button1Model� is the application model in this  case. Some comments refer to the name of Smalltalk messsages  used in the original MVC, which originated in Smalltalk,  though the correspondence is not always perfect, because  Java+Swing is not Smalltalk. For an explanation, see [Burbeck 87]:http://st-www.cs.uiuc.edu/users/smarch/st-docs/mvc.html  From reading the code below, you can learn how �Button1Model�  holds a list of its observers and how �Button1View� registers  itself with this model and then observes it.  I am using this program in my class at a point, where control  statements like �if� or �while� have not been introduced yet -  so all is done without a single �if� or �while�.ButtonModel.javapublic class ButtonModel{ public static void main( final java.lang.String[] args )   { javax.swing.SwingUtilities.invokeLater( new FrameController() ); }}class FrameView{ FrameController controller = null;  Button1View button = null;  javax.swing.JFrame frame = null; /* MVC "subView" */  public FrameView()   { frame = new javax.swing.JFrame( "MVC" );    frame.setDefaultCloseOperation    ( javax.swing.JFrame.DO_NOTHING_ON_CLOSE );    final Button1Model model = new Button1Model();    final Button1Controller controller = new Button1Controller();    controller.setModel( model );    button = new Button1View( frame, model, controller );    frame.pack(); frame.setVisible( true ); }  public void setController // MVC "model:controller:"   ( final FrameController controller )  { this.controller = controller;    this.controller.setView( this );    frame.addWindowListener( this.controller ); }  public void releaseController() /* MVC "release" */  { frame.removeWindowListener( this.controller );    this.controller.releaseView();     this.controller = null; }  public void dispose() /* MVC "release" */  { this.button.dispose(); this.button = null;     this.frame.dispose(); this.frame = null; }}class FrameController // MVC "Top-Level Controller implements java.lang.Runnable, // MVC "StandardSystemController" java.awt.event.WindowListener{ FrameView view = null;  public void setView( final FrameView view ) /* MVC "view" */  { this.view = view; }  public void releaseView()  { this.view = null; }  public void run() // MVC "open" (cto)   { FrameView view = new FrameView();    view.setController( this ); }  public void windowClosing /* MVC "terminate" */  ( final java.awt.event.WindowEvent e )   { FrameView view = this.view;    view.releaseController();     view.dispose(); }    public void windowOpened( final java.awt.event.WindowEvent e ){}  public void windowDeactivated( final java.awt.event.WindowEvent e ){}   public void windowDeiconified( final java.awt.event.WindowEvent e ){}  public void windowIconified( final java.awt.event.WindowEvent e ){}  public void windowActivated( final java.awt.event.WindowEvent e ){}  public void windowClosed( final java.awt.event.WindowEvent e){} }class Button1View /* MVC "view" */implements java.beans.PropertyChangeListener{ javax.swing.JButton button = null;  javax.swing.JFrame frame = null;  Button1Model button1Model = null;  private java.lang.String labelText( final boolean state )  { return state ? "turn off" : "turn on"; }  public Button1View /* MVC "model:controller" (va3) */  ( final javax.swing.JFrame frame,     final Button1Model button1Model,    final java.awt.event.ActionListener controller )  { assert javax.swing.SwingUtilities.isEventDispatchThread();    this.button1Model = button1Model;    this.button = new javax.swing.JButton( labelText( this.button1Model.isOn() ));    this.button.addActionListener( controller );    this.button1Model.addButton1Listener( this );    this.frame = frame;    this.frame.add( this.button ); }  public void propertyChange  ( java.beans.PropertyChangeEvent propertyChangeEvent )  { java.lang.System.out.println( "changed" );     this.button.setText( labelText( this.button1Model.isOn() )); }  public void dispose()  { this.button1Model.removeButton1Listener( this );    this.frame.remove( this.button );    this.button = null; }}class Button1Controllerimplements java.awt.event.ActionListener{ Button1Model button1Model = null;  public void setModel( final Button1Model button1Model )  { this.button1Model = button1Model; }  public void actionPerformed /* MVC event (cpe) */  ( final java.awt.event.ActionEvent actionEvent )  { button1Model.toggle(); }}class Button1Model{ private boolean state = false;  javax.swing.event.SwingPropertyChangeSupport listeners;  public Button1Model()   { listeners =     new javax.swing.event.SwingPropertyChangeSupport( this ); }  public boolean isOn(){ return state; }  public void toggle(){ state = !state; changedState(); }     private void changedState()  { this.listeners.firePropertyChange    ( new java.beans.PropertyChangeEvent      ( this, "on", state, !state )); }  public void addButton1Listener  ( final java.beans.PropertyChangeListener l )   { this.listeners.addPropertyChangeListener( l ); }  public void removeButton1Listener  ( final java.beans.PropertyChangeListener l )  { this.listeners.removePropertyChangeListener( l ); }}
0
Reply ram 2/13/2008 3:32:24 PM

Eric Sosman wrote:
> Philipp wrote:
>> Hello,
>> I just read 
>> http://java.sun.com/products/jfc/tsc/articles/architecture/ which was 
>> recommended in another thread and a question remains as how to 
>> separate the GUI and data.
>>
>> Suppose you have a boolean value in your application (for example, 
>> "Check for new messages at startup", in an email app), you will need 
>> this value for the app to work correctly. This value may be set by a 
>> checkbox in some Preferences window, but is usually hidden from the user.
>>
>> Would you:
>> - Initialize the GUI without showing it, and get the state by 
>> accessing the GUI component's value (eg. checkbox.isSelected())
>> - Use directly, or subclass, ButtonModel and use this ButtonModel to 
>> hold this data everywhere in your code, passing it to the JCheckbox 
>> upon construction of the GUI
>> - Implement your own update mechanism: i.e. storing the data in a 
>> simple boolean somewhere and when the GUI is shown, create a checkbox 
>> with the correct selection state. Update the boolean value only on 
>> close of the Preferences window.
>> - Any other sensible way I did not think of?
> 
>     I'd keep the "authoritative" boolean somewhere in the
> main part of the app, possibly using a Preferences to preserve
> the value from run to run.  If the user opens the part of the
> GUI that controls this boolean, I'd initialize a checkbox from
> the current authoritative value and update that value if the
> user changes the checkbox.
> 
>     I'd try to avoid having the app query the checkbox state
> directly, because this would tie the app to the specifics of
> the GUI design.  Rather, I'd use the GUI as a "knob" to show
> and control the real value.  This gives me the flexibility to
> change the GUI, to implement a non-GUI command-line interface
> for use in batch scripts, and so on.

Thanks for your answers. It makes sense to do it that way.

On the other hand, if for example the value is changed from somewhere 
else in the code while the GUI is showing, the GUI will not be updated. 
So you would have to implement some sort of ChangeListener equivalent, 
is this correct?

Phil
0
Reply sicsicsic (164) 2/13/2008 3:45:04 PM

On Feb 13, 1:57 pm, Eric Sosman <esos...@ieee-dot-org.invalid> wrote:
> Philipp wrote:
> > Hello,
> > I just readhttp://java.sun.com/products/jfc/tsc/articles/architecture/
> > which was recommended in another thread and a question remains as how to
> > separate the GUI and data.
>
> > Suppose you have a boolean value in your application (for example,
> > "Check for new messages at startup", in an email app), you will need
> > this value for the app to work correctly. This value may be set by a
> > checkbox in some Preferences window, but is usually hidden from the user.
>
> > Would you:
> > - Initialize the GUI without showing it, and get the state by accessing
> > the GUI component's value (eg. checkbox.isSelected())
> > - Use directly, or subclass, ButtonModel and use this ButtonModel to
> > hold this data everywhere in your code, passing it to the JCheckbox upon
> > construction of the GUI
> > - Implement your own update mechanism: i.e. storing the data in a simple
> > boolean somewhere and when the GUI is shown, create a checkbox with the
> > correct selection state. Update the boolean value only on close of the
> > Preferences window.
> > - Any other sensible way I did not think of?
>
>      I'd keep the "authoritative" boolean somewhere in the
> main part of the app, possibly using a Preferences to preserve
> the value from run to run.  If the user opens the part of the
> GUI that controls this boolean, I'd initialize a checkbox from
> the current authoritative value and update that value if the
> user changes the checkbox.
>
>      I'd try to avoid having the app query the checkbox state
> directly, because this would tie the app to the specifics of
> the GUI design.  Rather, I'd use the GUI as a "knob" to show
> and control the real value.  This gives me the flexibility to
> change the GUI, to implement a non-GUI command-line interface
> for use in batch scripts, and so on.
>
>      YMMV.
>
> --
> Eric Sosman
> esos...@ieee-dot-org.invalid

I think Eric said it all, usually that kind of setting
lends itself to a Preferences persistence mechanism.
So you'd have a view object(GUI) to update those settings
and persist it through Preferences(whether it be db or fs),
provided you load it into memory whenever it is needed.

 jtv
big hug
0
Reply jtviegas (1) 2/13/2008 3:58:08 PM

Philipp wrote:
> Eric Sosman wrote:
>> Philipp wrote:
>>> Hello,
>>> I just read 
>>> http://java.sun.com/products/jfc/tsc/articles/architecture/ which was 
>>> recommended in another thread and a question remains as how to 
>>> separate the GUI and data.
>>>
>>> Suppose you have a boolean value in your application (for example, 
>>> "Check for new messages at startup", in an email app), you will need 
>>> this value for the app to work correctly. This value may be set by a 
>>> checkbox in some Preferences window, but is usually hidden from the 
>>> user.
>>>
>>> Would you:
>>> - Initialize the GUI without showing it, and get the state by 
>>> accessing the GUI component's value (eg. checkbox.isSelected())
>>> - Use directly, or subclass, ButtonModel and use this ButtonModel to 
>>> hold this data everywhere in your code, passing it to the JCheckbox 
>>> upon construction of the GUI
>>> - Implement your own update mechanism: i.e. storing the data in a 
>>> simple boolean somewhere and when the GUI is shown, create a checkbox 
>>> with the correct selection state. Update the boolean value only on 
>>> close of the Preferences window.
>>> - Any other sensible way I did not think of?
>>
>>     I'd keep the "authoritative" boolean somewhere in the
>> main part of the app, possibly using a Preferences to preserve
>> the value from run to run.  If the user opens the part of the
>> GUI that controls this boolean, I'd initialize a checkbox from
>> the current authoritative value and update that value if the
>> user changes the checkbox.
>>
>>     I'd try to avoid having the app query the checkbox state
>> directly, because this would tie the app to the specifics of
>> the GUI design.  Rather, I'd use the GUI as a "knob" to show
>> and control the real value.  This gives me the flexibility to
>> change the GUI, to implement a non-GUI command-line interface
>> for use in batch scripts, and so on.
> 
> Thanks for your answers. It makes sense to do it that way.
> 
> On the other hand, if for example the value is changed from somewhere 
> else in the code while the GUI is showing, the GUI will not be updated. 
> So you would have to implement some sort of ChangeListener equivalent, 
> is this correct?

     You roll your own, or use ChangeListener and ChangeEvent.
The method that actually sets the boolean calls all the
registered listeners to notify them of the change.

     There's also an Observable class and an Observer interface
(the terminology sounds odd) that appear to do much the same
sort of thing.  You'd put the boolean in a class that extends
Observable, and your GUI component would implement Observer.
I've never used this pair myself, but they look quite similar
to the listener mechanism.

-- 
Eric.Sosman@sun.com

0
Reply Eric.Sosman (4228) 2/13/2008 4:30:55 PM

Philipp wrote:> On the other hand, if for example the value is changed from somewhere > else in the code while the GUI is showing, the GUI will not be updated. > So you would have to implement some sort of ChangeListener equivalent, > is this correct?While Eric gave great answers, I wanted point out that a ChangeListener would only be appropriate if the value of the checkBox (or any other intern GUI state) if the state can change while the GUI is displaying on screen.I would construct the GUI element (JFrame, Dialog, etc.) from the application state (the boolean or whatever) only when I was about to use that particular GUI element.  Then I'd display it, and retrieve state from the GUI object when the GUI is done.  I'd probably call dispose() on the element too, just to release resources, and dispose the object.If you GUI element is persistent (i.e., it's the main application top-window) and always displayed then yes, you'll need a ChangeListener (or whatever interface you decide is best) to update that state so the user can see it. But normally I associate checkBoxes with Dialog Boxes and simple user interactions that don't need to watch for an external state.
0
Reply Mark 2/13/2008 5:36:47 PM

Mark Space wrote:> Philipp wrote:> >> On the other hand, if for example the value is changed from somewhere >> else in the code while the GUI is showing, the GUI will not be >> updated. So you would have to implement some sort of ChangeListener >> equivalent, is this correct?> > > While Eric gave great answers, I wanted point out that a ChangeListener > would only be appropriate if the value of the checkBox (or any other > intern GUI state) if the state can change while the GUI is displaying on > screen.> > I would construct the GUI element (JFrame, Dialog, etc.) from the > application state (the boolean or whatever) only when I was about to use > that particular GUI element.  Then I'd display it, and retrieve state > from the GUI object when the GUI is done.  I'd probably call dispose() > on the element too, just to release resources, and dispose the object.> > If you GUI element is persistent (i.e., it's the main application > top-window) and always displayed then yes, you'll need a ChangeListener > (or whatever interface you decide is best) to update that state so the > user can see it. But normally I associate checkBoxes with Dialog Boxes > and simple user interactions that don't need to watch for an external > state.     Mark is right, of course: It's folly to commit canaricideby cannon.  And yet, the discipline of a stern separation betweenmodel and view can yield unexpected advantages ...     When I was teaching myself Swing, I wrote an application GUIthat took a bunch of inputs, applied some application-specificcalculations, and displayed a bunch of outputs.  Pretty simplestuff: no need for all this arms'-length separation of model andview.  But as my knowledge grew I said "Oh, Eric!  You have doneWrong!  Tut-tut!"  And as a sort of exercise in Rightness I wentback and rewrote the thing so all the domain-specific logic livedin a model and all the GUI stuff lived in a view.  A perfectlypointless exercise in political purity, no?     No!  Because an interesting thing happened: I decided to addthe ability to save the application data to a file and restoreit later, in another invocation -- and The Save And Restore CodeWas Just Another View!  The code that managed the I/O was ableto know Nothing about the GUI, and dealt only with the model.The GUI's code knew a very little bit about the save/restore code,only enough to invoke it when tickled from a JMenuBar.  The wholeadditional capability just "fell out of" the rigid separationbetween model and view.  I do not think I could have managed thisanywhere near so easily with the original all-in-one design.     The lesson (for me, anyhow), is that a careful discipline canyield unlooked-for benefits.  Do not short-circuit it casually.-- Eric Sosmanesosman@ieee-dot-org.invalid
0
Reply Eric 2/14/2008 2:31:31 AM

7 Replies
121 Views

(page loaded in 0.159 seconds)


Reply: