This post originated from an RSS feed registered with .NET Buzz
by Jonathan Crossland.
Original Post: Refactoring complex classes using Composition Part 1
Feed Title: Jonathan Crossland Weblog
Feed URL: http://www.jonathancrossland.com/syndication.axd
Feed Description: Design, Frameworks, Patterns and Idioms
Using Design Patterns which deal with compositional power, such as Wrapper, Facade, Adaptor, Bridge, Proxy and others are extremely useful for breaking large classes down. Rather have 10 smaller classes than 1 large one.
Well here is a very simple start to how to get your refactoring of that large class under way. Please be sure to compile your project after each step, to fix any errors, before moving to the next step.
Step 1
Split the class into two and create a wrapper.
Pick a very large, complex class. Mine is called "Runtime"
Refactor the name of the class. I will call mine "RuntimeExecutor". Add any suffix for the moment, you can always come back to it. (Naming classes becomes harder and harder as you divide classes.)
Add a new class with the "old name". Runtime.
Extract the Interface of ActionExecutor. In VS.NET you simply right-click on the RuntimeExecutor class, and select Refactor | Extract Interface For this exercise, simply select all the members.
A new class file with IRuntimeExecutor interface is created.
Now implement the interface on the Runtime class.
And create an Instance of the RuntimeExecutor from within the constructor of Runtime
It will look something like the code below
//abbreviated version of the class.
public class Runtime : IActionExecutor
{
RuntimeExecutor _Executor; // this is the original class, renamed and now included as variable
public Runtime()
{
_Executor = new RuntimeExecutor(); // we create the instance
}
#region IRuntimeExecutor Members
public void Start()
{
_Executor.Start(); //we call the executor
}
public void Stop()
{
_Executor.Stop(); //we call the executor
}
#endregion
}
Step 2
In the code above, you can see that I have added _Executor.Start() to the Start method. So in effect, Runtime is simple wrapping the Start of RuntimeExecutor. Now the next few steps are harder, but will increase the rewards.
Create a new class suffixed with State. I called mine RuntimeState
Take all the fields and properties for those fields from RuntimeExecutor, and cut/paste into State.
If you only have Fields, refactor them so that you now have Proeprties for those fields
Add a private field for the RuntimeState in RuntimeExecutor and
Modify the contructor of RuntimeExecutor by adding "RuntimeState state" as a new parameter, mapping the parameter to the field.
//new state class
internal class RuntimeState
{
private string _Field;
public string Field
{
get { return _Field; }
set { _Field = value; }
}
}
internal class RuntimeExecutor : IRuntimeExecutor
{
private RuntimeState _State; //new field, replaces all the other fields that were here
public RuntimeExecutor(RuntimeState state) //new param
{
_State = state; //set it and use it
//now wherever it used the old fields, map it to the _State.NewField
_State.Field = "some value";
}
}
So at this point, instead of 1 class, you have three. We refactored the class into two, the original and the State version. And we wrapped it higher up, by renaming it and substituting it for the new wrapper.
RuntimeExecutor (renamed original)
Runtime (new, a wrapper for RuntimeExecutor)
RuntimeState, (new split all properties and fields from RuntimeExecutor)
Step 3
We now need to split the original class further. This time instead of wrapping it, or refactoring UP, we now need to refactor down. Inspect the original class, and locate any methods that can be isolated and split from the class with minimal fuss.
Locate methods that are only looking at RuntimeState only, and move them, cut/paste into the RuntimeState class.
Make these methods internal, and point RuntimeExecutor to _State.MethodName
Locate methods that are utility functions to the class, such as getConfiguration, or IsRunning, and that can be moved to another class. If you have such functions, create a new class for it, and move it to the new location
So now you should have 4 classes from the original 1.
RuntimeExecutor (renamed original)
Runtime (new, a wrapper for RuntimeExecutor)
RuntimeState (now contains state related functions)
Utility class containing helpful functions
Conclusion
I follow this pattern for refactoring large classes that does not present itself clearly as refactorable. It gives me a set way of doing things, which then offers up more refactoring choices as I go along.
For example. When I find a lot of state, perhaps I refactor that too, into even smaller state classes. Importantly though as you go along, you should be on the lookout for common classes. Perhaps another large classes State was extremely similar. I then follow a pattern using inheritance for refactoring, and perhaps break things down even smaller.
In Part 2, I will show you how to utilize the wrapper for even more refactoring loveliness.