CSC/ECE 517 Fall 2009/wiki1a 8 rr: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
 
(68 intermediate revisions by 3 users not shown)
Line 1: Line 1:
'''''Refactor (verb)''': to restructure software by applying a series of refactorings without changing its observable behavior.''
'''''Categorization''' is the process in which ideas and objects are recognized, differentiated and understood.''
=Categorization of code refactoring=
=Categorization of code refactoring=
Refactoring techniques can be catagorized in various ways . The different catagorizations of refactoring may include using [http://wiki.java.net/bin/view/People/SmellsToRefactorings smells] , [http://sourcemaking.com/refactoring functionality] or many other parameters .The following catagorization of refactoring techniques gives a new approach which emphasizes on changes in code that will aid the user.The list consists of the most relevant and used techniques for refactoring.


==What is Code Refactoring?==
==What is Code Refactoring?==
[http://en.wikipedia.org/wiki/Code_refactoring Code Refactoring] is a technique of reorganization code to change its structure but at the same time preserving the basic functionality of the code. There are many [http://refactoring.com/catalog/index.html kinds of code refactoring]. This wiki tries to categorize these types of refactoring so that it's easy for people to learn these patterns and remember them.
[http://en.wikipedia.org/wiki/Code_refactoring Code Refactoring] is a technique of reorganization code to change its structure but at the same time preserving the basic functionality of the code. There are many [http://refactoring.com/catalog/index.html kinds of code refactoring]. This wiki tries to categorize these types of refactoring so that it's easy for people to learn these patterns and remember them.


==Categories of Code refactoring==
==Categories of code refactoring==
The Code refactoring can be categorized as:
The Code refactoring can be categorized as:
:*Improve readability and code reuse
::*Improve readability and code reuse
:*Code management and restructuring
::*Code management and restructuring
:*Encapsulation
::*Encapsulation
:*Change of logic
::*Change of logic
:*Merging/Reduction of code
::*Merging/Reduction of code


The explanation to the above categories with examples are as follows:
The explanation to the above categories with examples are as follows:


===1. Improve readability and code reuse===
===1. Improve readability and code reuse===
::''"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." - Fowler''
:: ''"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." - Fowler''


One of the major motivation for code refactoring is readability/understanding of the code. By redefining names, moving code to relevant locations the structure can be made more readable.  
One of the major motivation for code refactoring is readability/understanding of the code. By redefining names, moving code to relevant locations the structure can be made more readable.  


====Improve names====
====Improve naming conventions====
*Renaming methods/variables etc to more meaningful names is a good practice. The method name should tell the reader what exactly the method does. Or a variable name should give a clear picture of what the variable stores.
*[http://sourcemaking.com/refactoring/rename-method '''Renaming'''] methods/variables etc to more meaningful names is a good practice. The method name should tell the reader what exactly the method does. Or a variable name should give a clear picture of what the variable stores.
   display()  => displayStudentNames()
   display()  => displayStudentNames()
*Extract a sub package depending on their gross dependencies or usages. This is to make the code more flexible. For example
*A package either has too many classes to be easily understandable .[http://sourcemaking.com/refactoring/extract-method '''Extract'''] a sub package depending on their gross dependencies or usages. This is to make the code more flexible. For example
  interface org.davison.data.DataProvider
  interface org.davison.data.DataProvider
  class org.davison.data.DataFactory
  class org.davison.data.DataFactory
Line 32: Line 36:
  class org.davison.data.DataFactory
  class org.davison.data.DataFactory
  // Database classes
  // Database classes
  class org.davison.data.jdbc.JDBCProvider
  class org.davison.data.'''jdbc'''.JDBCProvider
  class org.davison.data.jdbc.JDBCHelper
  class org.davison.data.'''jdbc'''.JDBCHelper
:This holds good for method/class/interface also
:This holds good for method/class/interface also
====Change location of code====
====Change location of code====
*Always try to keep the class in relevant package. If it does not fit into any existing package then create a new package. This starts to make sense when this part of the code gets re-used in other parts of the project. This applies to class/method/field
*Always try to keep the class in relevant [http://en.wikipedia.org/wiki/Java_package '''package''']. If it does not fit into any existing package then create a new package. This starts to make sense when this part of the code gets re-used in other parts of the project. This applies to class/method/field
  class org.davison.ui.TextThing
  class org.davison.ui.TextThing
  class org.davison.ui.TextProcessor
  class org.davison.ui.TextProcessor
  class org.davison.log.Logger
  class org.davison.log.Logger
  //depends on
  //depends on
  class org.davison.ui.StringUtil
  class org.davison.'''ui'''.StringUtil


''Can be modified to''
''Can be modified to''
Line 49: Line 54:
  class org.davison.log.Logger
  class org.davison.log.Logger
  //depends on
  //depends on
  class org.davison.util.StringUtil
  class org.davison.'''util'''.StringUtil


*Pull up or Push down (method/fields)
*'''Pull up or Push down (method/fields)'''
:Pull the method/fields up to the superclass as shown in this [http://www.refactoring.com/catalog/pullUpField.html example] or push the method/fields to the subclass when needed as shown in this [http://www.refactoring.com/catalog/pushDownField.html example].  
:Pull the method/fields up to the superclass as shown in this [http://www.refactoring.com/catalog/pullUpField.html example] or push the method/fields to the subclass when needed as shown in this [http://www.refactoring.com/catalog/pushDownField.html example].  


*Some other methods include  
*'''Some other methods include'''
**Add parameter - The main motivation here is when the method needs more information by the caller. Also Remove parameter can be used to remove parameters which the function no longer wants.
**[http://sourcemaking.com/refactoring/add-parameter '''Add parameter'''] - The main motivation here is when the method needs more information by the caller. Also [http://sourcemaking.com/refactoring/remove-parameter '''Remove parameter'''] can be used to remove parameters which the function no longer wants.
** Introduce explaining variable - This can be explained with the below example
 
We added a parameter to getContact initially when required..
[[Image:10fig03.gif]]
 
But we  remove the parameter ''Date'' if it is not required.
 
** [http://sourcemaking.com/refactoring/introduce-explaining-variable '''Introduce explaining variable'''] - This can be explained with the below example
  if ((platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0 )
  if ((platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0 )
  {
  {
Line 69: Line 80:
   // do something
   // do something
  }
  }
** [http://sourcemaking.com/refactoring/move-method '''Move - (Methods , Objects , Fields)'''] - By moving Objects , Methods , Fields from one class to another , where its behavior is more represented by the class or if classes have too much behavior coupled with them.
[[Image:07fig01.gif]]
===2. Code management and restructuring===
===2. Code management and restructuring===
Management of code is a major part of refactoring. Many a times, code has a lot of logical parts written together. By restructuring pieces of code we can ensure better working of code.  
Management of code is a major part of refactoring. Many a times, code has a lot of logical parts written together. By restructuring pieces of code we can ensure better working of code.  


====Consolidate Conditional Expressions====
====Consolidate Conditional Expressions [http://sourcemaking.com/refactoring/consolidate-conditional-expression ] ====  
When there are a series of conditional statements to check and all these checks will finally boil down to the same action then all the conditional statements need to be combined into a single method. This action will replace the ''what you are doing'' with ''why you are doing''.
When there are a series of conditional statements to check and all these checks will finally boil down to the same action then all the conditional statements need to be combined into a single method. This action will replace the ''what you are doing'' with ''why you are doing''.
For Example
For Example
Line 84: Line 99:


  double disabilityAmount() {
  double disabilityAmount() {
     if (isNotEligableForDisability()) return 0;
     if ('''isNotEligableForDisability()''') return 0;
  // compute the disability amount
  // compute the disability amount


====Consolidate Duplicate Conditional====
isNotEligableForDisability() computes -seniority,Months Disabled and Part Time.
 
====Consolidate Duplicate Conditional [http://sourcemaking.com/refactoring/consolidate-duplicate-conditional-fragments]====  
Whenever there is same code being written in all the legs of the conditional statements then this set of code can be taken as a common code and executed before or after the conditional statement. However we need to preserve the original way the code was executed. If the common code was executed at the beginning then move it to before the conditional else at the end. For Example
Whenever there is same code being written in all the legs of the conditional statements then this set of code can be taken as a common code and executed before or after the conditional statement. However we need to preserve the original way the code was executed. If the common code was executed at the beginning then move it to before the conditional else at the end. For Example
  if (isSpecialDeal()) {
  if (isSpecialDeal()) {
Line 101: Line 118:
''Can be modified as''
''Can be modified as''


  price = cost;
  '''price = cost;''' // Cost moves up
  if (isSpecialDeal())  
  if (isSpecialDeal())  
     total = price * 0.95;
     total = price * 0.95;
  else
  else
     total = price * 0.98;
     total = price * 0.98;
  send();
  '''send();'''      // Send moves down


====Decompose Conditional====
====Decompose Conditional [http://sourcemaking.com/refactoring/decompose-conditional]====  
Whenever there is complex conditional statements, ''decompose'' and replace the chunks of code with method calls. This is simple to do. Just pull the ''then'' part and ''else'' into separate methods. For Example
Whenever there is complex conditional statements, ''decompose'' and replace the chunks of code with method calls. This is simple to do. Just pull the ''then'' part and ''else'' into separate methods. For Example
  if (date.before (SUMMER_START) || date.after(SUMMER_END))
  if (date.before (SUMMER_START) || date.after(SUMMER_END))
Line 115: Line 132:
     charge = quantity * _summerRate;
     charge = quantity * _summerRate;
''Can be modified as''
''Can be modified as''
  if (notSummer(date))
  if ('''notSummer(date)''')
     charge = winterCharge(quantity);
     charge = '''winterCharge(quantity)''';
  else  
  else  
     charge = summerCharge (quantity);
     charge = '''summerCharge (quantity)''';
 
These Methods compute the corresponding values.


====Split Loop====
====Split Loop====
Line 126: Line 145:
     double averageAge = 0;
     double averageAge = 0;
     double totalSalary = 0;
     double totalSalary = 0;
    for (int i = 0; i < people.length; i++) {
  '''  for (int i = 0; i < people.length; i++) {
       averageAge += people[i].age;
       averageAge += people[i].age;
       totalSalary += people[i].salary;
       totalSalary += people[i].salary;
     }
     }'''
     averageAge = averageAge / people.length;
     averageAge = averageAge / people.length;
     System.out.println(averageAge);
     System.out.println(averageAge);
Line 138: Line 157:
  void printValues() {
  void printValues() {
     double totalSalary = 0;
     double totalSalary = 0;
     for (int i = 0; i < people.length; i++) {
     '''for (int i = 0; i < people.length; i++)''' {
       totalSalary += people[i].salary;
       totalSalary += people[i].salary;
     }
     }
     double averageAge = 0;
     double averageAge = 0;
     for (int i = 0; i < people.length; i++) {
//------------Loop Split Here ------------------
     '''for (int i = 0; i < people.length; i++)''' {
           averageAge += people[i].age;
           averageAge += people[i].age;
     }
     }
Line 150: Line 173:
  }
  }


====Replace Conditional with Polymorphism====
====Replace Conditional with Polymorphism [http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism]====  
Whenever [http://en.wikipedia.org/wiki/Conditional_(programming) conditional statements] are encountered, replace it with polymorphism. The advantage is that if the same set of condition is repeated in many places. If you want to add a new type, you have to find and update all the places where the conditionals are used. But with subclasses, you need to just create a new subclass and provide the appropriate methods. Example
Whenever [http://en.wikipedia.org/wiki/Conditional_(programming) conditional statements] are encountered, replace it with [http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming polymorphism]. The advantage is that if the same set of condition is repeated in many places. If you want to add a new type, you have to find and update all the places where the conditionals are used. But with subclasses, you need to just create a new subclass and provide the appropriate methods. Example


  double getSpeed() {
  double getSpeed() {
     switch (_type) {
     switch (_type) {
       case EUROPEAN:
       case EUROPEAN:
           return getBaseSpeed();
           return''' getBaseSpeed()''';
       case AFRICAN:
       case AFRICAN:
           return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts;
           return '''getBaseSpeed() - getLoadFactor() * _numberOfCoconuts''';
       case NORWEGIAN_BLUE:
       case NORWEGIAN_BLUE:
           return (_isNailed) ? 0 : getBaseSpeed(_voltage);
           return '''(_isNailed) ? 0 : getBaseSpeed(_voltage)''';
     }
     }
     throw new RuntimeException ("Should be unreachable");
     throw new RuntimeException ("Should be unreachable");
Line 169: Line 192:
[[Image:repCondWithPoly.gif]]
[[Image:repCondWithPoly.gif]]


===3. Encapsulation===
Here depending on the object , the conditional method is triggered in the super class .
====Encapsulate Field====
All the different returns of condition are now methods in the subclass.
 
===3. Encapsulation[http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)]===
====Encapsulate Field[http://sourcemaking.com/refactoring/encapsulate-field]====
Encapsulation/data hiding is an important concept of object orientation. According to this concept, never make a data public. This is for the simple reason that if they are public, other objects can easily access them and modify them. So by adding accessors  we can achieve this.
Encapsulation/data hiding is an important concept of object orientation. According to this concept, never make a data public. This is for the simple reason that if they are public, other objects can easily access them and modify them. So by adding accessors  we can achieve this.
For example:
For example:
Line 181: Line 207:
  public void setName(String arg) {_name = arg;}
  public void setName(String arg) {_name = arg;}


====Encapsulate Downcast====
====Encapsulate Downcast[http://sourcemaking.com/refactoring/encapsulate-downcast]====
If you return a value from a method and you know the type of what you returned is more specialized than what the method signature says, you are putting unnecessary work on the called function. Rather than forcing the method to do a downcast, you shaould always provide them with the most specific type you can. For example:
If you return a value from a method and you know the type of what you returned is more specialized than what the method signature says, you are putting unnecessary work on the called function. Rather than forcing the method to do a downcast, you should always provide them with the most specific type you can. For example:


  Object lastReading() {
  Object lastReading() {
Line 188: Line 214:
  }
  }
''Can be modified as''
''Can be modified as''
  Reading lastReading() {
  '''Reading''' lastReading() {
     return (Reading) readings.lastElement();
     return '''(Reading)''' readings.lastElement();
  }
  }
If you return a value from a method,You know the type of what is returned is more specialized than what the method signature says, you are putting unnecessary work on your clients. Rather than forcing them to do the downcasting, you should always provide them with the most specific type you can.


===4. Change of logic===
===4. Change of logic===
The logic of the code can be improved from time to time. As you gain more understanding of the code, logic can be tweaked for better performance.
The logic of the code can be improved from time to time. As you gain more understanding of the code, logic can be tweaked for better performance.


====Substitute Algorithm====
====Substitute Algorithm[http://sourcemaking.com/refactoring/substitute-algorithm]====
When you find a simpler/clearer algorithm to solve a particular problem replace it. Generally this happens when you dive deeper into the problem and have a clearer understanding of the problem. For example:
When you find a simpler/clearer algorithm to solve a particular problem replace it. Generally this happens when you dive deeper into the problem and have a clearer understanding of the problem. For example:


Line 259: Line 287:
Merging of code that are dependent of each other reduces duplicaton and complexity of the code . Unused methods and unused classes should be removed.  
Merging of code that are dependent of each other reduces duplicaton and complexity of the code . Unused methods and unused classes should be removed.  


====Remove Middle Man====
====Remove Middle Man[http://sourcemaking.com/refactoring/remove-middle-man]====
When there is an encapsulation of a delegated object, everytime the client wants to use the feature of the delegate, a delegating method has to be added to the server. This could become a pain after a while. So a fix for this is to create an accessor for the delegate, then for each client use of a delegate method, remove the method from the server and replace the call in the client to call the method of delegate. An example is as shown below:
When there is an encapsulation of a delegated object, everytime the client wants to use the feature of the delegate, a delegating method has to be added to the server. This could become a pain after a while. So a fix for this is to create an accessor for the delegate, then for each client use of a delegate method, remove the method from the server and replace the call in the client to call the method of delegate.every time the client wants to use a new feature of the delegate, you have to add a simple delegating method to the server. After adding features for a while, it becomes painful. The server class is just a middle man, and perhaps it’s time for the client to call the delegate directly. An example is as shown below:


[[Image:removeMiddleMan.gif]]
[[Image:removeMiddleMan.gif]]


====Remove Setting Method====
====Remove Setting Method[http://sourcemaking.com/refactoring/remove-setting-method]====
When a variable is created and the programmer knows that the variable is going to change through the course of the program a setting method is a good idea. But if the programmer is aware that a variable is not going to be changed during the course of the program, then a setting method should not be created in the first place. This will give a clear idea about the intention of the creator of the variable.
When a variable is created and the programmer knows that the variable is going to change through the course of the program a setting method is a good idea. But if the programmer is aware that a variable is not going to be changed during the course of the program, then a setting method should not be created in the first place. This will give a clear idea about the intention of the creator of the variable.
[[Image:Examp.gif]]


====Preserve Whole Objects====
====Preserve Whole Objects[http://sourcemaking.com/refactoring/preserve-whole-object]====
When several data values are being passed to a method and if all these values are from the same object, then there is a problem. This problem can be fixed by passing the whole object from which the values are obtained. This will see to it that the parameter list is much more robust. As in, if the method requires more data values then the code needs to be changed in the calling method as well. But if the entire object is passed as parameter, then the called function can get any data value readily. Example:
When several data values are being passed to a method and if all these values are from the same object, then the problem with this is that if the called object needs new data values later, you have to find and change all the calls to this method. This problem can be fixed by passing the whole object from which the values are obtained. This will see to it that the parameter list is much more robust. As in, if the method requires more data values then the code needs to be changed in the calling method as well. But if the entire object is passed as parameter, then the called function can get any data value readily. Example:
  int low = daysTempRange.getLow();
  int low = '''daysTempRange'''.getLow();
  int high = daysTempRange.getHigh();
  int high = '''daysTempRange'''.getHigh();
  withinPlan = plan.withinRange(low, high);  
  withinPlan = plan.withinRange('''low, high''');  


''Can be modified as''   
''Can be modified as''   


  withinPlan = plan.withinRange(daysTempRange);
  withinPlan = plan.withinRange('''daysTempRange''');


==Conclusion==
==Conclusion==
Line 281: Line 310:


==References==
==References==
#[http://www.refactoring.com/ Martin Fowler's homepage about refactoring]
#[http://www.refactoring.com/ Martin Fowler's homepage about refactoring]
# [http://industriallogic.com/papers/smellstorefactorings.pdf Smells to Refactorings Quick Reference Guide]
# [http://industriallogic.com/papers/smellstorefactorings.pdf Smells to Refactorings Quick Reference Guide]
#[http://books.google.com/books?id=1MsETFPD3I0C&printsec=titlepage&dq=fowler+refactoring&source=gbs_toc_s&cad=1#PPP1,M1 Refactoring: Improving the Design of Existing Code] by Martin Fowler available at Google Books
#[http://books.google.com/books?id=1MsETFPD3I0C&printsec=titlepage&dq=fowler+refactoring&source=gbs_toc_s&cad=1#PPP1,M1 Refactoring: Improving the Design of Existing Code] by Martin Fowler available at Google Books
#[http://www.artima.com/intv/refactorP.html Refactoring with Martin Fowler]
#[http://www.artima.com/intv/refactorP.html Refactoring with Martin Fowler]
#[http://sourcemaking.com/refactoring Refactoring : SourceMaking.com]

Latest revision as of 04:57, 24 September 2009

Refactor (verb): to restructure software by applying a series of refactorings without changing its observable behavior.

Categorization is the process in which ideas and objects are recognized, differentiated and understood.

Categorization of code refactoring

Refactoring techniques can be catagorized in various ways . The different catagorizations of refactoring may include using smells , functionality or many other parameters .The following catagorization of refactoring techniques gives a new approach which emphasizes on changes in code that will aid the user.The list consists of the most relevant and used techniques for refactoring.

What is Code Refactoring?

Code Refactoring is a technique of reorganization code to change its structure but at the same time preserving the basic functionality of the code. There are many kinds of code refactoring. This wiki tries to categorize these types of refactoring so that it's easy for people to learn these patterns and remember them.

Categories of code refactoring

The Code refactoring can be categorized as:

  • Improve readability and code reuse
  • Code management and restructuring
  • Encapsulation
  • Change of logic
  • Merging/Reduction of code

The explanation to the above categories with examples are as follows:

1. Improve readability and code reuse

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." - Fowler

One of the major motivation for code refactoring is readability/understanding of the code. By redefining names, moving code to relevant locations the structure can be made more readable.

Improve naming conventions

  • Renaming methods/variables etc to more meaningful names is a good practice. The method name should tell the reader what exactly the method does. Or a variable name should give a clear picture of what the variable stores.
 display()  => displayStudentNames()
  • A package either has too many classes to be easily understandable .Extract a sub package depending on their gross dependencies or usages. This is to make the code more flexible. For example
interface org.davison.data.DataProvider
class org.davison.data.DataFactory
// Database classes
class org.davison.data.JDBCProvider
class org.davison.data.JDBCHelper 

Can be modified to

interface org.davison.data.DataProvider
class org.davison.data.DataFactory
// Database classes
class org.davison.data.jdbc.JDBCProvider
class org.davison.data.jdbc.JDBCHelper
This holds good for method/class/interface also

Change location of code

  • Always try to keep the class in relevant package. If it does not fit into any existing package then create a new package. This starts to make sense when this part of the code gets re-used in other parts of the project. This applies to class/method/field
class org.davison.ui.TextThing
class org.davison.ui.TextProcessor
class org.davison.log.Logger
//depends on
class org.davison.ui.StringUtil

Can be modified to

class org.davison.ui.TextThing
class org.davison.ui.TextProcessor
class org.davison.log.Logger
//depends on
class org.davison.util.StringUtil
  • Pull up or Push down (method/fields)
Pull the method/fields up to the superclass as shown in this example or push the method/fields to the subclass when needed as shown in this example.
  • Some other methods include
    • Add parameter - The main motivation here is when the method needs more information by the caller. Also Remove parameter can be used to remove parameters which the function no longer wants.

We added a parameter to getContact initially when required..

But we remove the parameter Date if it is not required.

if ((platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0 )
{
  // do something
}

Can be modified to

final boolean isMacOs     = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE")  > -1;
final boolean wasResized  = resize > 0;
if (isMacOs && isIEBrowser && wasInitialized() && wasResized)
{
  // do something
}
    • Move - (Methods , Objects , Fields) - By moving Objects , Methods , Fields from one class to another , where its behavior is more represented by the class or if classes have too much behavior coupled with them.

2. Code management and restructuring

Management of code is a major part of refactoring. Many a times, code has a lot of logical parts written together. By restructuring pieces of code we can ensure better working of code.

Consolidate Conditional Expressions [1]

When there are a series of conditional statements to check and all these checks will finally boil down to the same action then all the conditional statements need to be combined into a single method. This action will replace the what you are doing with why you are doing. For Example

double disabilityAmount() {
   if (_seniority < 2) return 0;
   if (_monthsDisabled > 12) return 0;
   if (_isPartTime) return 0;
   // compute the disability amount

Can be modified to

double disabilityAmount() {
   if (isNotEligableForDisability()) return 0;
// compute the disability amount

isNotEligableForDisability() computes -seniority,Months Disabled and Part Time.

Consolidate Duplicate Conditional [2]

Whenever there is same code being written in all the legs of the conditional statements then this set of code can be taken as a common code and executed before or after the conditional statement. However we need to preserve the original way the code was executed. If the common code was executed at the beginning then move it to before the conditional else at the end. For Example

if (isSpecialDeal()) {
   price = cost;
   total = price * 0.95;
   send();
}
else {
   price = cost;
   total = price * 0.98;
   send();
}

Can be modified as

price = cost; // Cost moves up
if (isSpecialDeal()) 
   total = price * 0.95;
else
   total = price * 0.98;
send();      // Send moves down

Decompose Conditional [3]

Whenever there is complex conditional statements, decompose and replace the chunks of code with method calls. This is simple to do. Just pull the then part and else into separate methods. For Example

if (date.before (SUMMER_START) || date.after(SUMMER_END))
   charge = quantity * _winterRate + _winterServiceCharge;
else 
   charge = quantity * _summerRate;

Can be modified as

if (notSummer(date))
   charge = winterCharge(quantity);
else 
   charge = summerCharge (quantity);

These Methods compute the corresponding values.

Split Loop

When you encounter a loop which is doing more than one thing it's a good idea to split them into more than one loop and execute the loop more than once to do different things in different loops. One may wonder that this would hit the performance of the system. But this method actually increases the performance. In data intensive applications, when two different arrays are being accessed in the same loop you can get hit badly by the cache misses. By splitting the loops into separate entities/loops, that act on only one array at a time we get considerable performance boost. Let's look at an example:

void printValues() {
   double averageAge = 0;
   double totalSalary = 0;
   for (int i = 0; i < people.length; i++) {
      averageAge += people[i].age;
      totalSalary += people[i].salary;
   }
   averageAge = averageAge / people.length;
   System.out.println(averageAge);
   System.out.println(totalSalary);
}

Can be modified as

void printValues() {
   double totalSalary = 0;

   for (int i = 0; i < people.length; i++) {
      totalSalary += people[i].salary;
   }
   double averageAge = 0;

//------------Loop Split Here ------------------

   for (int i = 0; i < people.length; i++) {
         averageAge += people[i].age;
   }
   averageAge = averageAge / people.length;
   System.out.println(averageAge);
   System.out.println(totalSalary);
}

Replace Conditional with Polymorphism [4]

Whenever conditional statements are encountered, replace it with polymorphism. The advantage is that if the same set of condition is repeated in many places. If you want to add a new type, you have to find and update all the places where the conditionals are used. But with subclasses, you need to just create a new subclass and provide the appropriate methods. Example

double getSpeed() {
   switch (_type) {
      case EUROPEAN:
         return getBaseSpeed();
      case AFRICAN:
         return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts;
      case NORWEGIAN_BLUE:
         return (_isNailed) ? 0 : getBaseSpeed(_voltage);
   }
   throw new RuntimeException ("Should be unreachable");
}

Can be modified to look like this

Here depending on the object , the conditional method is triggered in the super class . All the different returns of condition are now methods in the subclass.

3. Encapsulation[5]

Encapsulate Field[6]

Encapsulation/data hiding is an important concept of object orientation. According to this concept, never make a data public. This is for the simple reason that if they are public, other objects can easily access them and modify them. So by adding accessors we can achieve this. For example:

public String _name

Can be modified as

private String _name;
public String getName() {return _name;}
public void setName(String arg) {_name = arg;}

Encapsulate Downcast[7]

If you return a value from a method and you know the type of what you returned is more specialized than what the method signature says, you are putting unnecessary work on the called function. Rather than forcing the method to do a downcast, you should always provide them with the most specific type you can. For example:

Object lastReading() {
   return readings.lastElement();
}

Can be modified as

Reading lastReading() {
   return (Reading) readings.lastElement();
}

If you return a value from a method,You know the type of what is returned is more specialized than what the method signature says, you are putting unnecessary work on your clients. Rather than forcing them to do the downcasting, you should always provide them with the most specific type you can.

4. Change of logic

The logic of the code can be improved from time to time. As you gain more understanding of the code, logic can be tweaked for better performance.

Substitute Algorithm[8]

When you find a simpler/clearer algorithm to solve a particular problem replace it. Generally this happens when you dive deeper into the problem and have a clearer understanding of the problem. For example:

String foundPerson(String[] people){
   for (int i = 0; i < people.length; i++) {
      if (people[i].equals ("Don")){
         return "Don";
      }
      if (people[i].equals ("John")){
         return "John";
      }
      if (people[i].equals ("Kent")){
         return "Kent";
      }
   }
   return "";
}

Can be modified as

String foundPerson(String[] people){
   List candidates = Arrays.asList(new String[] {"Don", "John", "Kent"});
   for (int i=0; i<people.length; i++)
      if (candidates.contains(people[i]))
         return people[i];
      return "";
}

Replace Iteration with Recursion

A problem with some loops is that it is difficult to work out what each iteration is doing. And without comments it becomes extremely difficult to analyze. A solution to this is to replace the iteration with recursion. Unlike most procedural looping constructs, a recursive function call can be given a meaningful name. An example of this kind of factoring is as below:

unsigned greatest_common_divisor (unsigned a, unsigned b) {
   while (a != b)
   {
      if (a > b) {
         a -= b;
      }
      else if (b > a) {
         b -= a;
      }
   }
}

Can be modified as

unsigned greatest_common_divisor (unsigned a, unsigned b) {
   if (a > b)
   {
      return greatest_common_divisor ( a-b, b );
   }
   else if (b > a)
   {
      return greatest_common_divisor ( a, b-a );
   }
   else // (a == b)
   {
      return a; 
   }
} 


5. Merging/Reduction of code

Merging of code that are dependent of each other reduces duplicaton and complexity of the code . Unused methods and unused classes should be removed.

Remove Middle Man[9]

When there is an encapsulation of a delegated object, everytime the client wants to use the feature of the delegate, a delegating method has to be added to the server. This could become a pain after a while. So a fix for this is to create an accessor for the delegate, then for each client use of a delegate method, remove the method from the server and replace the call in the client to call the method of delegate.every time the client wants to use a new feature of the delegate, you have to add a simple delegating method to the server. After adding features for a while, it becomes painful. The server class is just a middle man, and perhaps it’s time for the client to call the delegate directly. An example is as shown below:

Remove Setting Method[10]

When a variable is created and the programmer knows that the variable is going to change through the course of the program a setting method is a good idea. But if the programmer is aware that a variable is not going to be changed during the course of the program, then a setting method should not be created in the first place. This will give a clear idea about the intention of the creator of the variable.

Preserve Whole Objects[11]

When several data values are being passed to a method and if all these values are from the same object, then the problem with this is that if the called object needs new data values later, you have to find and change all the calls to this method. This problem can be fixed by passing the whole object from which the values are obtained. This will see to it that the parameter list is much more robust. As in, if the method requires more data values then the code needs to be changed in the calling method as well. But if the entire object is passed as parameter, then the called function can get any data value readily. Example:

int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
withinPlan = plan.withinRange(low, high);										 

Can be modified as

withinPlan = plan.withinRange(daysTempRange);

Conclusion

There are a lot of refactoring methods mentioned in the catalog which the authors of this wiki believe can be classified into the above categories. Source of the examples used in this wiki can be found here.

References

  1. Martin Fowler's homepage about refactoring
  2. Smells to Refactorings Quick Reference Guide
  3. Refactoring: Improving the Design of Existing Code by Martin Fowler available at Google Books
  4. Refactoring with Martin Fowler
  5. Refactoring : SourceMaking.com