søndag den 14. juni 2009

Introducing Static Intent, a Refactoring Pattern

- Refactoring with the intent to create static utility methods.

In many developers' mind, the keyword static has quite a bad reputation. It is in many cases considered poor Object-Oriented design to overuse static, and developers generally go to great lengths to avoid it, and only use it as a last resort in places where it just exactly fit. Take for instance the design pattern Singleton, which can be used to create globally accessible instances. It is generally implemented through lazy instantiation of a static variable and have an equally bad reputation. But in the case of Singleton, I actually tend to agree - that is bad design - almost never implemented as stateless, even though the pattern dictates this, making it inherently hard to test.

But in many other cases, static can easily be utilized without breaking the Object-Oriented mindset (e.i. utility methods and classes). And it is to this context that Static Intent belongs. It can be seen as an extension to the rudimentary Extract Method pattern.

As stated in the title, this is a refactoring pattern, so let us make an example of a method that needs refactoring. The example is pseudo Java.

public class MyFrame
{
  private Button button1;
  private Button button2;
  private Frame myFrame = new Frame();

  public void initFrame()
  {
    button1 = new Button();
    button1.setCaption("button 1");
    button1.setPosition(10, 10);
    button1.setSize(40, 20);

    myFrame.add(button1);

    button2 = new Button();
    button2.setCaption("button 2");
    button2.setPosition(50, 10);
    button2.setSize(40, 20);

    myFrame.add(button2);
  }
}

The first step in the refactoring would be to extract the two button instantiations and create two private methods.

public class MyFrame
{
  private Button button1;
  private Button button2;
  private Frame myFrame = new Frame();

  public void initFrame()
  {
    button1 = createButton1();
    myFrame.add(button1);

    button2 = createButton2();
    myFrame.add(button2);
  }

  private Button createButton1()
  {
    Button button = new Button();
    button.setCaption("button 1");
    button.setPosition(10, 10);
    button.setSize(40, 20);
    return button;
  {

  private Button createButton2()
  {
    Button button = new Button();
    button.setCaption("button 2");
    button.setPosition(50, 10);
    button.setSize(40, 20);
    return button;
  }
}

As the two methods are quite similar, the next logical step would be to create a generic createButton() method. Also, the button sizes seems to be the same, so let us put them up as final/const member fields.

public class MyFrame
{
  private Button button1;
  private Button button2;
  private Frame myFrame = new Frame();
  private final int buttonSizeX = 40;
  private final int buttonSizeY = 20;

  public void initFrame()
  {
    button1 = createButton("button1", 10, 10);
    myFrame.add(button1);

    button2 = createButton("button2", 50, 10);
    myFrame.add(button2);
  }

  private Button createButton(String name, int posX, int posY)
  {
    Button button = new Button();
    button.setCaption(name);
    button.setPosition(posX, posY);
    button.setSize(buttonSizeX, buttonSizeY);
    return button;
  }
}

And that would be it. The refactoring produced a nice and neat generic method and we are done. However, in my mind, we could take it one step further.

What we have made with the createButton() method is a utility method for creating generic buttons. However, it operates on two member variables - the button sizes. So we cannot make it a static utility method. But let us instead try to pass the two variables as parameters to the createButton() method. Now it does not operate on any member fields anymore, so we could actually make it static. The result is below.

public class MyFrame
{
  private Button button1;
  private Button button2;
  private Frame myFrame = new Frame();
  private final int buttonSizeX = 40;
  private final int buttonSizeY = 20;

  public void initFrame()
  {
    button1 = createButton("button1", 10, 10, buttonSizeX, buttonSizeY);
    myFrame.add(button1);

    button2 = createButton("button2", 50, 10, buttonSizeX, buttonSizeY);
    myFrame.add(button2);
  }

  private static Button createButton(String name, int posX, int posY, int sizeX, int sizeY)
  {
    Button button = new Button();
    button.setCaption(name);
    button.setPosition(posX, posY);
    button.setSize(sizeX, sizeY);
    return button;
  }
}

At first it might seem as unnecessary optimization, but I argue that there are many other benefits of refactoring with the intent to make static utility methods.

When utilizing Static Intent, you end up declaring methods as what they are - utilities. It minimizes the number of methods that access and change member fields, making the class easier to debug and less error prone. It is also easier to unit test these methods as they are stateless and fully independent of the state of the class - the state is fully given as parameters.

In reality, making the method static is not that important - it is much more important to make the methods independent of their class. Making them static is just a guiding line to get there and ends up being a "why not?". The optimization of not having to copy the method to every instance of the class is just added bonus.

The only drawback is that such methods cannot be polymorphic. But in my experience such methods created based on refactoring will most likely become private, making the polymorphism superfluous.

So, in the future I will continue to refactor my code with the intent to make static utility methods. And I encourage you to do the same.