CSC/ECE 517 Fall 2009/wiki1a 7 sm

From Expertiza_Wiki
Jump to navigation Jump to search

Categorization of refactoring

Problem Statement

The list of refactoring has become quite long. But people don't remember long lists very well. To promote learning the different patterns, they should be categorized in some way, or perhaps, along various dimensions. Take the list of refactoring available at refactoring.org (and/or elsewhere) and categorize them along as many different dimensions as makes sense. Write up a description of the refactoring landscape.

What is Refactoring?

Refactoring is the process of modifying/rewriting the code of a computer program to change its internal structure without changing its external (functional) behavior in order to improve its internal or external software quality attributes. In simple terms, it is used to improve its readability, reusability or structure without affecting its meaning or behavior.

Why is refactoring required?

If a program code is poorly designed it will be difficult to understand it and therefore maintain it. This fact is made worse for commercial programs which are huge and much more complex to write. These commercial programs are most of the times created and maintained by more than one programmer and have a much greater life span than smaller programs. So these codes should be easy to understand and as a result easy to modify when the need arises. But if the design of the code is not optimum then understanding the code and hence changing/adding new features can be very complex for someone who has not written the code. Moreover the changes may be detrimental to the code. For example if there is high coupling then changing the code at one place may adversely affect the code at some other place. If correct coding practices are not followed then that may lead to security concerns.The main reasons for refactoring are as follows:

  • Improving the design of the code
  • Makes the code easier to understand
  • Makes debugging easier (since the code is easier to understand)
  • Code runs faster


Categorizing Refactoring Techniques According to Broad Goals

Refactoring techniques can be classified according to the broad goals they intend to achieve. The different categories are as follows.


Composing Methods

The refactoring methods coming under this basically deal with composing new methods to package the code properly. Most come from the method, class etc being too long and not representing its true functionality or trying to do a lot of things at once. So modularization is the key goal here.

Example

Before

void printDetails(Employee e)
{
 System.out.println(e.id);
 System.out.println(e.name);
 Address addr = e.getAddress();
 System.out.println(addr.street);
 System.out.println(addr.city);
 System.out.println(addr.zip);
 System.out.println(addr.state);
}

After

void printDetails(Employee e)
{
 System.out.println(e.id);
 System.out.println(e.name);
 printAddress(e.getAddress());
}
void printAddress(Address addr)
{
 System.out.println(addr.street);
 System.out.println(addr.city);
 System.out.println(addr.zip);
 System.out.println(addr.state);
}


Extract Method Code fragment which can be grouped together so create a new method whose name explains the purpose of the method.Used when a method is too long or when it starts needing comments to reveal its purpose (and so is not self explanatory). For additional information regarding this topic click here

Inline Method Used when a method’s body is as clear as the name of the method. Executed by putting the method's body into the body of its callers and remove the method.Sometimes the method body is so concise and clear that we would want to define the method in place and get rid of the method. For additional information regarding this topic click here

Introduce Explaining Variable If there is a complicated expression, put the result into a temporary variable with a meaninigful name.To make the expression and thus the code easier to understand. For additional information regarding this topic click here

Replace Temp With Query Instead of using a temporary variable to hold the result of an experession replace them with a method(query) and replace the expressions with the query method. This refactoring method is used as a preparation of the normal Extract Method refactoring method mainly because the existence of several temporary variables tend to make the ‘extracted’ method quite long since we have to pass them along to the extracted method. (But if the variable is assigned to more than once then consider the next method Split Temp Variable). For additional information regarding this topic click here

Split Temporary Variable If we 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.Some temporary variable (like ‘i’ in for loops) are just plalceholders for each iteration. On the other hand some temp variables are used to hold values of long expressions. Now if that variable is assigned again, it means that the variable more than one responsibility which is discouraged for a temporary variable. Any variable with more than one responsibility should be replaced with a temp for each responsibility. Using a temp for two different things is very confusing for the reader. For additional information regarding this topic click here

