aspose file tools*
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes writing good GUI code Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Java 8 in Action this week in the Java 8 forum!
JavaRanch » Java Forums » Certification » Developer Certification (SCJD/OCMJD)
Bookmark "writing good GUI code" Watch "writing good GUI code" New topic
Author

writing good GUI code

Ben Ritchie
Ranch Hand

Joined: Nov 18, 2002
Posts: 98
Hi chaps
I've put together a prototype MVC gui (at least, I thinkit's MVC. Not quite convinced that I've 'got it' yet, but that's a topic for another thread...), which seems to work OK. However, I'm used to server-side code, and to me the View code looks like a horrible mess - it's a huge class containing an explosion of widgets, gridbag constraints, listeners, utility methods and the like. It's really ugly. Anybody got some tips for writing good, easy to read swing code?
I've considered refactoring some of the more complex stuff (e.g. the Search and Booking panels) into their own classes, but that brings with it a load of Observer/Observable requirements for passing events back and forth. Still seems like a good idea though. Is it?


SCJP1.4, SCJD, SCEA (in progress)
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17249
    
    6

It appears that you are trying to put the MVC all into one class. What you need to do is split it all out and refactor.
First the View should only have code to draw the screen, this includes using GridBagConstraints, and also hook methods so that you can attach listeners to the JButtons, JComboBox, etc. Then you will have a controller that will call the view to add the listeners. The Model is the Data and your DefaultTableModel.
Now Eugene will add here and have a little different way of handling the actions and the communication between the Model and the View. Both ways work.
Here is an example of a Hook method in your View.

and here is an example of what I have in the controller to attach to the View

The searchFlight method is in the Controller and handles that Action to be done when this button is pressed.

I have a reference to the View in the Controller called frontEnd.
My take on the Controller is it controls everything it is the communication center and talks to all objects in the MVC, hence the reason for it's references to the View and the Models.
Mark


Perfect World Programming, LLC - Two Laptop Bag - Tube Organizer
How to Ask Questions the Smart Way FAQ
Ben Ritchie
Ranch Hand

