Refactoring a God Object

Refactoring scalpel in hand, a gigantic monstrosity stands in front of you; the God Object (or God Class).

Thats right, some developers have decided to create a class with everything in it including the kitchen sink. Or rather, a number of developers over a long period of time, have added layers upon layers of confusion; the internal workings of the class so tightly coupled that it resembles calcification. It’s a horrific incomprehensible mess, and you have to somehow reduce this into object oriented fare, a thing of beauty.

These 2000+ line monsters are some of the most challenging refactoring problems that I have come across. They simply need to be refactored because a simple change to the class requires about 2-3 days understanding how it works. When you have rapidly rotating developers, and lots of quick fixes needed in this class, they quickly become a money sink requiring enormous costs in terms of maintanene. Not to mention, they are extremely un-enjoyable for the developer to work with.

Generally to start off you should make a quick scan of all the methods in the class. Assume you have a class called Blob in front of you :

In this pseudo code example there are a number of methods from a to g. Method a calls methods b and c. Method b calls d, and method c calls no method in the class. You get the picture.

The first goal of refactoring the God Class is to reduce it into a number of different classes, the second goal is attaching some meaning to the classes the refactoring has created.

1. Consider Creating a Utils-Class

Start stripping out methods that are regularly called from multiple methods inside the Blob class. Put the c methods into a utilities method, as public static methods with no class field access. That is they should be purely black box and only deal with parameters coming in.

example :
convertDate(String).. calculateBMI(person)

convert references to class fields to local fields. Sometimes a method you want to strip out references a class variable. Change this class variable into a parameter in the Utils method as such:

becomes

Question: What if there are a large number of class fields being referenced in the method we are extracting into the Utils class? We dont want :

Try to encapsulate the parameters in their own class. Do we really want to pass in an FTP user, FTP password, FTP server, FTP pathname, FTP file, when we could easily just pass in an FTPConfiguration object?

2. Decrease Method Visibility and Harness Unit tests

Once you are dealing with pulling out methods with member level variables, you should start building some unit test scaffolding. The restorers of cathedrals don’t skimp on this, and while we may be dealing with an ungodly mess, I wouldn’t recommend skimping on this either.

Firstly convert any public methods to private methods which are not used by any other class. This small refactoring in itself can add sudden meaning to the God-Class in front of you, especially if you end up with only one public method.

Once you know the true external interface, you can then properly write the unit tests. Make sure you have a number of tests for each method. Incorporate boundary condition tests and exception handling. There are a number of good articles out there which explain how to write unit tests.

3. Look at Candidates to Pull Out into a Second Class

Does the class look like it can be split into 2 or more classes? Ie. is it something like a FTPUploaderReporter, does it have a vague sounding name like *Manager or *Controller?

If the methods do not lend themselves to object oriented design, try and chop up the class in as logical place as possible. Look at removing some methods that will cause the least pain, the least compile errors. Compile errors will happen when a lot of class level variables are used inside methods being pulled out.

Before you start pulling out methods into another class try and sort them within the God-class into logical sections. The main benefit of this, is that you can comment out large sections, and see how many compile errors appear. If sections that are being commented out create little or no compile errors then the sections are not too tightly coupled to the internal wirings of the class. It would be a good idea to move these sections into another class.

4. Aim for Class Cohesion

If the class still cannot be reduced into good sounding objects, try and aim for class cohesion. The utils class that you created before is a very cohesive class. That means that all the methods are similar in many aspects. Static Utils-class methods typically do not interfere with the internal structure of the class, and are provided strictly as a service to create a result based on some inputs.

Other cohesive classes that you may want to refactor out have methods that sound similar.

Eg :

5. Avoid Generalisations Until the End Stages.

Generalisation or inheritence is in the upper echelons of OO thought. Apply this only once you can attach meaning to the classes you see in front of you. Generally in my experience, splitting a God-Class into an inheritance hierarchy without understanding the object-oriented nature of what you are doing is hazardous. Sub methods call super methods which call sub methods which call super methods; an incomprehensible mess. It is almost messier than having everything in one class.

Finally, when you have attached some meaning to the classes in front of you, try and apply inheritance (if needed).

Hopefully over the period of a number of iterations you will have broken down the God-Class into a software engineering solution that is clear, elegant and a thing of beauty.


Posted in Refactoring | Tagged , , , | Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

THREE_COLUMN_PAGE