Chapter 5 : Generality and logical Entailment
Download
Report
Transcript Chapter 5 : Generality and logical Entailment
Refactoring
Originally prepared for COMP314,
Bernhard Pfahringer
see also:
-links on the COMP204 page
-Code Complete, Chapter 24
1
Refactoring definition
“change to the internal structure of
software to make it easier to
understand and cheaper to change,
WITHOUT changing the observable
behaviour” (Martin Fowler, 1999)
I.e. changes “inside the black box”
only
2
Key points
Program changes are a fact of life
Change => degrade or improve
“code smells” indicate need for
change
know different refactorings (esp. IDE
support …)
use safe strategy
do it early on
3
Examples
What is wrong with this:
public void sendEmail(String message, Employee
recipient) {
…
… recipient.getEmailAddress();
… recipient.getName();
}
recipient type too specific:
- missed potential for reuse
- worse: potential for misuse:
… recipient.approveRaise(10000) …
4
Solution one: primitive types
Only provide what is really needed using
primitives including strings (or similar
“value” or immutable types):
public void sendEmail(String message, String
address, String name) {
…
… address …;
… name …;
}
Cons: many parameters, must change all callers
5
Solution two: Interface
Define Interface which provides only what is
needed (e.g. only read access, can even mimic
C++ const declarations)
public interface Addressee {
String getEmailAddress();
String getName();
}
public class Employee implements Addressee {
…
}
6
Solution two continued
public void sendEmail(String message, Addressee
recipient) {
…
… recipient.getEmailAddress();
… recipient.getName();
}
no need to change any callers
easy extension, e.g. add functionality to
Addressee, like boolean prefersPlainText()
7
Decompose complex condition
Extract code into separate methods with meaningful names:
if (date.before(SUMMER_START)
||date.after(SUMMER_END)) {
charge = quantity * _winterRate +
_winterServiceCharge;
} else {
charge = quantity * _summerRate;
}
if (notSummer(date)) {
charge = winterCharge(quantity);
} else {
charge = summerCharge(quantity);
}
8
Reverse conditional
if (notSummer(date)) {
charge = winterCharge(quantity);
} else {
charge = summerCharge(quantity);
}
if (isSummer(date)) {
charge = summerCharge(quantity);
} else {
charge = winterCharge(quantity);
}
Unless there is no ELSE branch …
9
Define Null objects
X x = computeX(…);
if (x != null) {
x.methodZ();
}
If there is a:
class NullX extends X { … methodZ() …}
Then computeX(…) can return it if needed, => no
NullPointerException, the following is safe
X x = computeX(…);
x.methodZ();
10
Replace Constructors with
Factory methods
public final class Boolean {
private boolean value;
private Boolean(boolean value) {
this.value = value;
}
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean getValue(boolean value) {
if (value) return Boolean.TRUE;
return Boolean.FALSE;
}
}
11
Safe refactoring (similar for
code performance tuning)
ensure rollback (e.g. via svn)
small steps, one at a time
always test (unit tests, and more)
bug fixing is NOT refactoring
refactor early
12
“Code smells”
code is duplicated
a method is too long
a loop is too long or too deeply nested
class has poor cohesion
class interface does not provide consistent
level of abstraction
parameter list has too many parameters
changes within a class are compartmentalized
change requires parallel mods in mult.classes
inheritance hierarchies must be modified in
parallel
13
more code smells
case statements modified in parallel
related data not organized into classes
method uses more features of another class
than its own
primitive data type is “overloaded”
class does not too very much
chain of methods passes “tramp data”
middleman object doing nothing
class relies on internals of another
method has a poor name
public data members/fields
14
and even more code smells
subclass uses only small parts of its parent’s
methods
comments explain too complex code
use of global variables
method must use complex setup code before
and/or takedown code after calling another
method
code not needed now, but maybe in the future
15
Types of refactorings
Data level refactorings
Statement-level refactoring
Method-level refactoring
Class implementation refactoring
Class interface refactoring
System-level refactorings
16
Data level refactorings
replace magic number with named
constant
rename variable to clearer
informative name
move expression inline
replace expression with method call
introduce intermediate variable
replace multi-use variable with singleuse variables
17
More Data level refactorings
Use local variable instead of
parameter (use final in parameter
list)
convert primitive data to class
convert type codes to enumeration
or to class with sub-classes
change array to an object
encapsulate collection
18
Statement-level refactoring
decompose boolean expression
replace complex boolean exp. with wellnamed method
consolidate code fragments of different
parts of conditional statement
Use break/continue/return in loops
Return/break early
use polymorphism instead of switch
use “null” objects
19
Method-level refactoring
extract a method
move method call inline
turn a long routine into a class
replace complex algorithm with a
simple one
add a parameter
remove a parameter
20
more Method-level refactoring
separate query from modification
combine similar methods by
parameterization
separate methods
pass one whole object instead of
many specific fields (cf “Addressee”)
pass specific fields instead of object
encapsulate downcasting
21
Class implementation
refactoring
change value object to reference
object
change reference object to value
object
replace method with data initialization
change member data or method
placement
extract specialised code into subclass
combine similar code into superclass
22
Class interface refactoring
move method into another class
split class into two
remove a class
hide a delegate
remove a middleman
replace inheritance by composition
replace composition by inheritance
introduce “foreign” method
introduce extension class
23
more Class interface refactoring
encapsulate exposed data fields
remove unwanted set methods
hide methods that are intended for
use outside a class
encapsulate unused methods
collapse sub with superclass if very
similar
24
System-level refactorings
create reference source for data outside
your control
change bi-directional class association to
uni-directional
change uni-directional class association to
bi-directional
Provide factory method instead of a
constructor
replace error code with exceptions and v.v.
25