Skip to main content

Updating Value Objects: Events or Members

16 replies [Last post]
davetron5000
Offline
Joined: 2003-06-10
Points: 0

From my blog http://www.naildrivin5.com/davec/archives/000061.shtml

Please forgive the formmatting that this forum has put on the code snippets.

After implementing a buttload of UIs in Swing (usually data collection forms), I'm currently wondering what is the "right way" to go about it. It seems there are two main options: Create member variables of the various UI controls, and query their values in an ActionListener, or attach DocumentListeners, ItemListeners, etc. to the components and have the listeners update a central object (like a datacontainer).

Here's an example of the two approaches: We'll implement a subclass of JPanel that collects a username and password and sends it to the backend for validation. First, some support classes:

public class LoginCredentials
{
private String itsLoginName;
private String itsPassword;

public LoginCredentials(String loginName, String password)
{
setLoginName(loginName);
setPassword(password);
}

public String getLoginName() { return itsLoginName; }
public void setLoginName( String loginName ) { itsLoginName = loginName; }

public String getPassword() { return itsPassword; }
public void setPassword( String password ) { itsPassword = password; }

}

Now, let's assume a business delegate called LoginDelegate with the following interface:

public interface LoginDelegate
{
/** Returns true if login is good */
public boolean login(LoginCredentials credentials);
}

Now, the first way to implement our panel, using member variables might be:

public class LoginPanel extends JPanel
{
private JTextField itsLoginField;
private JPasswordField itsPasswordField;

public LoginPanel()
{
itsLoginField = new JTextField();
itsPasswordField = new JPasswordField();

// Omitting gridbaglayout stuff for clarity

add(new JLabel("Login:"));
add(itsLoginField);
add(new JLabel("Password:"));
add(itsPasswordField);

JButton button = new JButton("Login");
button.addActionListener(new LoginActionListener());
add(button);
}

private class LoginActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
LoginCredentials credentials = new LoginCredentials();

credentials.setLoginName(itsLoginField.getText());
credentials.setPassword(itsPasswordField.getText());

if (LoginDelegate.login(credentials))
{
// success
}
else
{
// failure
}
}
}
}

This is pretty straightforward, except if you imagine this with a more typical form with a ton of fields, checkboxes, combo boxes, and other complex controls, you end up with a ton of member variables and potentially nasty ActionListener, as it has to coerce the form values into the Value Object to pass to the back-end. What happens if we use Swing's event infrastructure instead?

public class LoginPanel extends JPanel
{
private LoginCredentials itsCredentials = new LoginCredentials();

public LoginPanel
{
// Omitting gridbaglayout stuff for clarity

JTextField tmpField;

add(new JLabel("Login:"));
tmpField = new JTextField();
tmpField.getDocument().addDocumentListener(new LoginListener(LoginListener.LOGIN_NAME));
add(tmpField);
add(new JLabel("Password:"));
tmpField = new JPasswordField();
tmpField.getDocument().addDocumentListener(new LoginListener(LoginListener.PASSWORD));
add(tmpField);

JButton button = new JButton("Login");
button.addActionListener(new LoginActionListener());
add(button);
}

private class LoginListener implements DocumentListener
{
public static final int LOGIN_NAME = 1;
public static final int PASSWORD = 2;

public LoginListener(int field)
{
itsField = field;
}

private void documentChanged(Document d)
{
try
{
String value = d.getText(0,d.getLength());
switch (itsField)
{
case LOGIN_NAME :
itsCredentials.setLoginName(value);
break;
case PASSWORD :
itsCredentials.setPassword(value);
break;
}
}
catch (BadLocationException ble)
{
// log it
}
}

public void changedUpdate(DocumentEvent e)
{
documentChanged(e.getDocument());
}
public void insertUpdate(DocumentEvent e)
{
documentChanged(e.getDocument());
}
public void removeUpdate(DocumentEvent e)
{
documentChanged(e.getDocument());
}
}
private class LoginActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if (LoginDelegate.login(itsCredentials))
{
// success
}
else
{
// failure
}
}
}
}

Certainly, this way requires some more code (although the ActionListener is vastly more simple, and note that the ActionListener will look just like that regardless of the number of fields.), but it feels somehow cleaner and more correct to be using the events. Also note that our LoginListener class can also implement ItemListener, or any other event handling interaface we need. With some extra code, we've separated the code to create the UI, the code to get data from the UI and the code to hit the back-end. So, is this better?

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
karsten
Offline
Joined: 2003-06-11
Points: 0

Another option for your problem is to use adapters that bind domain object to the UI elements; this works as follows:

Create a class that represents your domain object; you've choosen a LoginCredentials class with two properties: loginName and password. The domain classes make up a whole layer, the domain layer where you can model and code all domain related issues. This code is not related to the UI presentation.

