Refactoring - Arizona State University

Download Report

Transcript Refactoring - Arizona State University

Refactoring
Agile Coding Principles
Collective Code Ownership in practice:
 We cannot check in our code until:
•
•
•
•
All tests are green.
All duplication has been removed
The code is as expressive as we can make it.
The code is as simple as we can make it.
 Note: nothing in here says you wrote the original code!
The “code is the documentation”
 Comments should be minimized
• They often lie.
• They are an admission that we could not make our code express
our ideas.
• There are many things you can do to make software expressive.
Refactoring
"No matter how far you have gone on the wrong road, turn back.”
-- Turkish proverb
• Refactoring is the act of rewriting existing code to
make it simpler and more efficient.
• XP refactorsmercilessly.
• Refactoring is aided by relentlesstesting and
continuous integration.
• Refactoring takes courage!
• Refactoring aids in upgrading the design.
 A goal of refactoring is to move toward a design pattern
• If design is good, then everybody will do it everyday!
Adapted from presentation by J.Turpin & J.Morris, 2002
Code Smells
“If it stinks, change it.”
Types of smells:
• Within classes: Smells within the structure and
responsibilities of a single class
• Between classes: Smells arising from the
structure and collaborations of many classes
Examples:
Duplicate code
Switch statements
Long method
Large class
Data class (“data bag”)
Long parameter list
Primitive obsession
Temporary field
etc.
Adapted from presentation by W..Wake, 2000
Smells Within Classes
Comments
 typically don’t need inline comments
 if comment describes a block of code, make it a
method
 Comment should not be more descriptive than
method name - rename
Long Method
 Extract blocks into different methods
• produce a unique result
• callable from multiple locations
• someone else (client) might be interested in intermediate
value
 Just plain clean up the code
 But, maybe we just need this really long method
Smells Within Classes
Large Class
 Keep adding one more thing to the class
 Cohesion measure should be elevated if the class bloat
obfuscates a singular responsibility.
 Extract a new class - find features that reflect unique
concept and delegate responsibility to the new class
 Example: javax.swing.JTable - extract Column class
(CRUD, margin) Cell class
Long Parameter List
 Grows occasionally with new parameters
 Preserve object - pass the entire object instead of a
single value
 Introduce parameter object - combine parameters into a
new (immutable) object
Smells Within Classes
Type Embedded in Name
 We have duplicate - argument type and method name
duplicate one another
 e.g. “addCourse(Course)” - bit of a style issue
Uncommunicative Name
 Example: 1 or 2 character names, even loop variables
 Numbered names - side1, side2 vs. leftSide, rightSide
 Abbreviations - some common idioms, though some
(msg, conn, rs, i, etc.) are OK if used by convention
Dead Code
 Unused variables
 Methods - OK if application-independent methods
 Classes - OK if domain-independent classes
Smells Within Classes
Speculative Generality
 Guessing that flexibility will be need in future
• depends on what part system - lower level is probably OK
(framework)
 Remove general code, method, class, etc.
 OK when a framework or testing/debugging code
Magic Number
 Constants in code - always make a named constant
 Example: Math.PI instead of 3.14159 everywhere
 Important subscripts in arrays/lists
Complicated Expressions
 Introduce intermediate (explaining) variables
 Put expression into one or more methods
Smells Within Classes
Duplicate Code
 Can be nearly identical or conceptually identical
 If inside same class, extract common code into private
method
 If unrelated classes, extract into one class or a new class
Null Check
 Using null or 0 as reasonable default value - OK by me!
 Isolate check into single place and call externally and
*internally*:
 if (elements == null &&elements.size() == 0)
Smells Between Classes
Data Class
 Ensure methods arebehaviors, not view of internal structure
 Class will have methods that return state information
 Use accessors instead of public attributes
 Remove setters
 Hide collection attributes - Set getKeys() – see OK to me!
 Examine clients for pattern of use
