Evolution. It is inevitable. Software succumbs to
evolution, like everything else. Almost all software goes through
a process of revisions and changes between the time it was born a wee
little prototype, to its inevitable death. Object oriented
programming is by far, IMO, the easiest code design to deal with when
having to make changes during the life of a software product.
However, just because you might be using OOP, doesn’t mean you have
optimal design. Last week I talked about analyzing code metrics, which play a big part in determining how good, usable and maintainable your design is.
This is where refactoring comes in. Refactoring can be defined
as a process where developers examine existing code and improve the
design of that code by means of modifications. While there is no
specific model for the process of refactoring, there are certainly some
common areas where you see most problems in design, which is where
refactoring takes place.
Refactoring is a very in-depth subject, and I’m only going to skim
the top of it for you beginners out there. I’m not going to get
into test driven development or patterns of software design, but simply
explain 4 common tasks which are encapsulated in refactoring.
Identify methods that can be moved.
This means to look for methods that are encapsulated in the wrong
class. Methods should perform a task relevant to the class in
which they belong. If a method is making frequent calls to
another class, consider moving that method to the other class.
Look at the following example:
Hopefully it is clear that “CalculateAge” is in the wrong class. It is taken a single argument of type “Person” and acting entirely upon that argument. This is a clear case where a method should be moved, in this case from the “VoterRegistration” class to the “Person” class so you have code like below for the “Person” class:
Identify new methods.
Common mistakes amongst developers is to create methods that are too
complex and accomplish too much themselves. This leads to methods
that are hard to build upon, maintain and debug. Cyclomatic complexity is
a common code metric to use to determine if a method is too
complex. Dividing methods into smaller, more easily managed
pieces improves upon simple design and improved clarity.
Repetative code is another clear indicator of where to create new
Take a look at the following code:
Obviously, its staring you right in the face that there is reusable code here. This is where you create a new method.
Many times when you see “Select Case” or “Switch” statements, this
is a strong indicator that the code should be refactored into an
inheritance design. Look at the following code:
The code is pretty clean and simple, but it makes it hard to build
on and add other persontypes into the system. If you have code
like this everywhere, you’d have to go into a lot of different places
in the program and change code to adjust for an added personType.
This is an example of code that should be refactored into
inheritance. The result would be the following code:
Now, to add a new person type, we just add a new class for that person type that inherits from the class “Person” and we won’t have to change any existing code, we only added new code.
Fix your variable names.
What do you think td stands for? You
can probably come up with dozens of different things it could possible
be. What if I told you it stands for todaysDate?
This is a big problem in code. Meaningless variable names.
In Visual Studio, sure Intellisense tells me its a date, but other than
that, what the heck is it for? Give your variables
meaningful names, and not names where you leave out all the vowels
either. tdysDt is not very helpful
either. You are going to save somebody a lot of time and headache
in the future if you use meaningful names. I’ve even seen people
go back to their own code before and have to decifer what the heck td stood for by search back through code.
I have explained 4 common design issues to look for when
beginning your code refactoring. Once you do it a little while,
it becomes easier and you’ll be able to code to avoid these pitfalls,
rather than having to fix them afterwards by refactoring.