Joined: Nov 18, 2002
Posts: 98
Hi Mark
I do have MVC split into 3 classes (I've used something close to Eugene's flavour of MVC), although the model and controller are fairly small. I'm happy with them - it's just that the View is bl**dy ugly
The view does have hooks for listeners that require changes in the model (e.g. for search button, booking button). However, the view also contains listeners for actions contained completely within the view (e.g. if you select a different row in the JTable that displays the data, the action of updating an 'available seats' JTextField in the booking Panel is completely within the View - the model doesn't need to change - so I have put the listener is in the View class). There doesn't seem to be any need to pass these events through to the Controller, as neither it nor the Model care about them.
I also have utility methods in the view (e.g String getSearchCriteria(), which converts the values in search combo boxes in the View into a formatted String to pass to the Data.criteriaFind() method). These are just to avoid a load of getter methods (all GUI components in my view are private - and, yeah, I know you disagree with that too ).
My 'problem' isn't really with the internal listeners and utility methods, because they are quite small. The problem is with the couple of dozen widgets that build the View. Most need gridbag constraints to get them to appear in the correct place, and once you've built the object, added tooltips, colours etc., set the gridbag constraints and added them to a container you've written a dozen or so lines of code. Per widget. That adds up to a lot of code and a monster class and it's hard to read and hard to figure out what you are doing - even when you wrote it. I figure that experienced swingers must know a better way ...
Hope this explains it better
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17249
    
    6

e.g. if you select a different row in the JTable that displays the data, the action of updating an 'available seats' JTextField in the booking Panel is completely within the View - the model doesn't need to change - so I have put the listener is in the View class). There doesn't seem to be any need to pass these events through to the Controller, as neither it nor the Model care about them.
I also have utility methods in the view (e.g String getSearchCriteria(), which converts the values in search combo boxes in the View into a formatted String to pass to the Data.criteriaFind() method). These are just to avoid a load of getter methods (all GUI components in my view are private - and, yeah, I know you disagree with that too ).

All of these methods belong in the Controller. You have the Data class tightly coupled in you View, which goes against MVC.
You can also avoid the getter methods with those that the controller needs to access by having them as public, otherwise you are stuck with the Getters which are much prettier code than the above methods you emntion you have in the view.
As far as the GridBagContraints problem. Have you thought about creating a GridBagConstraintsHelper class that has static methods to change the values in the GridBagConstraint.
For example here is my code.

The first line is changing the anchor, which was set to GridBagConstraints.WEST earlier for the previous 5 items I was placing on the GUI.
Here is one example of one of the overloaded method makeConstraint

Mark
John Smith
Ranch Hand

Joined: Oct 08, 2001
Posts: 2937

The problem is with the couple of dozen widgets that build the View. Most need gridbag constraints to get them to appear in the correct place, and once you've built the object, added tooltips, colours etc., set the gridbag constraints and added them to a container you've written a dozen or so lines of code. Per widget. That adds up to a lot of code and a monster class and it's hard to read and hard to figure out what you are doing - even when you wrote it.

Did you use an IDE to generate the code for the components within your gridbag layout? I did that initially and it did create a mess. I then refactored it manualy and ended up with much more compact and readable code.
Eugene.
Ben Ritchie
Ranch Hand

Joined: Nov 18, 2002
Posts: 98
Thanks Mark - looks like I still have some thinking to do about MVC...
Eugene - The widget explosion is all of my own making. I write server-side code (JMS) in the day job, and this is my first go with swing.
Ben Ritchie
Ranch Hand

Joined: Nov 18, 2002
Posts: 98
OK, lots of reworking. Lets see if I've got MVC right now...
Model. This contains the important values displayed by the view; a String[][] for the table data, a String[] for the database schema, the current search string and the number of seats to be booked. It contains methods to access the facade (search, book...) and setter methods to change the model. All it knows about is the Data facade - it doesn't know about the View or the Controller. The class shouts when it changes, but doesn't care if anybody is listening.
View. This just sits there and looks pretty. It registers itself as a Model listener when it is created, and all it knows how to do is to update itself when it hears the model shout. It contains hooks so the Controller can set listeners on assorted components, but is unaware what the listeners are doing.
Controller. Knows about both the Model and the View. It registers listeners with the View on startup, and when they fire it gets the relevant information from the View, transforms and validates it if necessary (e.g. it converts the search ComboBox values into a String for criteriaFind, and checks that the number of seats to be booked is not greater than the number available) and passes it to the model setter methods.
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17249
    
    6


Mark
Ben Ritchie
Ranch Hand

Joined: Nov 18, 2002
Posts: 98
Thanks Mark - i'm getting there. One more thing is puzzling me
I've tried to decouple the View and Controller as much as possible, but it still isn't possible to change the View without changing the Controller too. In my implementation, any View must implement an interface (FBNControllable) which specifies the listener hooks (this isn't necessary, but when one class depends on another having certain methods I like to enforce it via an interface).

The last method allows you to set a listener which updates the Model based on which flight is selected. Currently I'm using a JTable, so the Controller creates and registers an (anonymous) implementation of ListSelectionListener. However, if I change the View then I must also change the Controller to register the correct listener to respond to the widget changing ... i.e. they are still coupled. I cannot see a way round this - am I missing something obvious?
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17249
    
    6

No matter how much decoupling you try, somehwere in the MVC there ahs to be a little coupling. It may be as far removed as the actual Main program that creates the Controller, View, and model instances. But somewhere that has to happen. That is why I always leave that to the Controller.
I think that you will have to make a change like that if the Widget changes. In your SelectionListener example. "However, if I change the View then I must also change the Controller to register the correct listener to respond to the widget changing"
Mark
John Smith
Ranch Hand

Joined: Oct 08, 2001
Posts: 2937

However, if I change the View then I must also change the Controller to register the correct listener to respond to the widget changing ... i.e. they are still coupled.

What I found works best is combination of Mark's approach of GUI "hook" methods with my approach of keeping the widgets private in the views. With that MVC design, if you rename, move, or replace your widget with something else in the View, absolutely nothing needs to change in the Controller, Model, or anywhere else. However, if you add a new widget to View, then of course the controller needs to change, -- but this is as expected, since the controller acts as a listener of user "gestures".

Notice that if I move, rename, or replace the searchFlightsButton with some other control, only View needs to change. Same goes for the two controls that hold the origin and destination airports: I can replace the comboboxes with text fields, or move them to another panel, or rename them, and except for the View itself, nothing needs to change in MVC or anywhere else.
Eugene.
[ February 27, 2003: Message edited by: Eugene Kononov ]
Ben Ritchie
Ranch Hand

Joined: Nov 18, 2002
Posts: 98
Thank you both for your help - I know you've had to teach MVC for dummies many times before, but it has been very helpful. I finally get it .. I think
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: writing good GUI code
 
Similar Threads
Passed SCJD with 377
MVC + Nickers in twist :)
MVC to support web clients and GUI clients?
problem w/ MouseListener and JLabel
your suggestions to make my "design" correct