General musings on programming languages, and Java.

Thursday, June 21, 2007

Missing Refactor - Convert to Anonymous Class

I'm still looking through my source for stray uses of 'private', following on from my recent blog about removing private, favouring other forms of encapsulation. One way I like to remove private is by converting named implementations of interfaces into anonymous ones. Here's some real code that I want to change:


public final class EthernetCableIcon implements
  ActionListener
{
  private final MouseInputListener listener;
  private final GUIContext context;
  private final JToggleButton button;

  private EthernetCableIcon(final GUIContext context)
  {
    button=new JToggleButton
      ("Ethernet Cable",EthernetCableHandler.icon);

    this.context=context;
    listener=new MouseInputListener(context,button);

    button.setVerticalAlignment(SwingConstants.CENTER);
    button.setVerticalTextPosition(SwingConstants.BOTTOM);

    button.setHorizontalTextPosition
      (SwingConstants.CENTER);

    button.addActionListener(this);

    button.setToolTipText("Click on this to draw an "+
      "Ethernet Cable, then drag on the display to "+
      "make one appear");
  }

  public static JToggleButton newButton
    (final GUIContext context)
  {
    return new EthernetCableIcon(context).button;
  }

  /**
   * Replaces the NetworkView's mouse listeners with its 
   * own, so that the EthernetCable can be dragged
   * without causing other problems.
  */
  public void actionPerformed(final ActionEvent event)
  {
    final Component view=context.getNetworkContext()
      .networkView;

    if (button.isSelected())
    {
      context.getNetworkContext().toggleListeners.off();

      view.addMouseListener(listener);
      view.addMouseMotionListener(listener);

      view.setCursor(new Cursor(Cursor.HAND_CURSOR));
    }
    else
    {
      view.removeMouseListener(listener);
      view.removeMouseMotionListener(listener);

      context.getNetworkContext().toggleListeners.on();
      view.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
    }
  }
}
My IDE, IntelliJ IDEA, has lots of refactorings, including from anonymous to nested, from nested to inner, moving classes, etc., but has nothing for making this an anonymous class. It's used precisely once, from the static method newButton. I've refactored it by hand, which isn't too bad, but it does make me wonder why I have to. Eclipse never supported this refactor either. Post-refactor:

public final class EthernetCableIcon
{
  public static JToggleButton newButton
    (final GUIContext context)
  {
    final JToggleButton button=new JToggleButton
      ("Ethernet Cable", EthernetCableHandler.icon);

    final MouseInputListener listener=new
      MouseInputListener(context, button);

    button.setVerticalAlignment(SwingConstants.CENTER);
    button.setVerticalTextPosition(SwingConstants.BOTTOM);

    button.setHorizontalTextPosition
      (SwingConstants.CENTER);

    button.addActionListener(new ActionListener()
    {
      public void actionPerformed(final ActionEvent e)
      {
        final Component view=context.getNetworkContext()
          .networkView;

        if (button.isSelected())
        {
          context.getNetworkContext()
            .toggleListeners.off();
          
          view.addMouseListener(listener);
          view.addMouseMotionListener(listener);

          view.setCursor(new Cursor(Cursor.HAND_CURSOR));
        }
        else
        {
          view.removeMouseListener(listener);
          view.removeMouseMotionListener(listener);

          context.getNetworkContext().toggleListeners.on();

          view.setCursor
            (new Cursor(Cursor.DEFAULT_CURSOR));
        }
      }
    });

    button.setToolTipText("Click on this to draw an "+
      "Ethernet Cable, then drag on the display to "+
      "make one appear");
    
    return button;
  }
}
Whether it's more readable now probably depends on the reader, but it certainly doesn't need private and hasn't lost anything in the encapsulation. I find it slightly more comprehensible anyway.

2 comments:

Anonymous said...

The exact refactoring you describe was just added last week to the Early Access versions of IDEA 7.0.

Ricky Clarkson said...

I've just tried it out, it seems to work reasonably, but it's a bit weird yet, leaving single-use variables around, etc. Thanks for mentioning it.

Blog Archive

About Me

A salsa dancing, DJing programmer from Manchester, England.