Remove Assignment To Parameter If the code assigns to a passed in parameter, use temp variable instead.In Java assigning to a parameter which is a primitive object (int, char etc, not objects) is fine because they are passed by reference. When objects are passed, the reference to the object is passed by value(not the copy of object, but copy of reference to original object). Hence if the reference is reassigned (e.g. swapping) that makes no change to original object but if the passed reference is used to reassign the member variables of the passed object, that may pose problems. Moreover any language which uses pass by reference will have problems if passed parameters are reassigned. For additional information regarding this topic click here

Replace Method With Method Object As we saw earlier, having a long list of local variable hinders the use of Extract Method. We overcame this earlier by Extract Temp with Query but sometimes breaking a method is not possible. So instead of breaking the method, we create an object out of the method so the local variables become fields which are now easily accessible. For additional information regarding this topic click here

Substitute Algorithm It is one of the simplest to execute in which an algorithm in a method is replaced with another set of more coherent and lucid algorithm just because we found a an easier algorithm for a set of code. For additional information regarding this topic click here


Moving features between objects

Object oriented programming is all about objects and their attributes and responsibilities. But creating object structure and deciding where to put the responsibility is tricky. So once created, need may arise due to more insight or due to testing of the classes that these attributes may need to be changed. These refactorings come under this category.

Example

Before

Class Employee
{
String name;
int officeAreaCode;
int officeNumber;
int residenceAreaCode;
int residenceNumber;
}

After

Class Employee
{
String name;
TelephoneNumber residencePhone;
TelephoneNumber officePhone;
}
Class TelephoneNumber
{
int areaCode;
int phoneNumber;
}  
 

Extract Class We have a class which has more responsibility than it should have (which may include both attributes and methods). So we can create a new class by taking out some features which can be separated from the existing class.Large sized classes are not easy to comprehend and lead us away from the concept of abstraction. For additional information regarding this topic click here

Hide Delegate (Opposite Of Remove Middle Man) The client should know and thus be very less dependent on the server code for execution. If a client calls a method defined on a field of the server directly then any changes which have to be made on this delegate has to be propogated to the client which is not desirable. So a delegating method can be added on server which limits the change to the client whenever a change is made to the server. For additional information regarding this topic click here

Inline Class (Opposite Of Extract Class) If a class is not doing much work it can be merged with a another class. This happens most of the times because of refactoring a class when the class is almost stripped off most of its functionalities. For additional information regarding this topic click here

Introduce Foreign Method Generally in commercial applications we are not provided with the server code but just the server API. We may sometimes need to add another method i.e. to add another service. If the method is used just once then it is acceptable to write it in the client side, but in case we need to use the method several times, then a method can be created on the client side for hat functionality. If we need just one or two methods then we use this refactor else we use the next one. For additional information regarding this topic click here

Introduce Local Extension This is a more general style of the “Introduce Foreign Method” and is used for exactly the same reasons. The only difference being that in case we need just one or two methods, we use the “Introduce Foreign Method”, else we use this method. Here we create a new class which contains these methods and make it a sub class of the original class. This helps in keeping all the methods together in one place. For additional information regarding this topic click here

Move Field This is used when a field in a class is used another class more than the class in which it was defined. Solution is to move the field in the class which uses it the most and make the appropriate changes in the methods which use that field. For additional information regarding this topic click here

Move Method It is similar to the “Move Field” refactor and is used if a method is used by another class more than the class in which it was defined. Solution is to move the method in the class which uses it the most and then either change the old method into a delegate or remove it. For additional information regarding this topic click here

Remove Middle Man (Opposite Of Hide Delegate) If the client is doing a lot of delegation work then whenever a feature is added in the server, a delegating method has to be added for that feature which is cumbersome of the additions tend to grow with time and the class seems to be doing delegating work more than it own. So in that case it is better to remove the delegating methods and make the client call the delegate directly. For additional information regarding this topic click here


Organizing Data

These set of refactoring methods are all about proper organization of the member functions and fields in an object oriented environment. They may initially seem unnecessary, for example getter() and setter() methods which not many use in short programs but in large commercial object orients code they go a long way in keeping the code well organized.

Example

Before

Class Order
{
int orderNumber;
String customerName;
String customerAddress;
int customerPhone;
}

