Java Refactoring for Fun and Profit: Interface Arguments

By Greg Larson – Sr. Software Engineer – ADP Cobalt SEO & Blogging Team

Since code duplication is generally agreed to be a Bad Thing, Java programmers often refactor similar methods into a single method with a differentiating argument. A typical case is shown in the following example – methodA and methodB are refactored into methodC with the addition of a boolean argument :

public MyClass methodA(String arg1, int arg2) {
   MyClass myClass = createObjectWithArg1(arg1);
   int intVal = doStuffWithBothValues(arg1, arg2);
   myClass.setValue(doStuffWithIntVal(intVal));
   doMoreStuff(myClass);
   return transform(myClass);
}

public MyClass methodB(String arg1, int arg2) {
   MyClass myClass = createObjectWithArg1(arg1);
   int intVal = doStuffWithBothValues(arg1, arg2);
   if(intVal < 0) {
      myClass.setValue(handleNegative(intVal));
   }
   return transform(myClass);
};

public MyClass methodC(String arg1, int arg2, boolean doOptionA) {
   MyClass myClass = createObjectWithArg1(arg1);
   int intVal = doStuffWithBothValues(arg1, arg2);
   if(doOptionA) {
     myClass.setValue(doStuffWithIntVal(intVal));
     doMoreStuff(myClass);
   } else if(intVal < 0) {
      myClass.setValue(handleNegative(intVal));
   }
   return transform(myClass);
};

The differentiating argument can also be an interface. When the method is called, a concrete implementation of the interface is passed in. In many cases the result is similar to passing a function pointer to a C++ method. For reuse, the implementation should be an instance of an externally defined class. In the case of one-time use, it can be an anonymous implementation inline with the method itself.

This approach is typical when applying the Strategy design pattern; Java’s Collections.sort method provides a good example:

Collections.sort(list, new Comparator<MyClass>(){
    public int compare(MyClass a, MyClass b) {
        // define strategy for comparing objects
    }
});

In the above example, defining the Comparator inline increases the readability of the code, since there is no need to look in another class to see the comparison strategy.

However, the same refactoring opportunity is often overlooked in the case of methods that vary by multiple similar method calls – especially if there is other logic separating the calls. The example below shows two methods that, although similar in structure, might easily escape refactoring.

public MyClass operationA(MyClass myClass) {
   for(MyItem myItem : myClass.getList() {
      utilA.transform(myItem, 1));
   }
   doStuff(myClass);
   myClass = serviceA.execute(myClass);
   doMoreStuff(myClass);
   return myClass;
}

public MyClass operationB(MyClass myClass) {
   for(MyItem myItem : myClass.getList() {
      utilB.transform(myItem, 2));
   }
   doStuff(myClass);
   myClass = serviceB.execute(myClass);
   doMoreStuff(myClass);
   return myClass;
}

To refactor, define an interface with the signatures of the varying method calls:

interface MyInterface {
   void doMethod(MyItem myItem, int transformInt);
   int getTransformInt();
   MyClass execute(MyClass myClass);
}

Notice that an additional method was added in this case to supply the differing int values used in the transform method.

Now replace the two methods with a single method that takes the interface as one of its arguments:

public MyClass operationC(MyClass myClass, MyInterface myInterface) {
   for(MyItem myItem : myClass.getList() {
      myInterface.doMethod(myItem, myInterface.getTransformInt()));
   }
   doStuff(myClass);
   myClass = myInterface.execute(myClass);
   doMoreStuff(myClass);
   return myClass;
}

The refactored method can then be called with the interface implementation defined anonymously in the method call:

MyClass myClass = operationC(myClass, new MyInterface() {
   public void doMethod(myClass) {
      // call UtilA.transform or UtilB.transform or...
   }
   public int getTransformInt() {
      // return whatever int value is needed
   }
   public MyClass execute(MyClass) {
      // call ServiceA.execute or ServiceB.execute or...
   }
});

One caveat: the extracted interface methods should be cohesive. If the interface definition becomes a hodge-podge of unrelated methods, it should be divided into multiple interfaces so that each interface has a clear focus.

Granted, the above refactoring does not produce an impressive simplification for only two methods. But often there are several similar methods all executing slightly different strategies, in which case this approach eliminates significant duplication and streamlines code considerably. An easy win!

About collectivegenius
Everyone has a voice and great ideas come from anyone. At Cobalt, we call it the collective genius. When technical depth and passion meets market opportunity, the collective genius is bringing it’s best to the table and our customers win.

One Response to Java Refactoring for Fun and Profit: Interface Arguments

  1. I’m not that much of a online reader to be honest
    but your blogs really nice, keep it up! I’ll go ahead and
    bookmark your website to come back in the future.
    Many thanks

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: