Download Evolving Software, Refactoring Philosophy - Lecture Slides | CMSC 433 and more Study notes Programming Languages in PDF only on Docsity! 1 CMSC 433 – Programming Language Technologies and Paradigms Spring 2007 Refactoring April 24, 2007 Lots of material taken from Fowler, Refactoring: Improving the Design of Existing Code 2 Evolving Software • Problem – The requirements of real software often change in ways that cannot be handled by the current design – Moreover, trying to anticipate changes in the initial implementation can be difficult and costly • Solution – Redesign as requirements change – Refactor code to accommodate new design 3 Example • (p204) Replace Magic Number with Symbolic Constant double potentialEnergy(double m, double h) { return m * 9.81 * h; } • becomes... static final double G = 9.81; double potentialEnergy(double m, double h) { return m * G * h; } 4 Some Motivations for This Refactoring • Magic numbers have special values – But why they have those values is not obvious – So we like to give them a name • Magic numbers may be used multiple times – Easy to make errors • May make a typo when putting in a number • May need to change a number later (more digits of G) 5 Conventional Wisdom: The Design is Fixed • Software process looks like this: – Step 1: Design, design, design – Step 2: Build your system • Once you’re on step 2, don’t change the design! – You might break something in the code – You need to update your design documents – You need to communicate your new design with everyone else 6 What if the Design is Broken? • You’re kind of stuck – Design changes are very expensive – When you’re “cleaning up the code,” you’re not adding features • Result: An inappropriate design – Makes code harder to change – Makes code harder to understand and maintain – Very expensive in the long run 7 Refactoring Philosophy • It’s hard to get the design right the first time – So let’s not even pretend – Step 1: Make a reasonable design that should work, but... – Plan for changes • As implementers discover better designs • As your clients change the requirements (!) • But how can we ensure changes are safe? 8 Refactoring Philosophy (cont’d) • Make all changes small and methodical – Follow mechanical patterns (which could be automated in some cases) called refactorings, which are semantics-preserving • Retest the system after each change – By rerunning all of your unit tests – If something breaks, you know what caused it – Notice: we need fully automated tests for this case 17 Feature Envy • A method seems more interested in a class other than the one it is actually in – E.g., invoking lots of get methods • Move Method – Move method from one class to another • Extract Method – Pull out code in one method into a separate method 18 Move Method • Should other methods also be moved? • What about sub- and superclasses? • What about access control (public, protected)? 19 Extract Method • Are you ever going to reuse this new method? • Local variable scopes? • Extra cost of method invocation? void printOwning(double amt) { printBanner(); System.out.println(“name” + name); System.out.println(“amount” + amt); } void printDetails(double amt) { System.out.println(“name” + name); System.out.println(“amount” + amt); } void printOwning(double amt) { printBanner(); printDetails(amt); } 20 Long Method • A method is too long. Long methods are harder to understand than lots of short ones. • Can decompose with Extract Method • Replace Temp with Query – Remove code that assigns a method call to a temporary, and replace references to that temporary with the call • Replace Method with Method Object – Use the command pattern to build a “closure” 21 Replace Temp with Query • Local variables make it hard to use some refactorings, e.g., Extract Method • What about performance? double basePrice = num * price; if (basePrice > 1000) return basePrice * 0.95; else return basePrice * 0.98; double basePrice() { return num * price; } if (basePrice() > 1000) return basePrice() * 0.95; else return basePrice() * 0.98; 22 Switch Statements • Usually not necessary in delegation-based OO programming • Replace Type Code with State/Strategy – Define a class hierarchy, a subclass for each type code • Replace Conditional with Polymorphism – Call method on state object to perform the check; switching is based on dynamic dispatch 23 Replace Type Code with State/Strategy 24 Replace Conditional with Polymorphism double getSpeed() { switch (kind) { case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed()-loadFactor()*numberOfCoconuts; case NORWEGIAN_BLUE: return (isNailed) ? 0 : getBaseSpeed(voltage); throw new RuntimeException(“Should be unreachable”); } } 25 Duplicated Code • The same expression used in different places in the same class – Use Extract Method to pull it out into a method • The same expression in two subclasses sharing the same superclass – Extract Method in each, then – PullUp method into parent • Duplicated code in two unrelated classes – Extract Class - Break a class that does too many things into smaller classes 26 Pull Up Method • Might do other refactorings if methods don’t quite match • What if doesn’t appear in all subclasses? 27 Extract Class • How do we decide what goes in new class? • Do fields still need to be accessed in orig class? 28 Long Parameter List • Lots of parameters occlude understanding • Replace Parameter with Method – Remove method parameters and instead use some other way to get the parameter value (e.g., method call) • Introduce Parameter Object – Group parameters that go together into a container object