Proposal to reduce repetition with Swing and SWT listeners

DRY – Don’t Repeat Yourself

We know that this is good, since it reduces code maintenance, so why do we persist with writing listeners like this: (Note that this is example is written with SWT; Swing looks almost the same.)

        final Button button = new Button(shell, SWT.NONE);
        button.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                text.setText("Hello World");
            }
        });

If you’ve got lots of these lying around, well first off, you should try using an application framework like the Eclipse Rich Client Platform or the Spring Rich Client Project. But if you don’t (and even if you do, you still need to deal with wiring interface elements together), then you can make a code template in your IDE to generate this idiom. However, you’re stuck with maintaining all this code, plus the code is not as clear as it could be.

What if instead, your code looked like this?

        final MyButton button = new MyButton(shell, SWT.NONE);
        button.onPress( text, "setText", new Object[] { "Hello World" } );

Unfortunately, because Java places a higher value on security of its classes than expressiveness, you cannot add methods to an existing class (which you can do in some other languages). Also, you cannot subclass SWT widgets. So, instead, we use a utility class, which is slightly less readable.

        final Button button = new Button(shell, SWT.NONE);
        ButtonUtils.onPress( button, text, "setText", new Object[] { "Hello World" } );

This is better, since it takes what used to take 5 lines of code to express and put it into 1. It also focuses on what should happen when the button is pressed, instead of that being dominated by the supporting code.

Swing introduced something similar back in Java 1.4 called EventHandler. This generates a dynamic event listener. For example:

        JButton button = new JButton( "Hello World" );
        button.addActionListener( (ActionListener) EventHandler.create(
                                                        ActionListener.class, textField, "setText", "source.text" );

Note that this has the disadvantage of having to grab parameters to pass to the target object from the ActionEvent object. That’s why I stuffed “Hello World” as the text of the JButton for this simple example.

Here is the main piece for ButtonUtils. The code for findMethod is relatively simple, but I can post it if people want it.

    /**
     * Registers an object that has a method named methodName,
     * which is called when the button is pressed.
     * 
     * @param button
     * @param instance
     * @param methodName
     * @param parameters
     */
    public static void onPress( Button button, final Object instance,
                                                    String methodName, final Object[] parameters ) {
        final Method method = findMethod( instance, methodName, parameters );
       
        button.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                try {
                    method.invoke( instance, parameters );
                } catch (IllegalArgumentException exception) {
                    exception.printStackTrace();
                } catch (IllegalAccessException exception) {
                    exception.printStackTrace();
                } catch (InvocationTargetException exception) {
                    exception.printStackTrace();
                }
            }
        });
    }

So what do people think about this? Does anyone use something like this in their code? The advantages are: 1. DRY, 2. less code overall, 3. somewhat clearer (could be clearer with Java language changes). Disadvantages: 1. reflection isn’t refactored easily, 2. reflective code doesn’t take advantage of static type checking at compile time, 3. potential for reflection exceptions.

2 Replies to “Proposal to reduce repetition with Swing and SWT listeners”

Comments are closed.