Friday, January 18, 2008

 

SUPIO - Refactoring Monolithic Methods

We've all seen them, and probably occasionally written them. And, if you're unlucky you've probably even had to modify them when they're written by other programmers. I'm talking about those huge "megalithic" functions -- a single method that is hundreds of lines of code and reaches 14 indents deep. Such methods are often difficult to refactor because of their complexity; any attempt to refactor them results in passing tons of variables as parameters, or creating custom structures or classes just to pass parameters around. I have come up with a time-honored solution that is proven to work for me in such cases.

I am not a big patterns guy. I am not against patterns -- it's just that I have never even read a single book on programming patterns. Yet, at the same time, I use common patterns because of solutions I have come up with myself or picked up from others over the years. So, forgive me if this is already a pattern known by a different name, but I believe it to be a pattern that I have created on my own. I am talking about what I call SUPIO: Single Use Per Instance Objects, the purpose of which is to refactor huge, monolithic methods.

A SUPIO is similar to a functor, but with a different purpose. The purpose of a SUPIO to refactor code, whereas the purpose of a functor is to pass a function along with its input parameters and expose a common mechanism for executing the function via an Execute() method.

The idea behind SUPIO is simple: Create a dedicated class that exposes one public static method that takes any necessary parameters. That static method creates an instance of an object, stores the input parameters in the object, executes a single instance method, and returns the results of that instance method out of the static function without ever actually returning the instance object that was created to do the work. That root instance method may in turn call other instance methods without passing a ton of parameters around because many things can be store as private instance member variables.

The example below illustrates these concepts. Keep in mind that using a SUPIO for this particular problem is overkill. But, as the complexity of the method goes up, so does the bennefit of using a SUPIO. In my experience, few things ever start as SUPIOs. Rather, a method is refactored into SUPIO as its complexity reaches the point of unmaintainability. [Most of the time this is when I have to modify a megalithic method written by someone else.] Hopefully you can refactor it before it implodes, or perhaps even have the forsight of starting it as a SUPIO if you know the logic is going to be outrageously complex.


// A SUPIO class will generally be internal because it makes for
// a non-friendyly API if it is exposed out of the assembly.
internal class MySupio
{
// This is the static method that exposes the single
// functionality of the SUPIO
public static bool DoSomething(int x, int y, out int z)
{
// Create an instance of the object, execute the function
// and return its results
MySupio mySupio = new MySupio(x, y);
bool retVal = mySupio.Execute(out z);
return retVal;
}

// Declare parameters and calculated values needed by
// multiple private instance methods as private instance
// member variables.
private int _x;
private int _y;
private int _a;
private int _b;

// The constructor is private to ensure that an instance
// is only created by the public static method that exposes
// the function of the SUPIO.
private MySupio(int x, int y)
{
// Store input parameters as private instance member variables
// so they can easily be shared between instance methods
_x = x;
_y = y;

// Calculate any additional values that may be needed often
// across multiple instance methods.
_a = 2 * _x + _y;
}

// Execute the function of the SUPIO. (Note that this
// method has been fabricated for this example to do some logic
// and use variables that were input parameters, calculated
// from the input parameters, and returnd from other methods.)
private bool Execute(out z)
{
int c;
switch(_x.CompareTo(_y))
{
case -1:
c = XLessThanY();
break;

case 0:
c = XEqualToY();
break;

case 1:
c = XGreatherThanY();
break;
}

z = c * _b;
}

private int XLessThanY()
{
...
}

private int XEqualToY()
{
...
}

private int XGreaterThanY()
{
...
}
}

// If the function performed by the SUPIO needs to be exposed
// out of an API it can easily be accomodated.
public static MyLibraryClass
{
public static bool CallMySupio(int x, int y, out int z)
{
return MySupio.DoSomething(x, y, out z);
}

public static bool MyNonSupio( ... )
{
...
}
}


Some people my question why an instance of an object has to be created? Why not just create a static class, with static member variables and static methods. The problem with that is that you can only have one thread in the method at the same time, and it is cannot recursive.

Comments: Post a Comment





<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]