Refactoring Tips by Martin Fowler
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Refactoring Tips by Martin Fowler

  • 16,796 views
Uploaded on

Software Refactoring , how to clean and organize the code.

Software Refactoring , how to clean and organize the code.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
16,796
On Slideshare
15,893
From Embeds
903
Number of Embeds
56

Actions

Shares
Downloads
542
Comments
1
Likes
15

Embeds 903

http://www.hamishgraham.net 269
http://redigon.blogspot.com 163
http://redigon.blogspot.in 71
http://katychuang.tumblr.com 62
http://bitacorapjm.blogspot.com.ar 57
http://www.redigon.blogspot.com 48
http://refact.blogspot.com 43
http://redigon.blogspot.co.uk 29
http://redigon.blogspot.com.au 26
http://redigon.blogspot.ca 13
http://redigon.blogspot.com.br 8
http://redigon.blogspot.de 8
http://www.redigon.blogspot.in 8
http://redigon.blogspot.it 7
http://redigon.blogspot.tw 6
http://www.slideshare.net 5
http://redigon.blogspot.ru 5
http://redigon.blogspot.kr 5
http://redigon.blogspot.ro 5
http://redigon.blogspot.sg 4
http://redigon.blogspot.com.es 4
http://www.redigon.blogspot.com.es 3
http://redigon.blogspot.no 3
http://redigon.blogspot.co.nz 3
http://redigon.blogspot.ie 3
http://redigon.blogspot.se 3
http://www.redigon.blogspot.com.br 3
http://www.redigon.blogspot.ch 3
http://redigon.blogspot.com.ar 3
http://redigon.blogspot.fi 2
http://redigon.blogspot.fr 2
http://bitacorapjm.blogspot.com 2
http://static.slidesharecdn.com 2
http://redigon.blogspot.co.il 2
http://safe.tumblr.com 2
http://redigon.blogspot.com.tr 1
http://10.70.168.173 1
http://www.google.com 1
https://twitter.com 1
http://www.scoop.it 1
http://redigon.blogspot.nl 1
http://redigon.blogspot.jp 1
http://redigon.blogspot.pt 1
http://redigon.blogspot.hu 1
http://redigon.blogspot.ch 1
http://redigon.blogspot.be 1
http://translate.googleusercontent.com 1
http://hamishgraham.net 1
http://yandex.ru 1
http://www.redigon.blogspot.mx 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • You have a temporary variable assigned to more than once, but is not a loop variable nor a collecting temporary variable. Make a separate temporary variable for each assignment.
  • Temporary variables are made for various uses. Some of these uses naturally lead to the temp's being assigned to several times. Loop variables [Beck] change for each run around a loop (such as the i in for (int i=0; i<10; i++). Collecting temporary variables [Beck] collect together some value that is built up during the method. Many other temporaries are used to hold the result of a long-winded bit of code for easy reference later. These kinds of variables should be set only once. That they are set more than once is a sign that they have more than one responsibility within the method. Any variable with more than one responsibility should be replaced with
  • // I.C Pass by Value pass by value, any change to the parameter is not reflected in the calling routine.
  • That's the essential refactoring. The benefit is that I can now easily use Extract Method on the compute method without ever worrying about the argument's passing: int compute () { importantValue1 = (inputVal * quantity) + _account.delta(); importantValue2 = (inputVal * yearToDate) + 100; importantThing(); int importantValue3 = importantValue2 * 7; // and so on. return importantValue3 - 2 * importantValue1; } void importantThing() { if ((yearToDate - importantValue1) > 100) importantValue2 -= 20;
  • Example An account class illustrates this refactoring: class Account... double overdraftCharge() { if (_type.isPremium()) { double result = 10; if (_daysOverdrawn > 7) result += (_daysOverdrawn - 7) * 0.85; return result; } else return _daysOverdrawn * 1.75; } double bankCharge() { double result = 4.5; if (_daysOverdrawn > 0) result += overdraftCharge(); return result; } private AccountType _type; private int _daysOverdrawn; Let's imagine that there are going to be several new account types, each of which has its own rule for calculating the overdraft charge. So I want to move the overdraft charge method over to the account type. The first step is to look at the features that the overdraftCharge method uses and consider whether it is worth moving a batch of methods together. In this case I need the _daysOverdrawn field to remain on the account class, because that will vary with individual accounts. 118 Next I copy the method body over to the account type and get it to fit. class AccountType... double overdraftCharge(int daysOverdrawn) { if ( isPremium() ) { double result = 10; if ( daysOverdrawn > 7) result += ( daysOverdrawn - 7) * 0.85; return result; } else return daysOverdrawn * 1.75; } In this case fitting means removing the _type from uses of features of the account type, and doing something about the features of account that I still need. When I need to use a feature of the source class I can do one of four things: (1) move this feature to the target class as well, (2) create or use a reference from the target class to the source, (3) pass the 0source object as a parameter to the method, (4) if the feature is a variable, pass it in as a parameter. In this case I passed the variable as a parameter. Once the method fits and compiles in the target class, I can replace the source method body with a simple delegation: class Account... double overdraftCharge() { return _type.overdraftCharge(_daysOverdrawn); } At this point I can compile and test. I can leave things like this, or I can remove the method in the source class. To remove the method I need to find all callers of the method and redirect them to call the method in account type: class Account... double bankCharge() { double result = 4.5; if (_daysOverdrawn > 0) result += _type.overdraftCharge(_daysOverdrawn); return result; } Once I've replaced all the callers, I can remove the method declaration in account. I can compile and test after each removal, or do them in a batch. If the method isn't private, I need to look for other classes that use this method. In a strongly typed language, the compilation after removal of the source declaration finds anything I missed. In this case the method referred only to a single field, so I could just pass this field in as a variable. If the method called another method on the account, I wouldn't have been able to do that. In those cases I need to pass in the source object: class AccountType... double overdraftCharge(Account account) { if (isPremium()) { double result = 10; if (account.getDaysOverdrawn() > 7) result += (account.getDaysOverdrawn() - 7) * 0.85; return result; } else return account.getDaysOverdrawn() * 1.75; } I also pass in the source object if I need several features of the class, although if there are too many, further refactoring is needed. Typically I need to decompose and move some pieces back.
  • Data Value is a String , Value Type. Lets say we have a telephone number as a string So we want to separate. this telephone number into area code so we transform the class into object. Motivation Often in early stages of development you make decisions about representing simple facts as simple data items. As development proceeds you realize that those simple items aren't so simple anymore. A telephone number may be represented as a string for a while, but later you realize that the telephone needs special behavior for formatting, extracting the area code, and the like. For one or two items you may put the methods in the owning object, but quickly the code smells of duplication and feature envy. When the smell begins, turn the data value into an object.
  • Motivation Arrays are a common structure for organizing data. However, they should be used only to contain a collection of similar objects in some order.
  • Motivation One of the principal tenets of object orientation is encapsulation, or data hiding. This says that you should never make your data public. When you make data public, other objects can change and access data values without the owning object's knowing about it. This separates data from behavior.
  • Motivation Sometimes you see a series of conditional checks in which each check is different yet the resulting action is the same. When you see this, you should use ands and ors to consolidate them into a single conditional check with a single result.
  • Motivation Sometimes you find the same code executed in all legs of a conditional. In that case you should move the code to outside the conditional. This makes clearer what varies and what stays the same.
  • Motivation An important part of the code style I am advocating is small methods to factor complex processes. Done badly, this can lead you on a merry dance to find out what all the little methods do. The key to avoiding this merry dance is naming the methods. Methods should be named in a way that communicates their intention. A good way to do this is to think what the comment for the method would be and turn that comment into the name of the method.
  • Motivation When you have a function that gives you a value and has no observable side effects, you have a very valuable thing. You can call this function as often as you like. You can move the call to other places in the method. In short, you have a lot less to worry about.
  • Motivation You may see a couple of methods that do similar things but vary depending on a few values. In this case you can simplify matters by replacing the separate methods with a single method that handles the variations by parameters. Such a change removes duplicate code and increases flexibility, because you can deal with other variations by adding parameters.  
  • Motivation Often you see a particular group of parameters that tend to be passed together. Several methods may use this group, either on one class or in several classes. Such a group of classes is a data clump and can be replaced with an object that carries all of this data. It is worthwhile to turn these parameters into objects just to group the data together. This refactoring is useful because it reduces the size of the parameter lists, and long parameter lists are hard to understand. The defined accessors on the new object also make the code more consistent, which again makes it easier to understand and modify.
  • Motivation Refactoring often causes you to change decisions about the visibility of methods. It is easy to spot cases in which you need to make a method more visible: another class needs it and you thus relax the visibility. It is somewhat more difficult to tell when a method is too visible. Ideally a tool should check all methods to see whether they can be hidden. If it doesn't, you should make this check at regular intervals.

Transcript

  • 1. Refactoring Tips by Martin Fowler
    • Igor Crvenov
    • Technical Lead
    • Redigon Software Solutions
    • Contact: [email_address] , crvenovi@gmail.com
  • 2.
    • You write code that tells the computer what to do, and it responds by doing exactly what you tell it.
    • Our code =
    • The trouble is that when you are trying to get the program to work, you are not thinking about that future developer.
  • 3. What happens when future developer comes ?
    • Someone will try to read your code in a few months' time to make some changes. The programmer will take a week to make a change that would have taken only an hour if she had understood your code.
  • 4. Solution to the spaghetti code problem
    • Refactoring
    Tip: Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
  • 5. What can we do with refactoring
    • In essence when you refactor you are improving the design of the code after it has been written, refactoring Improves the Design of Software
    • Refactoring Makes Software Easier to Understand
  • 6.
    • Refactoring helps you find bugs
    • Refactoring makes you program faster
  • 7. Who is Martin Fowler ? Martin Fowler is an author and international speaker on software development, specializing in object-oriented analysis and design, UML, patterns, and agile software development methodologies, including extreme programming. Fowler is a member of the Agile Alliance and helped create the Manifesto for Agile Software Development in 2001, along with more than 15 co-authors. Martin Fowler was born in Walsall England, and lived in London a decade before moving to United States in 1994.
  • 8. Refactoring definitions:
    • Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure
    • Refactoring (noun): a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.
    • Refactor (verb): to restructure software by applying a series of refactorings without changing its observable behavior.
  • 9. How should be refactoring scheduled?
    • Should we allocate two weeks every couple of months to refactoring?
    • M.F: Refactoring is something you do all the time in little bursts. You don't decide to refactor, you refactor because you want to do something else, and refactoring helps you do that other thing.
    • The Rule of Three(Don Roberts)
      • The first time you do something, you just do it
      • The second time you do something similar, you wince at the duplication, but you do the duplicate thing anyway.
      • The third time you do something similar, you refactor.
      • Tip: Three strikes and you refactor.
  • 10. When to refactor ?
    • Refactor when you add Function
    • Refactor when you need to fix a bug
    • Refactor as you do code review
  • 11. When you should not refactor?
    • There are times when the existing code is such a mess that although you could refactor it, it would be easier to start from the beginning.
    • The other time you should avoid refactoring is when you are close to a deadline. At that point the productivity gain from refactoring would appear after the deadline and thus be too late.
  • 12. Bad Smells in Code
    • Duplicated code
    • Long Method
    • Large Class
    • Long Parameter List
  • 13.
    • M.F:Whenever I do refactoring, the first step is always the same. I need to build a solid set of tests for that section of code. The tests are essential because even though I follow refactorings structured to avoid most of the opportunities for introducing bugs, I'm still human and still make mistakes. Thus I need solid tests.
    • Tip:
    • Before you start refactoring, check that you have a solid suite of tests. These tests must be self-checking
  • 14. Refactoring Methods
    • Exstract Method
      • You have a code fragment that can be grouped together. Turn the fragment in to a method whose name explains the purpose of the method.
      • Extract Method is one of the most common refactorings I do. I look at a method that is too long or look at code that needs a comment to understand its purpose. I then turn that fragment of code into its own method.
  • 15. Extract Method
    • Before refactoring
    • After Refactoring
    • void printOwing(double amount)
    • {
    • printBanner();
    • printDetails(amount);
    • }
    • void printDetails(double amount)
    • {
    • WriteLine("name:" + _name);
    • WriteLine("amount" + amount);
    • }
    • void printOwing(double amount)
    • {
    • printBanner();
    • //print details
    • WriteLine("name:" + _name);
    • WriteLine("amount" + amount);
    • }
  • 16. Inline Method
  • 17. Inline Method
    • Before Refactoring
    • After Refactoring
    • int getRating() {
    • return (moreThanFiveLateDeliveries()) ? 2 : 1;
    • }
    • boolean moreThanFiveLateDeliveries() {
    • return _numberOfLateDeliveries > 5;
    • }
    • int getRating() {
    • return (_numberOfLateDeliveries > 5) ? 2 : 1;
    • }
  • 18. Split Temporary Variable
    • You have a temporary variable assigned to more than once, but is not a loop variable nor a collecting temporary variable. Make a separate temporary variable for each assignment.
  • 19.
    • Before Refactoring
    • double temp = 2 * (_height + _width);
    • WriteLine(temp);
    • temp = _height * _width;
    • WriteLine(temp);
    • After Refactoring
    • double perimeter = 2 * (_height + _width);
    • WriteLine(perimeter);
    • double area = _height * _width;
    • WriteLine(area);
  • 20. Remove Assignments to Parameters
    • The code assigns to a parameter.
    • Use a temporary variable instead.
    • Before Refactoring:
    • int discount (int inputVal, int quantity, int yearToDate) {
    • if (inputVal > 50) inputVal -= 2;
    • After refactoring :
    • int discount (int inputVal, int quantity, int yearToDate) {
    • int result = inputVal;
    • if (inputVal > 50) result -= 2;
  • 21. Replace Method with Method Object
    • You have a long method that uses local variables in such a way that you cannot apply Extract Method. Turn the method into its own object so that all the local variables become fields on that object. You can then decompose the method into other methods on the same object.
    • class Order...
    • double price() {
    • double primaryBasePrice;
    • double secondaryBasePrice;
    • double tertiaryBasePrice;
    • // long computation;
    • ...
    • }
  • 22. Replace Method with Method Object
  • 23.
    • Class Account
    • int gamma (int inputVal, int quantity, int yearToDate) {
    • int importantValue1 = (inputVal * quantity) + delta();
    • int importantValue2 = (inputVal * yearToDate) + 100;
    • if ((yearToDate - importantValue1) > 100)
    • importantValue2 -= 20;
    • int importantValue3 = importantValue2 * 7;
    • // and so on.
    • return importantValue3 - 2 * importantValue1;
    • }
  • 24. class Gamma... private final Account _account; private int inputVal; private int quantity; private int yearToDate; private int importantValue1; private int importantValue2; private int importantValue3; Gamma (Account source, int inputValArg, int quantityArg, int yearToDateArg) { _account = source; inputVal = inputValArg; quantity = quantityArg; yearToDate = yearToDateArg; }
  • 25. int compute () { importantValue1 = (inputVal * quantity) + _account.delta(); importantValue2 = (inputVal * yearToDate) + 100; if ((yearToDate - importantValue1) > 100) importantValue2 -= 20; int importantValue3 = importantValue2 * 7; // and so on. return importantValue3 - 2 * importantValue1; } int gamma (int inputVal, int quantity, int yearToDate) { return new Gamma(this, inputVal, quantity, yearToDate).compute(); }
  • 26. Moving Features between object
    • One of the most fundamental, if not the fundamental, decision in object design is deciding where to put responsibilities.
  • 27. Move Method
    • A method is, or will be, using or used by more features of another class than the class on which it is defined.
    • Create a new method with a similar body in the class it uses most. Either turn the old method into a simple delegation, or remove it altogether.
  • 28. Move Method Motivation: Moving methods is the bread and butter of refactoring. I move methods when classes have too much behavior or when classes are collaborating too much and are too highly coupled. By moving methods around, I can make the classes simpler and they end up being a more crisp implementation of a set of responsibilities.
  • 29. Move Field
    • A field is, or will be, used by another class more than the class on which it is defined. Create a new field in the target class, and change all its users.
  • 30. Extract class
    • You have one class doing work that should be done by two. Create a new class and move the relevant fields and methods from the old class into the new class.
  • 31. Inline Class
    • A class isn't doing very much.Move all its features into another class and delete it.
    • Inline Class is the reverse of Extract Class. I use Inline Class if a class is no longer pulling its weight and shouldn't be around any more. Often this is the result of refactoring that moves other responsibilities out of the class so there is little left.
  • 32. Hide Delegate
    • A client is calling a delegate class of an object.
    • Create methods on the server to hide the delegate.
  • 33. Hide Delegate
    • Motivation
      • One of the keys, if not the key, to objects is encapsulation. Encapsulation means that objects need to know less about other parts of the system. Then when things change, fewer objects need to be told about the change—which makes the change easier to make
  • 34. Remove the Middle Man
    • A class is doing too much simple delegation. Get the client to call the delegate directly.
  • 35. Introduce Foreign Method
    • A server class you are using needs an additional method, but you can't modify the class. Create a method in the client class with an instance of the server class as its first argument.
  • 36. Introduce Foreign Method Example Date newStart = new Date (previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate() + 1);   Date newStart = nextDay(previousEnd); private static Date nextDay(Date arg) { return new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1); }
  • 37. Introduce Local Extension
    • A server class you are using needs several additional methods, but you can't modify the class.Create a new class that contains these extra methods. Make this extension class a subclass or a wrapper of the original
  • 38.  
  • 39. Self Encapsulated Field
    • Create getting and setting methods for the field and use only those to access the field
    • private int _low, _high;
    • boolean includes (int arg) {
    • return arg >= _low && arg <= _high;
    • }
    • After Refactoring :
    • private int _low, _high;
    • boolean includes (int arg) {
    • return arg >= getLow() && arg <= getHigh();
    • }
    • int getLow() {return _low;}
    • int getHigh() {return _high;}
  • 40.  
  • 41. Replace Data with value object
    • You have a data item that needs additional data or behavior. Turn the data item into an object.
  • 42. Replace Array with object
    • You have an array in which certain elements mean different things. Replace the array with an object that has a field for each element.
    • String[] row = new String[3];
    • row [0] = &quot;Liverpool&quot;;
    • row [1] = &quot;15&quot;;
    • After Refactoring:
    • Performance row = new Performance();
    • row.setName(&quot;Liverpool&quot;);
    • row.setWins(&quot;15&quot;);
  • 43. Replace Magic Number with symbolic Constant
    • You have a literal number with a particular meaning. Create a constant, name it after the meaning, and replace the number with it.
    • double potentialEnergy(double mass, double height) {
    • return mass * 9.81 * height;
    • }
    • double potentialEnergy(double mass, double height) {
    • return mass * GRAVITATIONAL_CONSTANT * height;
    • }
    • static final double GRAVITATIONAL_CONSTANT = 9.81;
  • 44. Encapsulate Field
    • There is a public field. Make it private and provide accessors.
    • public String _name
    • After Refactoring:
    • private String _name;
    • public String getName() {return _name;}
    • public void setName(String arg) {_name = arg;}
  • 45. Encapsulate Collection
    • A method returns a collection. Make it return a read-only view and provide add/remove methods.
    • Motivation
    • Often a class contains a collection of instances. This collection might be an array, list, set, or vector. Such cases often have the usual getter and setter for the collection.
  • 46. Decompose Conditional
    • You have a complicated conditional (if-then-else) statement. Extract methods from the condition, then part, and else parts.
    • if (date.before (SUMMER_START) || date.after(SUMMER_END))
    • charge = quantity * _winterRate + _winterServiceCharge;
    • else
    • charge = quantity * _summerRate;
    • After Refactoring:
    • if (notSummer(date))
    • charge = winterCharge(quantity);
    • else
    • charge = summerCharge (quantity);
  • 47. Consolidate Conditional Expression
    • You have a sequence of conditional tests with the same result. Combine them into a single conditional expression and extract it.
    • double disabilityAmount() {
    • if (_seniority < 2) return 0;
    • if (_monthsDisabled > 12) return 0;
    • if (_isPartTime) return 0;
    • After Refactoring:
    • // compute the disability amount
    • double disabilityAmount() {
    • if (isNotEligableForDisability()) return 0;
  • 48. Consolidate Duplicate Conditional Fragments
    • The same fragment of code is in all branches of a conditional expression. Move it outside of the expression
    if (isSpecialDeal()) { total = price * 0.95; send(); } else { total = price * 0.98; send(); } After Refactoring if (isSpecialDeal()) total = price * 0.95; else total = price * 0.98; send();
  • 49. Remove Control Flag
    • You have a variable that is acting as a control flag for a series of boolean expressions. Use a break or return instead
  • 50. Replace Nested Conditional with Gard Classes
    • A method has conditional behavior that does not make clear the normal path of execution. Use guard clauses for all the special cases
  • 51. double getPayAmount() { double result; if (_isDead) result = deadAmount(); else { if (_isSeparated) result = separatedAmount(); else { If (_isRetired) result = retiredAmount(); else result = normalPayAmount(); }; } return result; }; After Refactoring : double getPayAmount() { if (_isDead) return deadAmount(); if (_isSeparated) return separatedAmount(); if (_isRetired) return retiredAmount(); return normalPayAmount(); };
  • 52. Rename Method
    • The name of a method does not reveal its purpose. Change the name of the method
  • 53. Separate Query from Modifier
    • You have a method that returns a value but also changes the state of an object. Create two methods, one for the query and one for the modification.
  • 54. Parameterized Method
    • Several methods do similar things but with different values contained in the method body. Create one method that uses a parameter for the different values.
  • 55. Introduce Parameter to Object
    • You have a group of parameters that naturally go together. Replace them with an object.
  • 56. Remove Setting Methods
    • A field should be set at creation time and never altered. Remove any setting method for that field.
    • Providing a setting method indicates that a field may be changed. If you don't want that field to change once the object is created, then don't provide a setting method (and make the field final). That way your intention is clear and you often remove the very possibility that the field will change.
  • 57. Hide Method
    • A method is not used by any other class. Make the method private
  • 58. Pull up Field
    • Two subclasses have the same field. Move the field to the superclass.
  • 59. Pull up Method
    • Two subclasses have the same field. Move the field to the superclass.
  • 60. Push Down Method
    • Behavior on a superclass is relevant only for some of its subclasses. Move it to those subclasses
  • 61. Extract Subclass
    • A class has features that are used only in some instances. Create a subclass for that subset of features
  • 62. Extract Superclass
    • You have two classes with similar features. Create a superclass and move the common features to the superclass.
  • 63. Collapse Hierarchy
    • A superclass and subclass are not very different. Merge them together.
  • 64. Refactoring and Performance
    • To make the software easier to understand, you often make changes that will cause the program to run more slowly.
  • 65. References
    • Refactoring Improving the Design of Existing Code , Martin Fowler
    • www.refactoring.com
    • www.martinfowler.com
  • 66. Questions
    • ?