320 likes | 481 Views
Refactoring – III. Measured Smells. Smells Covered. 1. Comments 2. Long method 3. Large Class. 1. Comments. Symptoms: symbols (// or /*) appear on code. Some IDEs color them. Causes: Present for many reasons; Author realizes that something isn’t as clear and adds a comment.
E N D
Refactoring – III Measured Smells
Smells Covered • 1. Comments • 2. Long method • 3. Large Class
1. Comments • Symptoms: symbols (// or /*) appear on code. Some IDEs color them. • Causes: Present for many reasons; • Author realizes that something isn’t as clear and adds a comment. • Some comments may be helpful • Tell why something is done a particular way (or not) • Cite an algorithm not obvious (when a simpler algorithm won’t do) • Other times: comment not necessary; goal of a routine may be communicated via its name
Comments • What to do: • Extract Method: When comment explains block of code, can often use this oneto pull the block into a separate method. • Comment will often suggest a name for the new method • Rename Method: When comment explains what a method does better than the method’s name use comment as basis of the new name • Introduce Assertion: When comment explains preconditions, consider this one to replace the comment with code. • Payoff: • Improves communication. May expose duplication • Contraindications • Don’t delete comments pulling their own weight.
Comments – Extract Method Void printOwing (double amount){ printBanner(); // print details System.out.println (“name:” + name); System.out.println (“amount:” + amount); Change to: void printOwing (double amount){ printBanner(); printDetails (amount); }// end printOwning() void printDetails(double amount) { System.out.println (“name:” + name); System.out.println (“amount:” + amount); }// end printDetails()
Comments – Extract Method Motivation • Extract Method is a very common refactoring. • Look at long method or code that needs a comment to understand its purpose. • Turn fragment of code into its own method. • Prefer short, well-named methods; several reasons. • First, it increases the chances that other methods can use a method when the method is finely grained. • Second, it allows the higher-level methods to read more like a series of comments. • Overriding also is easier when the methods finely grained.
Mechanics of Extract Method • 1. Create new method • Name after the intention of the method • (name it by what it does, not by how it does it). • If code you want to extract is very simple, such as a single message or function call, can extract if name of new method will reveal intentionof the code in a better way. • If you can’t come up with a more meaningful name, don’t extract the code. • Copy the extracted code from the source method into the new target method.
Mechanics (continued) 2. Scan the extracted code for references to any variables that are local in scope to the source method. These are local variables and parameters to the method. See whether any temporaryvariables are used only within this extracted code. If so, declare them in the target method as temporary variables. 46 8
Mechanics (continued) • Scope of Variables: Look to see whether any of these local-scope variables are modified by the extracted code. • If one variable is modified, see whether you can treat extracted code as a query and assignresult to variable concerned. • If this is awkward, or if there is more than one such variable, you can’t extract the method as it stands. • You may need to use Split Temporary Variableand try again. 46 9
Mechanics Pass Variables- Pass variables to target method as parameters local-scope variables read from extracted code. Compile after dealing with all locally-scoped variables. Replace extracted code in source with call to target method. If you moved temporary variables over to target method, look to see whether they were declared outside of the extracted code. If so, you can now remove the declaration. Compile and test. 46 10
Comments – Rename Method • The name of a method does not reveal its purpose. • 02Change the name of the method.
Comments – Rename Method • Author advocates small methods to factor complex processes. • Done badly, can lead to merry dance to find out what all the little methods do. • Key: name methods that communicate their intention. • Good way: Think what comment for method would be. • Turn comment into name of method • If you see a badly named method, CHANGE IT!
Comments – Rename Method Motivation • Remember your code is for a human first and a computer second. • Humans need good names. • Good naming is a skill and is key to being a truly skillful programmer. • Same applies to other aspects of the signature. • If reordering parameters clarifies matters, do it. (see Add Parameter and Remove Parameter). 46 13
Comments – Rename Method Mechanics • Check to see whether the method signature is implemented by a superclass or subclass. • If so, perform these steps for each implementation. • Declare a new method with the new name. • Copyoldbody of code to newname; make alterations. • Compile. • Changebodyofoldmethod so it calls new one. • Compile and test. • Find all references to old method name and change them to refer to the new one. • Compile and test after each change. • Remove the old method. • If the old method is part of the interface and you cannot remove it, leave it in place and mark it as deprecated. • Compile and test.
Comments – Rename Method Example • Have a method to get a person’s telephone number: • public String getTelephoneNumber() { return ("(" + _officeAreaCode + ") " + _officeNumber); } • Want to rename method to getOfficeTelephoneNumber. • Create new method and copy body to new method. • Change Old method to call the new one: • class Person... public String getTelephoneNumber(){ return getOfficeTelephoneNumber(); } public String getOfficeTelephoneNumber() { return ("(" + _officeAreaCode + ") " + _officeNumber); } • Find callers of old method, Switch them to call the new one. • After switching, can remove the old method.
Comments – Introduce Assertion • A section of code assumes something about the state of the program. Make the assumption explicit with an assertion. double getExpenseLimit() { // should have either expense limit or a primary project return (_expenseLimit != NULL_EXPENSE) ? expenseLimit: primaryProject.getMemberExpenseLimit(); }// end getExpenseLimit() double getExpenseLimit() { Assert.isTrue (expenseLimit != NULL_EXPENSE || primaryProject != null); return (expenseLimit != NULL_EXPENSE) ? expenseLimit: primaryProject.getMemberExpenseLimit(); }// end getExpenseLimit()
II. Refactoring – Long Method • Symptoms • Large number of lines. Be immediately suspicious of any method with more than 5 to 10 lines) • Causes: • A method starts down a path and, ratherthan break the flow or identify the helper classes, the author adds “one more thing”. • Code is often easier to write than it is to read • So there’s a temptation to write blocks that are too big.
Refactoring – Long Method (cont.) • What to do: • Use ExtractMethod to break method into smaller pieces. • Look for comments or white space delineating interesting blocks. • Extract methods semantically meaningful, not just introduce a function call every seven lines. • Payoff: • Improves communications. • May expose duplication. • Often helps new classes and abstractions emerge.
Refactoring – Long Method (cont.) • Discussion: • Performance issues due to number of calls? • Most of the time, this is a nonissue. • By getting code as clean as possible before worrying about performance, may gain big insights to restructure systems and algorithms in a way that dramatically increases performance. • Contraindications: • Sometimes longer method is best way to express something. • Like almost all smells, length is a warning sign – not a guarantee- of problem.
III. Refactoring – Large Class • Symptoms: • Large number of instance variables • Large number of methods • Large number of lines • Causes: • Large classes get bit a little bit at a time. • Author keeps adding just one more capability to a class until eventually it grows too big. • Sometimes the problem is a lack of insight into the parts that make up the whole class. • In any case, the class represents too many responsibilities folded together.
Refactoring – Large Class • What to do: • In general, trying to break up the class. • If class has Long Methods, address that smell first. • To break up the class, three approaches are most common: • Extract Class– If you can identify a new class that has part of this class’s responsibilities • Extract Subclass, if you can divide responsibilities between the class and a new subclass • Extract Interface, if you can identify subsets of features that clients use. • Sometimes class is big because it’s a GUI class, and it represents not only the display component, but model as well. • Payoff: • Improves communication. May expose duplication
IIIa. Refactoring – Extract Class • Here you have one class doing work of two. • Create a new class and • Move relevant fields and methods from old class into new class.
Refactoring – Extract Class Motivation • Have heard a class should be a crisp abstraction, handle a few clear responsibilities, or similar. • In practice, classes grow. • Often we add a responsibility to a class feeling it’s not worth a separate class, but as that responsibility grows and breeds, class becomes too complicated. • Soon, class is as crisp as a micro-waved duck.
Refactoring – Extract Class Motivation • Such a class is • one with many methods and quite a lot of data. • A class that is too big to understand easily. • Consider where it can be split, and you split it. 46 24
Where to Split – Good Signs: • A subset of the data and a subset of the methods seem to go together. • Subsets of data that usually change together or are particularly dependent on each other. • Useful test: • What would happen if you removed a piece of data or a method. • What other fields and methods would become nonsense?
Refactoring – Extract Class Mechanics • Decide how to split the responsibilities of the class. • Create a new class to express the split-off responsibilities. • If the responsibilities of the old class no longer match its name, rename the old class. • Make a link from the old to the new class. • You may need a two-way link. But don’t make the back link until you find you need it. • More:
Refactoring – Extract Class Mechanics • Use Move Field on each field you wish to move. • Compile and test after each move. • Use Move Method to move methods over from old to new. Start with lower-level methods (called rather than calling) and build to the higher level. • Compile and test after each move. • Review and reduce the interfaces of each class. • Decide whether to expose the new class. • If you do expose the class, decide whether to expose it as a reference object or as an immutable value object. 46 27
Refactoring – Extract Class Example • class Person... • public String getName() { return name; } public String getTelephoneNumber() { return ("(" + officeAreaCode+ ") " + officeNumber); } public String getOfficeAreaCode() { return officeAreaCode; } public void setOfficeAreaCode(String arg) {officeAreaCode= arg; } public String getOfficeNumber() { return officeNumber; } public void setOfficeNumber(String arg) { officeNumber= arg; } private String name; private String officeAreaCode; private String officeNumber; • In this case I can separate the telephone number behavior into its own class. I start by defining a telephone number class:
In this case I can separate the telephone number behavior into its own class. I start by defining a telephone number class: • class TelephoneNumber{ }// end class • That was easy! • No problems. • I next make a link from the person to the telephone number: • class Person private TelephoneNumberofficeTelephone= new TelephoneNumber(); That too was easy. Note all the while your program still runs!
Now I use Move Field on one of the fields: class TelephoneNumber • { String getAreaCode() • return areaCode; • }// end TelephoneNumber() • void setAreaCode (String arg) { • areaCode = arg; • }// end setAreaCode() • private String areaCode; • }// end class TelephoneNumber • class Person... • public String getTelephoneNumber() { • return ("(" + getOfficeAreaCode() + ") " + officeNumber); • }// end getTelephoneNumber() • String getOfficeAreaCode() { • return officeTelephone.getAreaCode(); • }// end getOfficeAreaCode() • void setOfficeAreaCode(String arg) { • officeTelephone.setAreaCode(arg); • }// end setOFficeAreaCode()
I can then move the other field and use Move Method on the telephone number: • class Person... • public String getName() { • return name; } • public String getTelephoneNumber(){ • return officeTelephone.getTelephoneNumber(); } • TelephoneNumbergetOfficeTelephone() { • return officeTelephone; } • private String name; • private TelephoneNumberofficeTelephone= new TelephoneNumber(); • class TelephoneNumber... • public String getTelephoneNumber() { • return ("(" + _areaCode + ") " + number); } • String getAreaCode() { • return areaCode; } • void setAreaCode(String arg) { • areaCode= arg; } • String getNumber() { • return number; } • void setNumber(String arg) { • number = arg; } • private String number; • private String areaCode;
V. Conclusion • The smells in this set of slides are the easiest to identify. • They’re not necessarily the easiest to fix. • There are other metrics that have been applied to software. • Many of them are simply refinements of code length. • Pay attention when things feel like they’re getting too big. • There is not a one-to-one relationship between refactorings and smells; we’ll run into the same refactorings again. • For example, Extract Method is a tool that can fix many problems. • Finally, remember a smell is an indication of a potential problem, not a guarantee of an actual problem. • You will occasionally find falsepositives – things that smell to you, but are actually better than the alternatives. • But most code has plenty of real smells that can keep you busy.