0% found this document useful (0 votes)
28 views

Paying Attention To Disciplines: Refactoring: Hapter

This document discusses refactoring code to improve its structure and quality without changing its external behavior or functionality. It provides an example of refactoring code for a student report card program that prints grades. The example code is "smelly" and could be improved by extracting methods from the long printReport method, adding getter/setter methods to classes, using more descriptive names, and other changes. Refactoring allows improving code that works but could have a better design. It also allows changing well-written code when requirements change later. The key is being able to refactor code confidently without breaking it through disciplined, step-by-step changes.

Uploaded by

Lucas Vieira
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
28 views

Paying Attention To Disciplines: Refactoring: Hapter

This document discusses refactoring code to improve its structure and quality without changing its external behavior or functionality. It provides an example of refactoring code for a student report card program that prints grades. The example code is "smelly" and could be improved by extracting methods from the long printReport method, adding getter/setter methods to classes, using more descriptive names, and other changes. Refactoring allows improving code that works but could have a better design. It also allows changing well-written code when requirements change later. The key is being able to refactor code confidently without breaking it through disciplined, step-by-step changes.

Uploaded by

Lucas Vieira
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

CHAPTER 11

Paying Attention to Disciplines:


Refactoring

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

Chapter 11 Paying Attention to Disciplines: Refactoring

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.

Refactoring Bad Code

Refactoring Bad Code


