Monday, January 14, 2008

Named Paramaters and Layout Managers

I had made a hasty comment on a java.net post that named parameters made a lot of scene for layout managers. Of course it started to bother me because while I felt I was right, I really had only ever used one language that had named parameters. That language had a lot of weird things in it and so I had to do some experimentation to see if I might actually be right. To that end I tried to implement something that looked syntactically like named parameters for GridBagLayout.

If Java supported a variable list of named parameters for layouts it might look like this:

add(new JLabel("test"), gridx:1, gridy:1, weight:1, fill:BOTH);

Now there is a couple ways to actually write this today.

Example : 1

GridBagConstraints constraints = new GridBagConstraints();
constraints.
gridx = 1;

constraints.
gridy = 1;

constraints.fill = GridBagConstraints.BOTH;
constraints.
weightx = 1;

add(new JLabel("test"), constraints);

This isn't so bad, but it's a little verbose, it really adds up when you have oh say a dozen or more controls in a container The next example is something I've see a certain commercial IDE create. This is just maddening to try to read.

Example 2:

add(new JLabel("test"), new GridBagConstraints(1,1,1,1,1,0,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0,0,0,0), 0, 0));

To see if anything would actually be saved by named parameters I built GridBagFactory. It and it's related interfaces and helpers make the following syntax possible.

... create buttons in JPanel ...

public JavaTutorialDemo() {

GridBagFactory gbf = new GridBagFactory(this);
gbf.addDefaultConstraints(FILL.
HORIZONTAL);
gbf.add(
button1);
gbf.add(
button2);
gbf.add(
button3, GRID.WIDTH.REMAINDER);
gbf.add(
button4, PAD.y(40), GRID.WIDTH.REMAINDER);
gbf.add(
button5, INSETS.top(10), GRID.xy(1,2), GRID.WIDTH.REMAINDER, WEIGHT.y(1), ANCHOR.PAGE_END );

}

You can compare this to the original code in the Java tutorial . The relevant GridBag code weighs in at about 35 lines of code. GridBagFactory can do it in 6. Now code density is meaningless if it's not immediately clear what the line of code is doing. There is some noise with the declaration of the GridBagFactory object, but with a little inheritance and overriding I created GBFPanel. A JPanel integrated with a GridBagFactory.

... create buttons in JPanel ...
public
JavaTutorialDemo() {

addDefaultConstraints(FILL.HORIZONTAL);

add(button1);
add(
button2);
add(
button3, GRID.WIDTH.REMAINDER);
add(
button4, PAD.y(40), GRID.WIDTH.REMAINDER);
add(
button5, INSETS.top(10), GRID.xy(1,2), GRID.WIDTH.REMAINDER, WEIGHT.y(1), ANCHOR.PAGE_END );

}

Thats darn close to the named parameter syntax from the beginning. If we rewrite it in that style it becomes just a tad clearer.

... create buttons in JPanel ...
public
JavaTutorialDemo() {

addDefaultConstraints(FILL.HORIZONTAL);

add(button1);
add(
button2);
add(
button3, gridwidth:REMAINDER);
add(
button4, pady:40, gridwidth:REMAINDER);
add(
button5, top:10, gridx:1, gridy:2, gridwidth:REMAINDER, weighty:1, anchor:PAGE_END );

}

I tried to recreate a number of layout examples from other very powerful layouts, like MigLayout and PageLayout. In my test cases I came in really close in code density to those other layout managers. My point really is not about any particular layout manager but about named parameters. In this case it seems to increase readability and code density. So I feel better about my hasty statement, I think all layout managers would benefit from named parameters.

I'll post the full code for GridBagFactory just as soon as I find a place to host it.

No comments: