Refactorings
2. Software Evolution Ideas
-
Software is never finished.
-
Software maintenance is just continuous development.
-
Consequence: code becomes complex and brittle
3. Why?
-
Original design is always inadequate.
-
Even good designers cannot:
-
get right the first time.
-
predict how the software will evolve.
-
understand correctly the problem domain and user requirements.
-
4. Software Evolution Basis
— Grow, don’t build software.
Evolutionary Software Development
9. Software Refactoring Definition
A program transformation that preserves the visible behavior.
The activity of improving the design of a software source code.
10. Simple example
Class rename
public class Stuff {
// (...)
}
public class ConfigurationManager {
// (...)
}
11. Another Example
Insert intermediate class
public class Root {}
public class ConcreteA extends Root {}
public class ConcreteB extends Root {}
public class Root {}
public class IntermediateClass extends Root {}
public class ConcreteA extends IntermediateClass {}
public class ConcreteB extends IntermediateClass {}
12. A More Complex Example
13. Origins
-
Software maintenance and evolution
-
Software application development
-
Software framework development
14. Motivation
-
The eternal quest for code uniqueness
-
The code is read and modified more often than it is written
15. Difficulties
-
Understand an existing design is hard
-
Modify an existing design is even harder
-
Code changes may introduce errors, defeating the propose.
-
Results are not visible.
16. Difficulties (Cont.)
-
Every software project is under time pressure.
-
Developers are paid to add new features.
-
Refactoring can be very expensive.
17. Code Evolution Without Refactoring
-
Design becomes more corrupt and code becomes more brittle.
-
Changes become more expensive and more frequent and are made quickly and poorly.
18. Refactoring During Software Construction
-
Gradually change the code to get a healthy design.
-
Design is done continuously.
-
By building the system, we discover how to improve it.
20. Refactoring Operations
-
Simple source-to-source transformation with no visual effect.
-
Since refactoring operations are behavior-preserving, they can be composed and still preserve behavior.
-
Learning refactoring operations is similar to learning simple arithmetics.
21. Operation Preconditions
-
Each refactoring operation has a set of preconditions
-
That is, the conditions that one should respect to perform the transformation
-
The precondition to the operation remove attribute is that the attribute is not used.
22. Basic Operations
-
Add entity
-
Remove entity
-
Rename entity
-
Move entity
-
Intra-method
-
Composite operations
23. Add Entity
-
Add an attribute (instance or class level)
-
Add a class
-
Add a method (instance or class level)
24. Remove Entity
-
Remove an attribute (instance or class level)
-
Remove a class
-
Remove a method (instance or class level)
public class Example {
private String name;
public neverUsedMethod() {
doNothing();
}
}
25. Rename Entity
-
Rename a variable
-
Rename an attribute (instance or class level)
-
Rename a class
-
Rename a method (instance or class level)
-
Change a method signature:
-
rename, permute, add, or remove arguments.
-
public class Example {
private String maeby;
public myMethod() {
String maybe;
this.call(maeby, me);
}
public call(String one, String other) {
doSomething();
}
}
26. Push-down (Specialization) or Pull-up (Generalization) property
-
Attribute push-down or pull-up (instance or class level).
-
Method push-down or pull-up (instance or class level).
28. Intra-Method
-
Extract code as method
-
Extract code as temporary variable
-
Inline method
-
Inline temporary variable
int increment(int i) {
return i+1;
}
void m() {
int a;
a = increment(a);
}
void m() {
int a;
a = a + 1;
}
29. Composite Operations
-
Encapsulate attribute
-
Make attribute read-only
-
Extract interface from class
-
Extract inner class
-
Create template method
class A {
public int code;
}
class A {
private int code;
public int getCode() {return code;}
public void setCode(int i) {code = i;}
}
30. Other Operations
-
Change entity visibility
-
Introduce factory method
-
Convert variable to attribute
class A {
public A(int i, String s, float f) {}
}
class A {
private A(int i, String s, float f) {}
public static createA(int i, String s, float f) {
return new A(i, s, f);
}
}
32. When to Refactor
-
Extend and then refactor
-
Refactor to extend
-
Debug and then refactor
-
Refactor to debug
-
Refactor to understand
33. Extend and then Refactor
-
Find a class or method with similar behavior and copy it
-
Make it work
-
Eliminate redundancy
34. Refactor to Extend
-
Refactor the current design to make the change easy
-
Make the change
35. Debug and then Refactor
-
Locate and fix the bug
-
Add assertions
-
Extract method
-
Assign meaningful names
-
Create explaining constants for magic numbers
-
Create explaining constants or variables for complex expressions
36. Refactor to Debug
-
Before debugging, refactor to simplify complex code
-
Then, debug it
37. Refactor to Understand
-
Split large methods
-
Create explaining constants or variables for magic numbers
-
Assign meaningful names
-
Do not worry about performance
38. Refactor for Extensibility
-
Refactor code before code extension
-
Separate things that change from things that do not.
-
Apply design patterns.
39. Design Patterns
| Variability point | Design Pattern |
|---|---|
Algorithms |
Strategy, Visitor |
Actions |
Command |
Implementation |
Bridge |
Response to change |
Observer |
Interactions between objects |
Mediator |
40. Design patterns (Cont.)
| Variability point | Design Pattern |
|---|---|
Object being created |
Factory Method, Abstract Factory, Prototype |
Structure being created |
Builder |
Traversal Algorithm |
Iterator |
Object Interfaces |
Adapter |
Object Behavior |
Decorator, State |
41. Example: Introduce Algorithm Variability
public class Client {
public void writeAsciiOn(OutputStream o) {
o.print("name: ");
o.print(this.name);
(...)}
}
Suppose that we want to print in HTML, XML, JSON, etc.
42. Steps
-
Create
AsciiStrategyclass -
Add instance attribute to class
Clientand initialize it toAsciiStrategy -
Move method
writeAsciiOn()to classAsciiStrategy -
Rename method
writeAsciiOn()towriteOn()
43. Result
public class Client {
private WriteStrategy writeStrategy = new AsciiStrategy();
public void writeOn(OutputStream o) {
writeStrategy.writeOn(this, o)}
}
public class AsciiStrategy {
public void writeOn(Client c, OutputStream o)
o.print("name: ");
o.print(c.name);
(...)}
}
44. Consequences
-
The printing behavior was extracted from the class
Client. -
Adding new printing behavior could be easily achieved.
45. Refactor to Improve Code Maintainability
-
Use Code Smells to find where the code should be improved
46. Code Smells
-
Long Method
-
Large Class
-
Long Parameter List
-
Nested Conditionals
-
Parallel Inheritance Hierarchies
-
Duplicated Code
-
Speculative Generality
47. Long Method
-
Extract code snippets as smaller methods:
-
If an entire method is long and low-level, find the sequence of higher-level steps.
-
Comments in the middle of a method often point out good places to extract.
-
-
Smaller methods can often be reused
48. Large Class
-
Create compositions of smaller classes
-
Find logical sub-components of the original class and create classes to represent them
-
Move methods and attributes into the new components
-
Related refactoring operations: Extract Class, Extract Subclass
49. Long Parameter List
-
Create a class containing all interrelated parameters.
-
Use this class as a parameter
-
Find methods that should be in the new class
50. Nested Conditionals
-
If the conditional expression involves type test (
isKindOf(),type(),getClass(), etc.), put the method on that class. -
If the expression involves null objects (
isEmpty(),isNull(),null,empty()), consider the Null Object pattern.
51. Parallel Inheritance Hierarchies
-
Use Move Method and Move Attribute to combine the hierarchies into one.
52. Duplicated Code
-
Push-up identical methods to common superclass
-
Push-up the more general method
-
Move the method into a common component (e.g., Strategy)
-
Related refactoring operations: Extract Method, PullUp Method, Form Template Method
53. Speculative Generality
-
Remove unnecessary delegation with Inline Class
-
Remove (almost) empty abstract classes
-
Remove unused parameters
-
Methods named with odd abstract names should be brought down to earth with Rename Method
55. The Loan Metaphor
-
Quick and dirty coding is like taking out a loan
-
Living with bad code is the interest
-
Debt is necessary for a business
-
Too much debt is not healthy and will eventually catch up to you
56. Refactoring Phase
-
Little more breathing room
-
The design is still fresh in your mind
58. Agile Software Development (Cont.)
-
Some of the principles still apply, even if you’re not extreme
-
Whenever something seems difficult or awkward, refactor to make it easy
-
Let the program tell you where it needs to be fixed
-
If you cut and paste, you must refactor
60. Using Standard Tools
-
Safe refactoring needs tests
-
Tests must pass before and after each refactoring operation.
-
Use standard testing tools: JUnit, TestNG, etc.
-
Current Java IDE provide refactoring operations: Eclipse, Netbeans, IntelliJ IDEA
61. Refactoring Tools
- Smalltalk
-
Refactoring Browser, Lint.
- Python
-
Bicycle Repair Man, pycheck.
- Java
-
Eclipse, IntelliJ Idea, JFactor, XRefactory, JBuilder, RefactorIt, JRefactory, Transmogrify, JafaRefactor, CodeGuide, jLint.
- C++
-
SlickEdit, Ref++, Xrefactory.
- Ruby
-
Ruby Refactoring
63. Conclusion
-
Evolutionary Software Development
-
Refactoring operations
-
Ways of integrating refactoring into your process
64. References
-
PhD Thesis from William Opdyke, Don Roberts, and John Brant.
-
Martin Fowler’s book and website