views:

318

answers:

3

I've come across 2 different styles when adding button listeners in UI. I use SWT as example. But nonetheless I saw the similar code in J2ME, Flash Actionscript as well.

style 1:

 b1.addSelectionListener(new SelectionListener()
 {
  public void widgetSelected(SelectionEvent e)
  {
   System.out.println("b1: ");
  }

  public void widgetDefaultSelected(SelectionEvent e)
  {
   System.out.println("Default selection");
  }
 });

 b2.addSelectionListener(new SelectionListener()
 {
  public void widgetSelected(SelectionEvent e)
  {
   System.out.println("b2: ");
  }

  public void widgetDefaultSelected(SelectionEvent e)
  {
   System.out.println("Default selection");
  }
 });

style 2:

    b1.addSelectionListener(this);
 b2.addSelectionListener(this);

public void widgetSelected(SelectionEvent e)
{
 if (e.getSource() == b1)
 {
  System.out.println(b1);
 } 
 else if (e.getSource() == b2)
 {
  System.out.println(b2);
 }

}

public void widgetDefaultSelected(SelectionEvent e)
{
 System.out.println("Default selection");
}

Personally I prefer second style, because it gives me a centralize way to handle mouse event. Which style do you prefer? and why?

+2  A: 

I generally dislike the second style, because it creates a coupling between the two buttons and also with their container.

If the functionality of B1 and B2 is independent, B1 shouldn't know anything about B2 and vice versa. By sharing the event handler, not only are you doing a wasteful check, but you also break this independence. They now share their handling.

Furthermore, by taking option 2, you've now coupled three classes: B1, B2, and the container that has the handling mechanism. On the other hand, if you followed option 1, B1 is coupled to its personal handler, B2 is coupled to its own personal handler, and the container does not need to know anything about either handlers!

In fact, by following option #2, you are giving another responsibility to the container - it does event routing. The toolkit already does event routing for you - given a click, it will call the correct event handler. With this option, you are reinventing the wheel.

If the handling for each of the button is complex enough, it might make sense to create two subclasses of the button, and have each subclass install its own listener. Then, your container just instantiates the buttons, and does not have to actually contain the code (even as an anonymous class) for handling the event.

Note that I understand the sentiment of wanting to centralize event handling; however, that is micro-management. GUI programming is hard and tedious enough. Event handling is one of the more annoying things in GUI programming. It's one of the first things you should be happy about not having to route and manage yourself. There are very few convincing situations where you could manage things better than the toolkit.

Uri
1. For the 1st approach, If I have 10 buttons, I need to instantiate 10 listener classes? For 2nd approach, I need to add one more condition check, but it won't consume as much memory, it could run little slow, though.2. For the 2nd approach, I can remove listener by calling removeListener(this). I am not sure how to do that with anonymous listener.
janetsmith
Swing (and GUI in general) is so heavyweight, that the cost of a few instantiated handlers is minimal, especially since the code is stored separately. The object has minimal footprint.
Uri
As for removing the listeners, when it's time to shut down you call getActionListeners() to get all the associated listeners, then iterate over all of them and call removeListener on each.
Uri
+1  A: 

In my opinion this is one of those things where 'it depends' might be the best answer you're going to get. If you're doing something quick and dirty the first is going to be faster and easier. If you're developing a real application you're going to value the separation of concerns the second buys you (although I'd probably want to see the ActionListener stuff in a whole class of its own).

For what it's worth, almost all of the introductory Swing materials I saw when I was learning it a while back took the second approach.

The first has the disadvantage of scattering listeners around your code, but it also generally makes it easier to find the listener that deals with a specific element, and it can be a lot more flexible, and if you have more than a couple of elements it can be a lot less ugly than running through a giant if-else if block.

But the second separates your code a lot more cleanly--the listeners are controller logic, and the view (the GUI) shouldn't care what they do. It also centralizes your controller logic: if you need to change something, it's all there. If you do any web application programming the second is going to look a lot more familiar.

That said, it really comes down to the circumstances. If I'm developing a small application with only a few actions which route am I going to take? The first. It's faster and cleaner initially and connects elements and actions together in code which, at least at first, takes less effort to maintain. If I'm developing a larger application though? I'd create a separate class that is an ActionListener and have that be a dedicated controller. In the long run it's a lot cleaner and on an application of any real size you're going to be happy you did it in the long run.

Sam DeFabbia-Kane
A: 

Answer from Uri gave me some idea about matching the action with listener. I've just come out with a modified version of the first approach.

    private Map<Control, ICommand> commandMap = new HashMap<Control, ICommand>();
    private HelloCommand helloCommand = new HelloCommand();
    private WorldCommand worldCommand = new WorldCommand();

    protected Control createContents(Composite parent)
    { ...
            b1.addListener(this);
            b2.addListener(this);
     commandMap.put(b1, helloCommand);
     commandMap.put(b2, worldCommand); 
    }

    public void widgetSelected(SelectionEvent e)
    {
     ICommand command = commandMap.get(e.getSource());
     command.execute();
    }

It seems like I am going to create a bunch of classes though, instead of creating Listener classes, I create Command classes.

Normally Controller class can control view class & modal class. However in this case, I can't access view component directly, unless I make them inner classes. I think same goes to individual listener class approach. Any solution on this?

janetsmith