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

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 205: Line 205:


===5. Merging/Reduction of code===
===5. Merging/Reduction of code===
====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:
[[Image:removeMiddleMan.gif]]
====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.





Revision as of 21:45, 8 September 2009

Categorization of code 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

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.

Improve names

  • 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()
  • 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 

Could 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

Could 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.
    • 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 )
{
  // 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
}

2. Code management and restructuring

Consolidate Conditional Expressions

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

Consolidate Duplicate Conditional

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;
if (isSpecialDeal()) 
   total = price * 0.95;
else
   total = price * 0.98;
send();

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

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);

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;
   for (int i = 0; i < people.length; i++) {
         averageAge += people[i].age;
   }
   averageAge = averageAge / people.length;
   System.out.println(averageAge);
   System.out.println(totalSalary);
}


3. Allow abstraction

4. Change of logic

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:

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

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:

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.


Conclusion

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