On the other end of this architecture you have the presentation layer, where all your containers and the UI component tree is built. In your case the Swing presentation will likely use a JTextField, a JPasswordField and a JButton. The UI can be built by a class that vends a JPanel instance; often this class doesn't need to be a JPanel subclass. I favor to use JPanel over subclassing it.

In the middle between the domain and presentation layers you can locate objects and code that connect both sides. In the literature this is sometimes called the [i]application model layer[/i] or [i]tool model layer[/i]. There are a couple of approaches to actually do the connection: 1) copy property data back and forth (by hand), 2) bridge the domain properties and Swing convenience models, 3) plug-in Swing models that adapt the domain properties.

The first approach reads domain properties and sets them into the Swing component models via the component setters. This is easy to understand, works in almost all cases. But it scales poorly and makes it difficult to a) automatically reflect changes in the domain and b) bind the domain objects to multiple views. The latter can be done by adding a couple of listeners.

The second and third approach convert the domain object properties into a format that is suitable for the UI components. Most developers are familiar with this approach when using a JTable. You write a custom TableModel that connects your JTable to your domain data. The same can be done with text fields, combo boxes, check boxes, lists, etc. The two approaches slightly differ in that the third replaces the Swing convenience models where the second bind or wraps the convenience models. In the third approach the JTextField is being connected to the loginName property using a custom implementation of the Document interface.

Anyway, to prepare domain object properties for being adaptable you can use a helper class like a PropertyAdapter or BeanAdapter that converts an object property into an object that in turn can be wrapped, used, delegated to, etc. Optionally you can observe property changes and report changes to the UI.

What is so attractive about the above approach is a clear separation of concerns, among other things the separation of domain and presentation. That is the motivation behind the popular MVC pattern. Where MVC doesn't really fit into the Swing architecture, the separation of domain and presentation does. You can find a good motivation to separate the domain from the presentation in the Big Refactorings section of Fowler's refactoring book, see pp.370.

I have found that developers welcome the separation into three layers; it leads to a clear production process. You can work on the domain classes, someone else creates the presentation layer, and a third role is to provide the connection. The connecting layer consists of all presentation specific models and code including actions and event handling. This way the presentation layer is quite dumb and is primarily responsible for the construction and layout of the panels.

I plan to publish sources for a sample application that follows approach 3 later this year together with a data binding library that consists of property adapters, buffering models, multiplexers, and a set of adapters for the Swing components.

davetron5000
Offline
Joined: 2003-06-10
Points: 0

I think I follow you, however code would be more helpful.

In realityi, I have some forms which have 10 or even 20 fields on them. Usabilty aside, I wouldn't relish creating 20 Document implementations.

I guess the slipperly slope would be a PropertyDocument that uses reflection to get and set a particular property of a DomainObject to it's text value.

And reflection always makes me nervious, because it seems like more often than not, it is a sign of poor design (i.e. it is a severely overused feature).

Right now, my JPanel subclass' constructor basically takes a Domain object and calls setText() on all the fields created. Then in the ActionListener for the "doit" button, it calls getText() on said fields, then set[i]PropertyName[/i] on the Domain object for each property.

Perhaps this is a rationale for HashMapDTOs instead of proper Domain Objects. That would eliminate the need for reflection and thus one custom Document class could be created, as opposed to several.

karsten
Offline
Joined: 2003-06-11
Points: 0

> Usabilty aside, I wouldn't relish creating 20 Document implementations.

Instead of creating individual Document implementations you can reuse a single DocumentAdapter if and only if it adapts to a uniform API. That's where the ValueModel interface comes in; it only defines two methods: #getValue() and #setValue(Object).

You can combine ValueModel implementations quite freely, for example to buffer a value (defer a write through to the domain layer), to multiplex, for indirection or to convert value types.

To connect ValueModels to the domain layer I use a PropertyAdapter that connects and optionally observes bound properties of bean-style domain objects. As a former compiler constructor I was concerned about using the bean PropertyChangeSupport; reality shows that this isn't the place where problems arise. But you can write indidivual type safe PropertyAdapters.

The whole queue from domain to the presentation object can then look like this:
domain object - PropertyAdapter - Indirecting ValueModel - Buffering ValueModel - DocumentAdapter - JTextField. I construct this queue with a few lines of code using convenience factory methods.

As you can see all classes, except the domain class are reusable. The downside is, that you loose the compile time type safety. But I've found that this is almost no problem. A true problem is the increased learning curve. Novice developers need to understand many new concepts, where each is quite simple: domain adapters, property change support, value indirection, buffering, Swing adapters. Consequently I discourage to follow this approach in a team of inexperienced developers. On the other hand, the biggest advantage of this approach/framework is, that it leads to a good Swing production process; every developer knows where to locate what code. Also, an advantage over the simple and powerful copying pattern is, that you can: 1) easily observe and reflect domain property changes, 2) easily present properties on multiple synchronized views.

