Refactoring Originally prepared for COMP 314 Bernhard Pfahringer
Refactoring Originally prepared for COMP 314, Bernhard Pfahringer see also: -links on the COMP 204 page -Code Complete, Chapter 24 1
Refactoring definition o o “change to the internal structure of software to make it easier to understand cheaper to change, WITHOUT changing the observable behaviour” (Martin Fowler, 1999) I. e. changes “inside the black box” only 2
Key points o o o 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 send. Email(String message, Employee recipient) { … … recipient. get. Email. Address(); … recipient. get. Name(); } recipient type too specific: missed potential for reuse worse: potential for misuse: … recipient. approve. Raise(10000) … o 4
Solution one: primitive types o Only provide what is really needed using primitives including strings (or similar “value” or immutable types): public void send. Email(String message, String address, String name) { … … address …; … name …; } Cons: many parameters, must change all callers 5
Solution two: Interface o Define Interface which provides only what is needed (e. g. only read access, can even mimic C++ const declarations) public interface Addressee { String get. Email. Address(); String get. Name(); } public class Employee implements Addressee { … } 6
Solution two continued public void send. Email(String message, Addressee recipient) { … … recipient. get. Email. Address(); … recipient. get. Name(); } o o no need to change any callers easy extension, e. g. add functionality to Addressee, like boolean prefers. Plain. Text() 7
Decompose complex condition Extract code into separate methods with meaningful names: if (date. before(SUMMER_START) ||date. after(SUMMER_END)) { charge = quantity * _winter. Rate + _winter. Service. Charge; } else { charge = quantity * _summer. Rate; } if (not. Summer(date)) { charge = winter. Charge(quantity); } else { charge = summer. Charge(quantity); } o 8
Reverse conditional if (not. Summer(date)) { charge = winter. Charge(quantity); } else { charge = summer. Charge(quantity); } if (is. Summer(date)) { charge = summer. Charge(quantity); } else { charge = winter. Charge(quantity); } Unless there is no ELSE branch … 9
Define Null objects X x = compute. X(…); if (x != null) { x. method. Z(); } If there is a: class Null. X extends X { … method. Z() …} Then compute. X(…) can return it if needed, => no Null. Pointer. Exception, the following is safe X x = compute. X(…); x. method. Z(); 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 get. Value(boolean value) { if (value) return Boolean. TRUE; return Boolean. FALSE; } } 11
Safe refactoring (similar for code performance tuning) o o o 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” o o o o o 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 o o o o o 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 o o o 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 o o o Data level refactorings Statement-level refactoring Method-level refactoring Class implementation refactoring Class interface refactoring System-level refactorings 16
Data level refactorings o o o 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 o o o 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 o o o o 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 o o o 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 o o o 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 o o o 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 o o o o o 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 o o o 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 o o o 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
- Slides: 25