• Example: client should call area() instead of length()/width()
Data Clump
 Same data in classes or parameter lists (city, state, zip)
 Or, same data and methods commonly appear
 Extract common data/methods into new Class
 Introduce a parameter object if commonality is in method
Smells Between Classes
Primitive Obsession
 Not enough objects - only primitive information
 Combine related primitives into objects (Data Clump)
Feature Envy
 Class manipulating data of other class rather than its
own
 Place actions of method in appropriate place - the
class and hide the data
Smells Between Classes
Message Chains
 a.b().c().d()
 Issue is A knows too much
about B and C, that they are
delegating to
 Extract method to out class in chain (e.g. add d() to
class B)
Middle Man
 f() {delegate.f()}
 Result from solving Message Chain above
• what! make up your mind!
Smells Between Classes
Divergent Change
 Class picks up unrelated features over time
• Should show up in your cohesion numbers
 Extract related features to new class(es)
Shotgun Surgery
 Simple changes require modification to several
classes
• Too much coupling
• To me, this is as much a smell as a refactoring practice
 Isolate changes to common place (if possible)
• may need to extract changes to a new class and move
features to it
Refactorings
Fowler’s book has a catalog (see Readings (1))
• There are many more
• Often reversible
Principles:
• Pay attention to the mechanics
• Let the compiler tell you
• Small steps with constant tests (XP philosophy)
Unit Testing and Refactoring
 By having unit tests available, you can verify a
refactoring produces functional equivalence
 XP’s approach is “test-code-refactor” (“lather-rinserepeat”)
Adapted from presentation by W..Wake, 2000
Refactoring
Refactoring actions rewrite source code
 Within a single Java source file
 Across multiple interrelated Java source files
Refactoring actions preserve program semantics
 Does not alter what program does, just affects the
way it does it
Refactoring strengths




Encourages exploratory programming
Facilitates Agile programming techniques
Encourages higher code quality
The resulting code should be of higher quality
• This should be represented in your static analysis tools
Refactor: Extract Method
Observation: "You have a code fragment that can be grouped
together (i.e. it is a cohesive 'lump' of code)."
Refactoring:


