Paying Attention To Disciplines: Refactoring: Hapter
Paying Attention To Disciplines: Refactoring: Hapter
efactoring is not a new idea. I have been doing it, in one sense or
another, for my entire career as a software developer, with neither
a name for it nor any systematic way of describing it. I am doing refactoring whenever I am changing my code because I do not like how it is
written, for whatever reason that is. I suspect this is true for you, too.
I have been doing refactoring in the chapters that precede this one
when I changed an example to make it more cohesive, or to decouple it,
or to remove redundancy, or to make the code more readable. These were
all refactorings because I was changing the code to make it better code,
rather than to change its behavior.
In the year 2000, Martin Fowler wrote a very important book called
Refactoring: Improving the Design of Existing Code, in which he took the process
we had all been doing (with various degrees of efficiency and effectiveness) and gave it a structure and a set of rules to guide us. This turns out
to be very important, because it is the discipline of refactoring that helps
us to reduce the fear we normally feel when we make changes to code that
currently seems to work properly.
You know the feeling. You are working on your code and you see a
method or an entire class that is poorly designed, and you know that it
really ought to be changed, but you are afraid that
You will break it
You will break something else that uses it
Either way, it will be your fault
213
214
Emergent design is all about changing code, due to the need to improve
it (in terms of, chant with me, coupling, cohesion, redundancy, readability, testability), because the requirements of the project have become
clearer to me or have changed, or as part of the process of up-front testing. In other words, if I am going to work in an evolutionary way, I am
going to be changing my code more than I have in the past, and so I
need something in my process to make change less worrisome and more
reliable.
But refactoring is more than this. I want to recast change from the traditional role it has played in software developmentan opportunity to fail,
a dangerto something more positive. I want to make change my ally, to
alter the equation of change such that it becomes an opportunity to succeed, and a chance to improve my code.
I need to be able to make changes confidently, so I can concentrate on
the goals I have in making the change, and less on the process of change
itself. This concentration, combined with a good understanding of patterns, will give me the mental freedom and power to recognize the designs
that are emerging from my process, and capitalize on the opportunities
they represent.
For many years, I accepted the belief that code decays over time, that
this is an inevitable reality in software development. I do not want to
accept that any more. I want to see every change I make as a step along
the way toward correctness and increased value, a moment of evolution
in the lifecycle of my project. Typically, the need to make a change is
motivated by one of two things:
A bug has been found, or the system fails to meet a requirement
A new requirement (or a better understanding of an existing
requirement) has emerged
Either way, the motivation represents a pressurizing force that stresses
the existing design, and in making a change to accommodate this force,
I am not only improving the quality of the code, but also making the system more appropriate to the needs it is intended to address. In other
words, each time I make a change because of a business need, the change
tends to align the software with the real ways it will be used.
Thus, change can become an opportunity instead of a danger, if I can
find a way to approach it with confidence and aggression.
This is what refactoring will do for me.
There are a lot of things I do not like about this. Fowler says that my
sense of smell is at work here. This code is smelly right off, and it is
pretty easy to determine the source of the odor if I examine it carefully.
215
216
In other words, my code may be strong cohesive, intentionally coupled, free of redundancies, and clear as a bell, but still prove to be inadequate to address the problem domain sufficiently.
This can easily happen if I am trying to avoid waste in my design, for
instance, if I make the CourseGrade in the code example above an interface instead of a class.
public interface CourseGrade {
public String getTitle();
public char getGrade();
}
public class BasicCourseGrade implements CourseGrade {
// Implementation here
}
I could easily argue that this is decoupled to a greater degree than the
previous design, even if I refactored the original code to use accessor methods. This is because ReportCard would not even have identity coupling
to BasicCourseGrade, and I would be free to add other specialized implementations to the systemProbationaryCourseGrade, for instance
without having to modify ReportCard.
But if my requirements state that there are CourseGrades and only
CourseGrades, then using an interface at this point is wasteful of my
time, wasteful of the process by which this code is stored, version controlled, and so forth, and creates more complexity than is required to
solve the problem.
And requirements, of course, never change.
(I will let you mop up your coffee and dry your tears of laughter before
I proceed.)
Yes, of course, the problem is that the very same domain expert who
was absolutely certain there was only one kind of course grade may very
well come back later and change his tune. This is because
He may not understand the implications, in the software development sense, of the word never. Software development is more exacting than many other professions, requiring more precise language
than most people are accustomed to using.
You may not have asked the question you meant to ask, given your
limited understanding of his problem domain and its traditions.
217
218
You may have misunderstood his answer when you asked the question in the first place.
Things may have changed since his first answer.
So, we can do the following:
Complain and commiserate about the fact that requirements always
seem to be in flux.
Over-design everything to allow for any possible change we can
imagine.
Assume that this is a reality of the business we are in and prepare
ourselves to deal with it.
We are back to the overall nature of our profession again. We do not
plan, do, test, and ship; we code what we know and expect it will evolve.
almost certainly be the very last little thing I did, which I can easily back
out, and probably just as easily fix.
219
220
Agreed.
Do we need to know the refactoring moves after all? No, unless
You are imperfect
You have to work with other peoples code
You are not writing your very first piece of code tomorrow
You never have a changing requirement that totally throws your
design a curve
In other words, yes.
221
222
Copy the extracted code from the source method into the new target method.
Scan the extracted code for references to any variables that are local
in scope to the source method. These are local variables and parameters to the method.
See whether any temporary variables are used only in this extracted
code. If so, declare them in the target method as temporary variables.
Look to see if any of these local-scope variables are modified by the
extracted code. If one variable is modified, see whether you can
treat the extracted code as a query and assign the result to the variable concerned. If this is awkward, or if there is more than one such
variable, you cannot extract the method as it stands. You may need
to use Split Temporary Variable and try again. You can eliminate temporary variables with Replace Temp with Query (see the discussion in
the examples).
Pass into the target method as parameters local-scope variables that
are read from the extracted code.
Compile when you have dealt with all the locally scoped variables.
Replace the extracted code in the source method with a call to the
target method.
If you have moved any temporary variables over to the target
method, look to see whether they were declared outside of the
extracted code. If so, you can now remove the declaration.
Compile and test.
For each refactoring move, Fowler is giving me steps to follow that are
discrete To Dos and that also encapsulate the wisdom he and his coauthors have gleaned from their experiences and investigations. One of
the values, for me, of Fowlers book is that it is not just a cookbook
of solutions, but also a detailed investigation of the principles behind
those solutions.
Refactoring is at your elbow at all times when you are evolving a design.
To show this in action, I will try improving the smelly code I created earlier in the chapter by using Extract Method. This is just a brief example,