Refactoring and Code Smell
Refactoring and Code Smell
Code Smells
If it Stinks, change it!
65
What is Refactoring?
67
What is Refactoring?
67
What is Refactoring?
67
What if you hear...
68
What if you hear...
68
What if you hear...
68
What if you hear...
68
What if you hear...
68
What if you hear...
68
What if you hear...
68
What if you hear...
68
What if you hear...
68
What if you hear...
68
What if you hear...
68
Why do we Refactor?
70
Why do we Refactor?
70
Why do we Refactor?
More flexibility
Increased re-usability
70
Why do we Refactor?...
71
Why do we Refactor?...
71
Why do we Refactor?...
71
Why do we Refactor?...
71
Why do we Refactor?...
71
Why do we Refactor?...
71
Readability
72
When should you refactor?
73
When should you refactor?
73
When should you refactor?
To find bugs
refactor to understand the code
73
When should you refactor?
To find bugs
refactor to understand the code
73
When should you refactor?
Like championship
refactor the design to make it simple to add
setting
refactor to understand the codeourselves up for
73
The Two Hats
Adding Function
Refactoring
Add new capabilities to the Does not add any new features
system
Does not add tests (but may change some)
Adds new tests Restructure the code to remove
Get the test working redundancy
74
How do we Refactor?
Things that we suspect are not quite right or will cause us severe pain if we do
not fix
77
2 Piece of Advice before Refactoring
78
2 Piece of Advice before Refactoring
Baby Steps
78
2 Piece of Advice before Refactoring
78
Code Smells?
79
A short history of Code Smells
80
Common Code Smells
Inappropriate Naming Long Method
Comments Long Parameter List
Dead Code Switch Statements
Duplicated code
Speculative Generality
Primitive Obsession
Oddball Solution
Large Class
Feature Envy
Lazy Class
Refused Bequest
Alternative Class with
Black Sheep
Different Interface
Train Wreck 81
Inappropriate Naming
82
Inappropriate Naming
Names given to variables (fields) and methods should be clear and
meaningful.
82
Inappropriate Naming
Names given to variables (fields) and methods should be clear and
meaningful.
A variable name should say exactly what it is.
Which is better?
private string s; OR private string salary;
82
Inappropriate Naming
Names given to variables (fields) and methods should be clear and
meaningful.
A variable name should say exactly what it is.
Which is better?
private string s; OR private string salary;
A method should say exactly what it does.
Which is better?
public double calc (double s)
public double calculateFederalTaxes (double salary)
82
Comments
Comments represent a failure to express an idea in the code . Try to make your code
self-documenting or intention-revealing
When you feel like writing a comment, first try "to refactor so that the
comment becomes superfluous.
Remedies:
Extract Method
Rename Method
Introduce Assertion
83
Comment: “Grow the Array” smells
public class MyList
{ void AddToList(string element)
int INITIAL_CAPACITY = 10; {
bool m_readOnly; if (!m_readOnly)
int m_size = 0; {
int m_capacity; int newSize = m_size + 1;
string[] m_elements; if (newSize > GetCapacity())
{
public MyList() // grow the array
{ m_capacity += INITIAL_CAPACITY;
m_elements = new string[INITIAL_CAPACITY]; string[] elements2 = new string[m_capacity];
m_capacity = INITIAL_CAPACITY; for (int i = 0; i < m_size; i++)
} elements2[i] = m_elements[i];
84
Comment Smells Make-over
void AddToList(string element) private void Grow()
{ {
if (m_readOnly) m_capacity += INITIAL_CAPACITY;
return; string[] elements2 = new string[m_capacity];
if (ShouldGrow()) for (int i = 0; i < m_size; i++)
{ elements2[i] = m_elements[i];
Grow();
} m_elements = elements2;
StoreElement(element); }
}
private void StoreElement(string element)
{
m_elements[m_size++] = element;
}
private bool ShouldGrow()
{
return (m_size + 1) > GetCapacity();
}
85
Smell: Comments
Rename Method
86
Smell: Comments
Extract Method
87
Smell: Comments
Extract Method
void PrintOwning(double amount){
PrintBanner();
// print details
System.Console.Out.WriteLine(“name: “+ name);
System.Console.Out.WriteLine(“amount: “+
} amount);
87
Smell: Comments
Extract Method
void PrintOwning(double amount){
PrintBanner();
// print details
System.Console.Out.WriteLine(“name: “+
System.Console.Out.WriteLine(“amount: “+ amount);
name);
}
Introduce Assertion
88
Smell: Comments
Introduce Assertion
double getExpenseLimit() {
// should have either expense limit or a primary project
return (_expenseLimit != NULL_EXPENSE) ? _expenseLimit :
_primaryProject.GetMemberExpenseLimit();
}
88
Smell: Comments
Introduce Assertion
double getExpenseLimit() {
// should have either expense limit or a primary project
return (_expenseLimit != NULL_EXPENSE) ? _expenseLimit :
} _primaryProject.GetMemberExpenseLimit();
double getExpenseLimit() {
Assert(_expenseLimit != NULL_EXPENSE || _primaryProject != null,
“Both Expense Limit and Primary Project must not be null”);
88
Long Method
89
Long Method
A method is long when it is too hard to quickly comprehend.
89
Long Method
A method is long when it is too hard to quickly comprehend.
Long methods tend to hide behavior that ought to be shared, which leads to
duplicated code in other methods or classes.
89
Long Method
A method is long when it is too hard to quickly comprehend.
Long methods tend to hide behavior that ought to be shared, which leads to
duplicated code in other methods or classes.
89
Long Method
A method is long when it is too hard to quickly comprehend.
Long methods tend to hide behavior that ought to be shared, which leads to
duplicated code in other methods or classes.
Extract Method
Decompose Conditional
89
Long Method Example
private String toStringHelper(StringBuffer result)
{
result.append("<");
result.append(name);
result.append(attributes.toString());
result.append(">");
if (!value.equals(""))
result.append(value);
Iterator it = children().iterator();
while (it.hasNext())
{
TagNode node = (TagNode)it.next();
node.toStringHelper(result);
}
result.append("</");
result.append(name);
result.append(">");
return result.toString();
}
90
Long Method Makeover (Extract Method)
private String toStringHelper(StringBuffer result)
{ private void writeValueTo(StringBuffer result)
writeOpenTagTo(result); {
writeValueTo(result); if (!value.equals(""))
writeChildrenTo(result); result.append(value);
writeEndTagTo(result); }
return result.toString();
} private void writeChildrenTo(StringBuffer result)
{
private void writeOpenTagTo(StringBuffer result) Iterator it = children().iterator();
{ while (it.hasNext())
result.append("<"); {
result.append(name); TagNode node = (TagNode)it.next();
result.append(attributes.toString()); node.toStringHelper(result);
result.append(">"); }
} }
private void writeEndTagTo(StringBuffer result)
{
result.append("</");
result.append(name);
result.append(">");
}
91
Smell: Long Method
92
Smell: Long Method
92
Smell: Long Method
94
Smell: Long Method
95
Smell: Long Method
withinPlan = plan.withinRange(daysTempRange());
95
Smell: Long Method
96
Smell: Long Method
double primaryBasePrice;
double secondaryBasePrice;
double tertiaryBasePrice;
// long computation;
...
}
96
Smell: Long Method
Decompose Conditional
You have a complicated conditional (if-then-else) statement.
Extract methods from the condition, then part, and else parts.
if (date.before (SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;
if (notSummer(date))
charge = winterCharge(quantity);
else charge = summerCharge (quantity);
97
Example of Conditional Complexity
98
Long Parameter List
Methods that take too many parameters produce client code that is
awkward and difficult to work with.
Remedies:
Introduce Parameter Object
174
Example
private void createUserInGroup() {
GroupManager groupManager = new GroupManager();
Group group = groupManager.create(TEST_GROUP, false,
GroupProfile.UNLIMITED_LICENSES, "",
GroupProfile.ONE_YEAR, null);
user = userManager.create(USER_NAME, group, USER_NAME, "jack",
USER_NAME, LANGUAGE, false, false, new Date(),
"blah", new Date());
}
175
Smell: Long Parameter List
176
Smell: Long Parameter List
176
Smell: Long Parameter List
Customer
AmoutInvoicedIn(DateRange range)
AmoutRecivedIn(DateRange range)
AmoutOverdueIn(DateRange range)
176
Smell: Long Parameter List
177
Smell: Long Parameter List
withinPlan = plan.withinRange(daysTempRange());
Feature Envy
A method that seems more interested in some other class than the one it is in.
Data and behavior that acts on that data belong together. When a method
makes too many calls to other classes to obtain data or functionality, Feature
Envy is in the air.
Remedies:
Move Field
Move Method
Extract Method
178
Example
Public class CapitalStrategy{
double capital(Loan loan)
{
if (loan.getExpiry() == NO_DATE && loan.getMaturity() != NO_DATE)
return loan.getCommitmentAmount() * loan.duration() * loan.riskFactor();
return 0.0;
}
} 179
Smell: Feature Envy
Move Field
180
Smell: Feature Envy
Move Method
181
Dead Code
Accidental Changes.
Remedies
114
Dead Code Example
A Loan class contains five constructors, three of which are shown below:
public Loan(double commitment, double outstanding, int customerRating, Date maturity, Date expiry) { this(null,
commitment, outstanding, customerRating, maturity, expiry); }
public Loan(CapitalStrategy capitalStrategy, double commitment, int riskRating, Date maturity, Date expiry)
{ this(capitalStrategy, commitment, 0.00, riskRating, maturity, expiry); } ... }
115
Duplicated Code
The most pervasive and pungent smell in software
There is obvious or blatant duplication
Remedies
Extract Method
Pull Up Field
123
Example Of Obvious Duplication
124
126
Levels of Duplication
127
Literal Duplication
128
Semantic Duplication
for(int i : asList(1,3,5,10,15))
stack.push(i);
129
Data Duplication
130
Conceptual Duplication
132
Logical Steps - Duplication
134
Smell: Duplicate Code
Pull Up Field
136
Smell: Duplicate Code
137
Smell: Duplicate Code
Substitute Algorithm
138
Smell: Duplicate Code
Substitute Algorithm
String foundPerson(String[] people){
for (int i = 0; i < people.length; i++) {
if (people[i].equals ("Don")){
return "Don";
}
if (people[i].equals ("John")){
return "John";
}
if (people[i].equals ("Kent")){
return "Kent";
}
}
return ""; }
138
Smell: Duplicate Code
Substitute Algorithm
String foundPerson(String[] people){
138
Speculative Generality
You get this smell when people say "Oh, I think we will need the ability to do that
someday" and thus want all sorts of hooks and special cases to handle things that
aren't required.
This odor exists when you have generic or abstract code that isn’t actually
needed today. Such code often exists to support future behavior, which may or may
not be necessary in the future.
Remedies
Collapse Hierarchy
Inline Class
Remove Parameter
105
Smell: Speculative Generality
Collapse Hierarchy
Salesman
104
Smell: Speculative Generality
Inline Class
109
Smell: Speculative Generality
Remove Parameter
110
Lazy Class
A class that isn't doing enough to carry its weight
We let the class die with dignity
Often this might be a class that used to pay its way but has been downsized
with refactoring. Or it might be a class that was added because of changes that
were planned but not made.
Remedies
Inline Class
Collapse Hierarchy
99
Lazy Clazz Example
public interface SomeInterface {
void methodOne();
void defaultMethod();
}
public abstract class LazyClazz implements SomeInterface {
public abstract void methodOne();
public void defaultMethod() {
//do nothing
}
}
public class WorkerClazz extends LazyClazz {
public void methodOne() {
// some actual code here
}
public void defaultMethod() {
//some more actual code
}
}
100
Another Lazy Class
public class Letter {
private final String content;
Inline Class
103
Smell: Lazy Class
Collapse Hierarchy
Salesman
104
Refused Bequest
This rather potent odor results when subclasses inherit code that they don’t want.
In some cases, a subclass may “refuse the bequest” by providing a do-nothing
implementation of an inherited method.
Remedies
Push Down Field
116
Smell: Refused Bequest
Salesman
104
Smell: Refused Bequest
104
Black Sheep
A method in a class that is noticeably different from other methods in the class.
118
Example
119
Primitive Obsession
This smell exists when primitives, such as strings, doubles, arrays or low-level
language components, are used for high-level operations instead of using classes.
This typically occurs when you haven’t yet seen how a higher-level
abstraction can clarify or simplify your code.
Remedies
Extract Class
160
Primitive Obsession Example
if (someString.indexOf("substring") != -1)
if(someString.contains("substring"))
161
Primitive Obsession Example
private void Grow() {
Object[] newElements = new Object[elements.length + 10];
for (int i = 0; i < size; i++)
newElements[i] = elements[i];
elements = newElements;
}
161
Primitive Obsession Example
public class CompositeShape
{
IShape [] arr = new IShape[100];
int count = 0;
163
Smell: Primitive Obsession
167
Smell: Primitive Obsession
167
Oddball Solution
When a problem is solved one way throughout a system and the same problem is
solved another way in the same system, one of the solutions is the oddball or
inconsistent solution. The presence of this smell usually indicates subtly duplicated
code.
168
Oddball Solution Example
string LoadUserProfileAction::process()
{
//some code here
return process("ViewAction");
}
string UploadAction::process() {
//some code here
return process("ViewAction");
}
string ShowLoginAction::process() {
//some other code here
Action* viewAction = actionProcessor().get("ViewAction");
return viewAction->process();
}
169
Oddball Solution Example
private void grow() {
Object[] newElements = new Object[elements.length + 10];
for (int i = 0; i < size; i++)
newElements[i] = elements[i];
elements = newElements;
}
170
Smell: Odd Ball Solution
Substitute Algorithm
String foundPerson(String[] people){
for (int i = 0; i < people.length; i++) {
if (people[i].equals ("Don")){
return "Don";
}
if ("John".equals (people[i])){
return "John";
}
if (people[i].equals ("Kent")){
return "Kent";
}
}
return ""; }
171
Smell: Odd Ball Solution
Substitute Algorithm
String foundPerson(String[] people){
for (int i = 0; i < people.length; i++) {
if (people[i].equals ("Don")){
return "Don";
}
if ("John".equals (people[i])){
return "John";
}
if (people[i].equals ("Kent")){
}
return "Kent";
}
return ""; } String foundPerson(String[] people){ List candidates =
Arrays.asList(new String[] {"Don", "John", "Kent"});
for (String person : people)
if (candidates.contains(person))
return person;
return ""; }
171
Large Class
Like people, classes suffer when they take on too many responsibilities.
GOD Objects
Fowler and Beck note that the presence of too many instance variables usually
indicates that a class is trying to do too much. In general, large classes typically
contain too many responsibilities.
Remedies
Extract Class
152
153
153
Smell: Large Class
Extract Class
154
Smell: Large Class
155
Smell: Large Class
156
Smell: Large Class
157
Smell: Large Class
158
Switch Statement
This smell exists when the same switch statement (or “if…else if…else if”
statement) is duplicated across a system.
139
Switch Smell Examples
140
More Switch Smell Examples
142
Evil Switch Example
143
Smell: Switch Smell
144
Smell: Switch Smell
145
Smell: Switch Smell
void setHeight(int h) {
this.height = h;
}
void setWidth (int w) {
this.width = w;
}
145
Smell: Switch Smell
146
Smell: Switch Smell
// In client class
Customer customer = site.getCustomer();
// In Null Customer
There is a natural relation between patterns and refactorings. Patterns are where
you want to be; refactorings are ways to get there from somewhere else. - Martin
Fowler
182
Reference Reading
185
Further Information On
Code Smells and Refactoring
186
References
187