After

Class Order
{
int orderNumber;
Customer customer;
}
Class Customer
{
String customerName;
String customerAddress;
int customerPhone;
}


Self Encapsulate Field Self encapsulating, which is in simple terms, adding getter() and setter() methods for the fields have a few advantages. In inheritance when the sub class wants to change the values of the base class with values computed in the sub class, these methods can be overridden. It is also used to implement lazy initialization (initializing the field only when needed to). For additional information regarding this topic clickhere

'Replace Data Value with Object In the early stages of program, the simple facts were represented as data items but as the program gets bigger need may arise to include more details. This is made easy if the data item is converted into an object which helps in adding much more detail about the data. For additional information regarding this topic click here

Change Value To Reference If a value object needs to be allowed to have changeable data and the change need to propagate to every item referring to it, then this refactoring should be used. For additional information regarding this topic click here

Change Reference To Value This is the opposite case from Change Value To Reference. If the reference value is immutable and small, it may as well be changed to a value object. For additional information regarding this topic click here

Replace Array With Object An array is supposed to hold similar object but if an array is being used to hold different types of items then it should probably be replaced with an object, i.e. a new class should be formed for that array. In array, the first item may be used to store name, second may be used to store address and so on. It may not be easy to remember this conventions and is not transparent. So replacing it with a class is recommended. For additional information regarding this topic click here

Duplicate Observed Data IN the case of GUI applications where domain data is there in a GUI control and if the domain methods need access to the domain data then we have to use this refactoring method. General method is to copy the data to a domain object and use observer to synchronize the data at the two locations. For additional information regarding this topic click here

Change Unidirectional Association To Bidirectional If we have 2 classes and BOTH need to use each other’s features, but only one class has a connection to the other. We need to add a connection from the other class to this class and change modifiers to reflect this. We need this because we may start with two classes in which one refers the other but over time find out that the referred class may now need to access to the objects that refer to it. For additional information regarding this topic click here

Change Bidirectional Association To Unidirectional In this case, one of the classes may no longer need to refer to the other class objects. In that case the unrequired association may be dropped. Bidirectional associations are complex to maintain because care has to be maintained that objects are properly created and destroyed. They also lead to zombie objects in which the object which was supposed to have been reclaimed was not because of a stray reference which was not cleared. For additional information regarding this topic click here

Replace Magic Number with Symbolic Constant If a literal has a specific meaning then it is wise to create a constant with appropriate name. For additional information regarding this topic click here

Encapsulate Field Change a public field to private and provide accessers instead. For encapsulation (data hiding). We do not want changes to be made to the data without the object’s knowledge. For additional information regarding this topic click here

Encapsulate Collection On similar lines along the 'Encapsulate Field' it is used to hide the collection from being changed from outside. Create a method which returns a read only view of the collection and we can provide getter() setter() methods for the collection. For additional information regarding this topic click here

Replace Record With Data Class Sometimes while dealing with legacy codes, we have to bring data records. In these cases its wise to create a dumb object (class) for the record set and provide mutators (getters and setters) for each data member. This case is similar to the Replace Array With Object. For additional information regarding this topic click here

Replace Type Code With Class We know them better in C by ‘enums’. A new class can be formed with different fields for each value. One important thing to note is that we can do this only when the enumerations do not affect the behavior of the class. If they do, we have to use the next refactor method. For additional information regarding this topic click here

Replace Type Code With Subclasses If the enumerations affect the behavior, we replace them with sub classes. We take the class which has the type code (enumeration) and create a sub class for each type code. But sometimes we cannot do this (subclass) when either the value of the typecode changes after creation or if the class is already subclasses. In that case we have to use the next refactor method. For additional information regarding this topic click here

Replace Type Code With State/Strategy In this case we replace the type code with a state object. For additional information regarding this topic click here

Replace Subclass With Fields If we have subclasses which differ in their methods only by their return values, its good to change the methods to superclass fields and remove the subclass altogether. For additional information regarding this topic click here


Simplifying Conditional Expressions