As said before, this will be much easier to understand if I publish the sources of both the framework and an example application.

zander
Offline
Joined: 2003-06-13
Points: 0

First put code blocks aroun your code to make it more readable. (see 'help')

I always use the approuch to
a) create a GUI only class with protected members like 'JLabel' and 'JTextField'
b) extend from that class and put your logic for setting and getting the text in there. A login screen can be simply a short piece of logic, while a data-entry form can take a dataobject where the values are set/restored from.
c) use firePropertyChange events and listeners to signal 'changed' content (for a save button etc.)
d) add a hierarchy of 'open(MyDataObject o)' methods on your Gui to 'fill it'

The above approuch is loosly based on Model View Controller design pattern

davetron5000
Offline
Joined: 2003-06-10
Points: 0

Why make two classes? Why not put the getting/setting logic in the same class if you are creating members?

My approach (and I didn't realize about the code block thingy) is to use a datacontainer as a "model", and have a class listen for DocumentEvents and ItemChangeEvents to update that "model" whenever the user changes a control.

It creates a lot of objects, but for a complex form, you don't end up with 50 protected/private members and huge amounts of code to get values out of the controls. Plus, your action listeners are WAY less coupled to your gui class.

BUT, it still doesn't feel right. What would be nice is if the events fired from the controls (DocumentChange, ItemChange, etc.) could include some sort of field name or something other than a reference to the control. Then, your "model" could be the listener itself, but not have to know what kind of control it was listening for. OR, even better, have a "ValueChangedEvent" that abstracts you from DocumentChange and ItemChange. PropertyChangeEvents seem to be used in Swing for configuration properties of controls NOT for user input values (for whatever reason)

zander
Offline
Joined: 2003-06-13
Points: 0

> Why make two classes? Why not put the getting/setting logic in the same class if you are creating members?

For a very simple reason; to decouple the GUI from the logic that controls it.
I can move objects around and even change a button to a UICColorButton if I want, without changing _anything_ in the controller class.
Since I automatically generate my GUI class (the one I extend) this is even more evident.

> My approach (and I didn't realize about the code block thingy) is to use a datacontainer as a "model", and have a class listen for DocumentEvents and ItemChangeEvents to update that "model" whenever the user changes a control.

This has a fundamental flaw; you are violating the model-view-controller in its assumtion that the model is the one that drives the view.
Imagine a model having 2 views (a list of all documents and another panel with the specifcs of one document allready is 2 views to the same data). When a view triggers an event to change the model and the model triggers an event to update all the views; you end up in an endless loop.
The proposal you made is commonly described as an antipattern (use google on that one : )

Next to that problem; you will have points that you want to check the value before it goes into the model, so you can't just blindly use the text the user typed. Imagine a field that gives a name to a user. You don't want the username to be empty, but disabling the field from being emptied with backspace is also not an option...

As a last argument; your view (that one panel or form) displays a very specific part of your model. However you look at it, the view is intimitely familiar with the model. Decoupling that is silly and can only be called over design.

Hope to have given you more food for thought; don't hesitate to share more ideas with us!

davetron5000
Offline
Joined: 2003-06-10
Points: 0

I guess I'm not really concerned so much with preserving MVC, moreover, I'm trying to make the classes in question maintainable and easy to understand. MVC is just a technique, but if it loses sight of the goal, it should be set aside.

So, I'm still not really seeing why your proposal is advantageous. It doesn't seem to decouple anything really that much (or at least any more than my event-based approach). I mean, you still have to write code to translate values into those specific to the control used to collect them, as well as code used to grab the values out of said control. In my approach, since it never acutally queries the control for a value, but rather uses the event system, it's somewhat more decoupled. I can replace JTextFields with anything that fires a DocumentChangeEvent, and my code won't have to change. The main problem with my approach (as you point out) is that if the model changes, it is not clear how the UI gets updated.

I guess I'm just trying avoid the big-huge 200-line action listener that goes through all the protected members calling getText() or isSelected() or whatever. It makes it very hard to modify when you have a reasonably large form.

As to checking values before putting them in a model, that is really business logic, and in my applications that logic is completely decoupled from the interface (and usually resides in an EJB on the server), so the front-end really doesn't do any checking at all. It takes whatever the user provided and tries to process it, returning error messages as appropriate. Accomplishing that while maintaining usability is a whole other thread of discussion....

Message was edited by: davetron5000

zander
Offline
Joined: 2003-06-13
Points: 0

> So, I'm still not really seeing why your proposal is
> advantageous.
The answer to that is simple; because it fits into the rest of my program and setup.

From experience I found that saving your GUI to you model can easily be done when you change tabs or even just when the users presses 'ok' or 'save'.
This completely eliminates the need for listeners.
At the same time I create widgets that are so focused on their goal that I have never had a method for synchronizing data that extends 50 lines. Including business logic to check illegal values etc.

That said; at my company we wrote a framework to do event handling a lot better then the kludge that Java Swing provides, meaning that, yes, I too use events based calls to do things like enabling buttons etc.

But in the end all of this does not matter; I _still_ use a separate class for my GUI code as one for my gui handling code. Simply because its easier to maintain and takes stuff out of my face that I don't want to know about.
I have never had a 200 member class, simply because 200 editable-widgets on screen is absurd, and will never happen in real live.

As my last note; if you did not have to write GUI code, would you still want to place your gui-handling code in the same class?
Take a look at the zips on http://uic.sf.net/tutorials/ to see an example of very simple classes because the GUI code is not in there anymore.

davetron5000
Offline
Joined: 2003-06-10
Points: 0

It is not always as simple as:

[code]
myDataContainer.setName(nameField.getText());
[/code]

sometimes you have to go through more gyrations. And, really, if your business logic for any form is never more than 50 lines, you are very lucky. The projects I work on require multiple pages of requirements just to explain the validation of form data and what happens when the user submits information!

And, my example doesn't really combine the gui handling code with the gui creation code; in fact my gui handling code, since it is event-based, is very abstracted from the gui-creation code. In my example, it's in the same .java file, but that is not a requirement. And that's why I thought to do the event-based approach, because it didn't seem right to have an inner (or extending, in your case) class coupled to a bunch of fields that essentially amount to global variables when there was a complete event framework that seemed to be made just for the purpose of updating your own model.

> From experience I found that saving your GUI to you model
> can easily be done when you change tabs or even just
> when the users presses 'ok' or 'save'.
Well, why is elimination of listeners of any importance? Is there a performance issue with having listeners? I haven't noticed, but haven't profiled either. In my case, the model is constantly updated when any value changes, and so my business logic is abstracted entirely from model-population logic (but it kinda has to be in my case, since the business logic is on the server).

zander
Offline
Joined: 2003-06-13
Points: 0

> > From experience I found that saving your GUI to you
> model
> > can easily be done when you change tabs or even
> just
> > when the users presses 'ok' or 'save'.
> Well, why is elimination of listeners of any
> importance?

Less programming; less creation of objects (when no listener exists then the event is not even created in Swing) and naturally less objects being present.

I'm curious; since you seem to have automated many parts of this design; do your listeners all unregister when the document (model) is unloaded and another one is loaded?
Otherwise you will have memory leaks as well, which brings me to the next point; (many) more listeners means more data to maintain.
Unless you are using a framework that basically means more errors.

davetron5000
Offline
Joined: 2003-06-10
Points: 0

Perhaps all of this is a function of the application I'm working on. It is very much like a web-app in how it operates. When a form is successfully completed, it is basically discarded. I guess that could cause a memory leak, depending on what happens with the controls are all GC'ed.

And, I'm not automating anything, really. It's all done by hand. I have not had any luck creating a usable interface any other way. I guess I just hate having to create so many member variables just; it complicates the data-flow within the class. Most of the forms in my application are quite large (maybe not 100 elements, but there can be 30 or 40)

The memory issue hadn't occured to me, and that is probably a problem.

I guess what I'd really like is to be able to tell a JTextField (e.g.) that it's "model" is the "firstName" (e.g.) property of my DataContainer, and have it display the value there, and have it call setFirstName() whenever the user changes it. Without a custom JTextField that doesn't seem possible.

zander
Offline
Joined: 2003-06-13
Points: 0

Tried java.beans.EventHandler yet?

[code]
myTextField.getDocument().addDocumentListener(
(javax.swing.event.DocumentListener) EventHandler.create(
javax.swing.event.DocumentListener.class, model, "FirstName", "source.Text"));

[/code]

or similar ]:)

I really dislike that interface; but it is standard JVM!

davetron5000
Offline
Joined: 2003-06-10
Points: 0

HOw did you determine the key "source.Text"? I agree it's hella ugly, tho...

zander
Offline
Joined: 2003-06-13
Points: 0

the argument of the method (event) can be queried:
get[b]Source[/b].ge[b]tText[/b]

But there is a "interfaceMethod" name missing somewhere in there. I created a in-between class that handles this for me and a framework that nicely connects stuff.

davetron5000
Offline
Joined: 2003-06-10
Points: 0

That must be in 1.4. I'm still using 1.3 :(

mthornton
Offline
Joined: 2003-06-10
Points: 0

> That must be in 1.4. I'm still using 1.3 :(

True, but the mechanism for implementing it (java.lang.reflect.Proxy) was introduced in 1.3. Thus you can implement something similar and save a huge number of trivial inner classes.