Make a method whose name explains the fragment’s purpose.
Replace all occurrences of the fragment with calls to the method.
Before:
void printOwing() {
printBanner();
System.out.println("name:
" + name);
System.out.println("amount: " + getOutstanding());
}
After:
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails (double outstanding) {
System.out.println("name:
" + name);
System.out.println("amount: " + outstanding);
}
Adapted from Scach, Object-oriented and Classical Software Engineering
Refactor: Encapsulate Field
Observation: "There is a public field."
(1) Make the field private.
(2) Create accessor method.
(3) Create mutator method (only if needed.)
(4) Revise all references to the field to call the accessor
or mutator instead.
BEFORE
AFTER
private String name;
public String name;
public String getName()
{return name;}
public String setName
(String newName) {
name = newName;
}
Adapted from Scach, Object-oriented and Classical Software Engineering
Adapted from presentation by W..Wake, 2000
Introduce Null Object
Use when your code does repeated checks for null
return values
 Null subclass type encapsulates default behaviors
you might have when null is returned.
BEFORE
AFTER
if (user == null)
plan = Plan.basic();
else
plan = user.getPlan();
Adapted from presentation by W..Wake, 2000
Parameterize Method
Several methods on a class may do similar things
but with different values in the method body
 Create one method that uses a parameter for the
different values
 Useful when a small number of finite alternatives are
used in a switch statement with constants (e.g. factory)
BEFORE
AFTER
HTTPService
HTTPService
handleGet(…)
handle(serviceType,…)
handlePut(…)
Adapted from presentation by W..Wake, 2000
Replace Constructor w/ Factory Method
You want to do more than simply constructing an
object when it is created
You want to manage object lifecycles
 Use a factory method/pattern
 Replace the constructor with a factory method
BEFORE
AFTER
public User(int type) {
this.type = type;
}
User(int type) {
this.type = type;
}
public static User
createUser(int type) {
return new User(type);
}
Adapted from presentation by W..Wake, 2000
Refactoring: Chain Constructors
Observation: "There is a class with multiple
constructors, each of which does non-trivial and
partially overlapping work."
Refactoring:
(1) Identify and write the 'least common denominator'
constructor.
(2) Recode all other constructors to call this one to do
the shared work.
Adapted from Scach, Object-oriented and Classical Software Engineering
Refactoring: Split Loop
BEFORE:
void printValues() {
double averageAge = 0;
double totalSalary = 0;
for (int i=0; i<people.length; i++) {
averageAge += people[i].age;
totalSalary += people[i].salary;
}
averageAge = averageAge / people.length;
System.out.println(averageAge);
System.out.println(totalSalary);
}
AFTER:
void printValues() {
double totalSalary = 0.0;
for (int i=0; i<people.length; i++)
totalSalary += people[i].salary;
Observation: "There is a loop
that is doing two things."
(e.g., accumulating both
average age and total
salary for all employees.)
Refactoring: Split into 2 loops.
 Makes each thing easier
to understand.
 Opens the door to further
refactoring/optimization
(e.g., one loop may be
replaced by a Collection
aggregate function).
double averageAge = 0.0;
for (int i=0; i<people.length; i++)
averageAge += people[i].age;
averageAge = averageAge / people.length;
System.out.println(averageAge);
System.out.println(totalSalary);
}
Adapted from Scach, Object-oriented and Classical Software Engineering
Refactoring -- Parameterize Method
Observation: "Several methods do similar things,
but with different values in the method body."
Refactoring:
(1) Create a parameterized method that can substitute
for each repetitive method;
(2) Compile;
(3) Replace the body of one method with a call to the
new method;
(4) Compile and test;
(5) Replace all calls to old method with parameterized
calls to new method;
(6) Repeat 2--6 for all replaced methods.
Nowadays you may be able to apply a generic
Adapted from Scach, Object-oriented and Classical Software Engineering
Refactoring: Split Temporary Variable
Observation: "You have a temporary variable assigned to in
more than one place, but is not a loop variable nor an
accumulating/collecting temporary variable."
Refactoring: Make a separate temp var for each assignment.
Before:
double temp = 2 * (height + width);
System.out.println(temp);
temp = height * width;
System.out.println(temp);
After:
final double perimeter = 2 * (height + width);
System.out.println(perimeter);
final double area = height * width;
System.out.println(area);
Adapted from Scach, Object-oriented and Classical Software Engineering
Refactoring Summary
Refactoring is sometimes thought of as “in-place design”, or
“just-in-time design”
 I do not agree with these statements. Refactoring may improve the
low-level design of code, but in places where full design is needed,
do it!
Refactoring is an expression of the Collective Code
Ownership and Courage agile values
 The code is the responsibility of everyone
 You should not go into a code module, look at it and think “geez
what a mess” and not do anything about it!
 You also need the courage to alter schedule expectations in the
name of quality
Refactoring is only of limited use in isolation
 Real power is in embedding in a test-code-test-code cycle
 Use unit tests and metrics to diagnose, measure, and inject quality!
25
Readings
1. Fowler, M. Refactoring: Improving the Design of Existing
Code, Addison-Wesley, 2000.
2. Wake, W. Refactoring Workbook, Addison-Wesley, 2003.
3. Kerievsky, J. Refactoring to Patterns, Addison-Wesley,
2005.
4. Scach, S. Classical and Object-oriented Software
Engineering, 4th ed., McGraw-Hill, 1999.
•
•
•
•
ASU’s Safari online service has (1), which includes the
full catalog of smells and refactorings
Most XP books include a description of refactoring and its
role in Agile development
Websites: www.refactoring.com, www.xp123.com
There is a decent section in Code Complete 2