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