Fowler says, Refactoring is the process of changing a software system in
such a way that it does not alter the external behavior of the code yet
improves its internal structure. Just so. The question, of course, is what
is meant by improves. If we do not alter the external behavior of a class
or method, how can we say we have improved it internally? What is the
nature of the improvement?
Lets look at an example.
import java.util.*;
public class ReportCard {
public String studentName;
public ArrayList clines;
public void printReport(){
System.out.println("Report card for " + studentName );
System.out.println("-------------------------------");
System.out.println("Course Title
Grade");
Iterator grades = clines.iterator();
CourseGrade grade;
double avg = 0.0;
while(grades.hasNext()) {
grade = (CourseGrade)grades.next();
System.out.println(grade.title + "
" +
grade.grade);
if(!(grade.grade=='F')) {
avg = avg + grade.grade-64;
}
}
avg = avg / clines.size();
System.out.println("-------------------------------");
System.out.println("Grade Point Average = " + avg);
}
}
class CourseGrade{
public String title;
public char grade;
}

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

Chapter 11 Paying Attention to Disciplines: Refactoring

First, the printReport() method is far too complicatedit does too


much, and it is not particularly readable. Right away, I can see that I
should move some of this code into private methods that work on a more
fine-grained level, and keep printReport() at the specification level
of perspective (see Chapter 7, Paying Attention to Qualities and
Pathologies). This will follow the principle that I want strong cohesion
in my methods.
Also, I do not like that CourseGrade has public members rather than
private members with get() and set() (accessor/mutator) methods to
provide access to them. I also think it should have a constructor to ensure
that I never accidentally get a CourseGrade instance without meaningful state. This helps to ensure that these classes are logically coupled (nothing is coupled to the fact that CourseGrade has these members per se,
only that it has the responsibility of somehow tracking the title of the
course and the grade received).
Similarly, I do not like that studentName and clines are public in
ReportCard. They should be private, with accessor methods too. The
name clines is not terribly descriptive eitherit does not add to the
readability of this code. I know that I want my code to be as readable as
possible.
However, it runs. It works. If I change it to improve it and in the
process damage it to the point that it no longer works, I will have essentially vandalized the code. I am sure to be unpopular for that! I will be
accused of being a purist or being arrogant because I think I know better, and have broken the code as a result.
When I get to the mechanics of refactoring later in this chapter, I will
show how I can improve the odor of this code without fear of harming it.

Refactoring Good Code


Fowlers emphasis is on refactoring code that works but could be implemented in a way that is easier to understand. After refactoring, the code
is clearer in purpose, thus making it easier to change in the future. This
is extremely powerful.
However, the same techniques that allow us to confidently make
changes to poorly written code also allow us to change code that was
well-written, but later needed changing due to a new or changed requirement. Or, sometimes code that seemed fine when I wrote it is revealed to
be naive when I finally gain understanding of the nature of the problem
being solved.

Refactoring Good Code

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

Chapter 11 Paying Attention to Disciplines: Refactoring

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.

Structural Changes Versus Functional Changes


One of the reasons that changing code is so fraught with danger is that our
tendency is to change too much simultaneously. I know that my mind is
capable of multiple modes of thought, but if I try to engage too many of
them at once, chaos ensues.
So, I never bathe the baby while I am cooking the stew. I do not want
to end up with onions in the shampoo.
Similarly, when refactoring code I want to make changes to structure
and changes to function in separate steps. When I am thinking about,
say, adding a new method to my class, I find that this is a different mode
of thought for me than when I consider the code that should be moved
into it, at least in any detailed sense. I will know that I want to move
that loop and stuff, but that is about all the detail I will consider as I think
about naming the method, what its signature should be, and so forth.
Then, once the new method is in place, I can consider how to extricate
the code from its original spot without disturbing anything. My mental discipline will be stronger, and since I am not what-if-ing anymore, I can
focus myself more thoroughly on the care with which I make the change.
Moving in little steps, compiling (and unit testing, of course) between
each change, gives me a lot of confidence. If something goes afoul, it will

Refactoring Helps You Choose Your Battles

almost certainly be the very last little thing I did, which I can easily back
out, and probably just as easily fix.

Refactoring Helps You Choose Your Battles


I have been using, studying, and teaching the refactoring discipline for
quite a while now. At first, the main value it gave me was an increased
sense of confidence when making changes.
It still does that, but now that most of the refactorings are second nature
to me, they help me in a way I never anticipated: They tell me when to
yield to the views of others, and when to stand my ground.
We have all been there. There is a design decision before the team, and
the way you want to solve the problem is different from the majority.
You are pretty sure you are right, but you cannot seem to make any headway with the rest of the group.
Do you give up, or fight it out?
Well, some things are always worth fighting for. Do no harm is essential to an evolutionary process, so if I am sure we are heading for decay
by doing whatever it is the rest of the team wants to do, I am gonna strap
on the six-guns and hold my ground.
But, on the other hand, some things are relative, and subtle. So, when
I find myself in a situation that is not so clear, I find it very helpful to ask
the refactoring question:
If I do it their way, and later I am proved right, what is the refactoring
we will need to perform to get from their design to mine?
The refactoring moves are not all equally difficult, and in knowing
them well I also know which ones are expensive to accomplish, in terms
of time and effort. Move Method, for instance, is easier than Extract
Method. Replace Conditional with Polymorphism can be a bear, but Pull
Up Method is not so bad. Also, some of the refactorings can be automated
more easily than others, and the tools we use reflect this.
So, when I cannot convince my colleagues of the wisdom of my position, I ask myself the refactoring question. If the answer is that the refactoring will be simple, I yield. If it is going to be a toughie, however, then
its go time!
This also means I fight fewer battles, which I have found increases my
chance of winning the ones that remain.

219

220

Chapter 11 Paying Attention to Disciplines: Refactoring

Patterns Can Be Targets of Refactoring


Some of the refactoring moves are, in fact, named for the patterns they
result in. Form Template Method and Replace Type Code with State/Strategy
are good examples.
Patterns represent examples of good design, and refactoring is about
changing an existing design into a better one. If we did not find some
confluence between patterns and refactoring, I would have to suspect
that one or the other was misbegotten.
However, even the refactorings that are not specifically attached to patterns can be used to find them in an existing design. In his splendid book,
Refactoring to Patterns, Josh Kerievsky outlines this nicely. It is a worthwhile read in my opinion.
Later in this book, we are going to examine how designs can evolve,
and how refactoring, in all of its forms, contributes to this process.

Avoiding Refactoring: Prefactoring


Knowing how to refactor is extremely helpful, and learning it as a discipline is especially important in an evolutionary process. However, it does
take time and is not always what I would call fun.
Where I can avoid it, I do.
For instance, in Chapter 9, Paying Attention to Practices, I outlined
the practice of programming by intention. If you examine the results of
the refactoring Extract Method (demonstrated later in this chapter), it
might occur to you that the code would have already been in the refactored form if the programmer had simply followed this practice in the
first place.
Precisely.
You might also note that the practice of encapsulating constructors
means that changes will be very limited when polymorphism shows up
late in a design, and that therefore less refactoring will be needed when
that happens.
Indeed.
Furthermore, you might suggest that refactoring moves like Form
Template Method would not be needed if the Template Method was
noticed in the first place, if one was designing with the patterns in mind
all along.

The Mechanics of Refactoring

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.

The Mechanics of Refactoring


I will end this chapter by demonstrating a bit of refactoring. If you have
done Extract Method and Split Loop before, you probably do not need to
read this section. However, if you find my prose too scintillating to resist,
read on anyway.
In Fowlers book, he provides a catalog of refactorings (named for
what they do), and for each refactoring he provides a list of mechanics.
These are the discrete steps you should follow in performing the given
refactoring.
One of the more common refactorings is called Extract Method. I find
that I often encounter methods that are not as cohesive as I would like,
and thus I end up breaking them into lots of smaller methods, which is
what Extract Method helps me to do safely.
In Refactoring, Fowler gives us the following mechanics for Extract
Method:
Create a new method, and name it after the intention of the method
(name it by what it does, not by how it does it).
If the code you want to extract is very simple, such as a single
message or function call, you should extract it if the name of the
new method will reveal the intention of the code in a better way.
If you cannot come up with a more meaningful name, do not
extract the code.

221

222

Chapter 11 Paying Attention to Disciplines: Refactoring

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,

You might also like