Conditional expressions form a major part of programming. It is inconceivable to come across any non trivial program that does not make use of conditional expressions. However, several times it so happens that the purpose of a conditional expression is unclear, the expression is either long or can be replaced altogether by another programming construct or technique. There are several refactoring methods whose aim is to simplify conditional expressions.

Example

Before

if(total>70){
award(“grade A”);
updateRecords();
}
else{
award(“grade B”);
updateRecords();
}

After

if(total>70){
award(“grade A”);
}
else{
award(“grade B”);
}
updateRecords();


Decompose Conditional

This refactoring method aims to simplify a complicated conditional expression. This is done by extracting the logic present in the conditional expression, then clause and else clause into different methods. For additional information regarding this topic click here.

Consolidate Conditional Expression

When there are several conditional tests executed sequentially with the same results then they can be consolidated into a single conditional expression and the logic in each of the tests can be extracted into a method.For additional information regarding this topic click here.

Consolidate duplicate conditional expressions

This is in case the same fragment of code is present in all branches of a conditional expression. In such cases this fragment of code can be brought outside the conditional constructs.For additional information regarding this topic click here.

Remove Control Flags

Often, when we have a conditional expression in a loop we have a control flag that checks to see how long the loop should continue. However, the logic and purpose of the loop and conditional expression become a lot clearer when we replace the control flag with break, return and continue statements. For additional information regarding this topic click here.

Replace Nested Conditional With Guard Clauses

It is often advisable to replace nested conditional statements with guard clauses since this would make it easier to follow the logic and purpose of the conditional expressions.For additional information regarding this topic click here.

Replacing Conditional with polymorphism

There are cases where in different actions need to be taken depending on the type of the object. In such cases it is advisable to replace conditional expressions with polymorphism. For additional information regarding this topic click here.


Making Method Calls Simpler

Any program written in the object oriented paradigm has several method calls. Simplifying method calls is an important part of refactoring.

Example

Before

int square(int base)
int cube(int base)

After

int raiseToPower(int base, int power)


Rename Method

It is very important that a method name give a clear idea of the purpose of the method. The method name should not be too long, but at the same time it should convey a clear idea as to what the method accomplishes. For additional information regarding this topic click here.

Add or Remove Parameters

When a method requires more parameters in order to perform its function we add a parameter to the method. Similar when a method no longer requires a parameter it should be removed. Without having all the required parameters the method cannot achieve its purpose. Having more methods than necessary can confuse the users about how the method actually works. For additional information regarding this topic click here or here.

Separate Query from Modifiers

We can sometimes have a method that both returns a value and changes the state of an object. In such cases we should have two methods. One, that returns a value and another that changes the state of the object. For additional information regarding this topic click here.

Parameterize Method

We might have methods that do essentially the same tasks that differ slightly based on the values used in the method body. In such cases we should consolidate such methods into a single method that takes the values as parameters. For additional information regarding this topic click here.

Replace Parameters with Explicit Methods

When we have a method that executes different blocks of code depending on the value of one of its parameters we should replace this method with a separate method for each value of the parameter. For additional information regarding this topic click here.

Preserve Whole Object

When we retrieve several values from an object and pass these values as parameters to a method call we should pass the object itself as a parameter to the method call. The necessary values are retrieved from the object in the body of the method. For additional information regarding this topic click here.

Replace Parameter with Method

We come across situations where an object invokes a method and passes the return value as a parameter to a different method. We should modify the code such that the receiver itself invokes the corresponding method instead of accepting its return value as a parameter. This results in the list of parameters of the receiver method becoming shorter. For additional information regarding this topic click here.

Introduce Parameter Object

We have a method that has a list of parameters that can be logically grouped into a single unit. In such cases we can replace the list of parameters with an object. For additional information regarding this topic click here.

Remove Setters for fields that are immutable

We should not have any setter methods for fields that are immutable. Such fields are initialized with a value and retain the value throughout. For additional information regarding this topic click here.

Hide Method

In case we feel that the visibility of a method is greater than required we need to reduce its visibility. We should always try to reduce the visibility of methods as much as possible. For additional information regarding this topic click here.

Replace Constructor with Factory Method

We do this when we want more than just simple construction when we create a method. An example is when different subclasses of the object need to be created based on different conditions. For additional information regarding this topic click here.

Replace Error Codes with Exceptions

Most of the modern programming languages offer support for exceptions. Thus, we should make use of this facility and replace error codes such as returning “-1” with the appropriate exceptions. For additional information regarding this topic click here.


Dealing with Generalization

Inheritance is an important part of object oriented programming. Proper use of inheritance will result in code that is efficient and easy to understand. Refactoring to deal with generalization is an important category of refactoring techniques.


Example

Before

Class Employee{
int empid;
}
Class Salesman extends Employee{
float salary;
// Other fields and methods
}
}
Class Manager extends Employee{
float salary;
// Other fields and methods
}

After

Class Employee{
int empid;
float salary;
}

Class Salesman extends Employee{
//Other fields and methods
}
Class Manager extends Employee{
//Other fields and methods
}



Collapse Hierarchy

A superclass and subclass that are not very different can be merger together. For additional information regarding this topic click here.

Pull Up Field

In case all the subclasses of a superclass have a common field, the field can be moved to the superclass. For additional information regarding this topic click here.

Pull Up Method

In case all the subclasses of a superclass have a common method, the method can be moved to the superclass. For additional information regarding this topic click here.

Pull up constructor body

In case we have subclass constructors with identical bodies, the body of the subclass constructors can be extracted to the constructor of the superclass. For additional information regarding this topic click here.

Push Down Method

In case certain aspects of the superclass behavior are relevant only for some of its subclasses, the behavior can be extracted into a method of the corresponding subclasses. For additional information regarding this topic click here.

Push Down Field

In case a field is used only by some subclasses of a superclass, the field can be moved to the subclasses that actually use it. For additional information regarding this topic click here.

Extract Subclass

In case a class has features that are used only in some instances then a subclass can be created for these features. For additional information regarding this topic click here.

Extract Superclass

In case two classes have common features a superclass can be created and common features can be moved to the superclass. For additional information regarding this topic click here.

Extract Interface

In case several clients use a common subset of a class’s interface then that subset can be extracted into a separate interface. For additional information regarding this topic click here.

Form Template Method

We have two or more methods in subclasses that perform similar steps in the same order but the steps are different. In this case we can extract these steps into methods of the same signature. Since these methods now have the same signature, the original method which contained the steps extracted into these methods and the newly formed methods can all be pulled up. For additional information regarding this topic click here.

Replace Inheritance with Delegation

In case a subclass uses only a subset of the super class’s interface we can create a field for the superclass and adjust methods in the subclass to delegate to the superclass where required. We can then remove the subclassing. For additional information regarding this topic click here.

Replace Delegation with Inheritance

In case we are using delegation and are adjusting a majority of the methods to delegate to a different class, we can make the delegating class a subclass of the delegate. For additional information regarding this topic click here.


Categorizing Refactoring according to what Patterns they lead to

In addition there are refactoring techniques that are used to ensure that the code conforms to certain design patterns. The refactoring techniques that fall under this category are as follows.

  • Chain Constructors
  • Compose Method
  • Encapsulate Classes with Factory
  • Encapsulate Composite with Builder
  • Extract Adapter
  • Extract Composite
  • Extract Parameter
  • Form Template Method
  • Inline Singleton
  • Introduce Null Object
  • Introduce Polymorphic Creation With Factory Method
  • Limit Instantiation with Singleton
  • Move Accumulation to Collecting Parameter
  • Move Accumulation to Visitor
  • Move Creation Knowledge to Factory
  • Move Embellishment to Decorator
  • Replace Conditional Dispatcher with Command
  • Replace Conditional Logic with Strategy
  • Replace Constructors with Creation Methods
  • Replace Hard-Coded Notifications with Observer
  • Replace Implicit Language with Interpreter
  • Replace Implicit Tree with Composite
  • Replace One/Many Distinctions with Composite
  • Replace State-Altering Conditionals with State
  • Replace Type Code with Class
  • Unify Interfaces with Adapter
  • Unify Interfaces

Additional information about refactoring to patterns can be found here


References