CSC/ECE 517 Fall 2012/ch1b 1w45 is
Automated Refactoring
Introduction
Refactoring is a technique for restructuring existing code. It is defined as altering the code's internal structure without changing its external behavior. It is done as a series of small transformations which together produce significant restructuring. Since each transformation is small, it's less likely to go wrong. The system is also kept fully working after each small transformation, reducing the chances that a system can get seriously broken during the restructuring.<ref name="refone"> Refactoring Home Page http://refactoring.com/ </ref> When people make small changes to the code without fully understanding its design, the code loses its structure, which eventually has a cumulative effect. If the developer cannot understand the code design, then it becomes more difficult for him to preserve it. Timely refactoring helps the code to retain its shape. <ref name="reftwo"> Why should you Refactor http://sourcemaking.com/refactoring/why-should-you-refactor </ref>
Automated refactoring refers to refactoring which is under the developers control. What we mean by this is that, the system is not responsible for deciding what refactoring is required, rather this is the job of the developer. Automated refactoring is a feature of most widely used IDE's today, which helps expedite the task of refactoring for the developers. <ref name="refthree"> Refactoring for everyone http://www.ibm.com/developerworks/library/os-ecref/ </ref> In this article, we are aiming at describing the various techniques for automated refactoring. This article gives a clear understanding of these techniques which are illustrated with examples. We have also given a comparison of the refactoring functionality provided by various frequently used IDE's.
Common Refactoring Techniques
Extraction Refactoring
There are several refactoring techniques related to extraction: Extract method, Extract local variables, Extract constants. The first method "Extract method will create a new method from code you've selected. Extract Local Variable refactoring takes an expression that is being used repeatedly and assigns it to a local variable. Extract constant refactoring is to select a static, constant expression, which the refactoring will convert to a static final constant. This is useful for removing hard-coded numbers and strings from your code
Before refactoring
- public void abc() {
- int x= 9;
- int y= 100;
- int z=x+y;
- int s=x +z;
- }
After refactoring
- public void abc() {
- int x=9;
- int y=50;
- int z= add(x,y);
- int s= add(x,z);
- }
- private int add(int x, int y) {
- return x+y;
- }
2) Extract local variable: This refactoring technique takes out an expression and then assigns it to a variable. This variable is then used in place of expression everywhere in the code where the expression is present. It has several advantages. First of all it gives meaningful name to an expression which helps in understanding what an expression does. It helps in debugging of code and it makes code efficient.
Before refactoring
- void xyz() {
- int i=12;
- int j= 6;
- int k=15;
- int p=10;
- p+=i+j+k;
- }
In order to perform refactoring in eclipse or any other tool first we select the expression (here i+j+k) or any other tool, it asks for name of new variable name. Suppose we give it q.
After refactoring
- void xyz() {
- int i=12;
- int j= 6;
- int k=15;
- int p=10;
- p+=q;
- }
Before refactoring
- void abc() {
- int a=100;
- int b=100;
- int z= a+b;
- }
Now if we want to extract the constant 100 we will select it and enter a name for it say for example ‘num’. Now instead of using 100 we will use the variable ’num’
After refactoring
- void abc() {
- int a=num;
- int b=num;
- int c=num+num;
- }
Inline Refactoring
This type refactoring is the reverse of local variable extraction, in which we remove a local variable and use the value of the variable(typically an expression) in the place where the variable is used. This can be marginally more efficient than using a temporary variable and, by making the code terser, makes it either easier to read or more cryptic, depending on your point of view<ref name="refthree"> refactoring for everyone http://www.ibm.com/developerworks/library/os-ecref/ </ref>
Inline Refactoring
Put in easy words, Inline refactoring is doing what an external method call does but without making the actual call to an external method. This is as good as writing what a method does inline instead of calling a method defined outside. Let us illustrate this with an example.
Before Inline Refactoring
- void funtion1(String msg) {
- Console.write(msg);
- }
- void function2() {
- function1(“someString”);
- }
After Inline refactoring
- void funtion2() {
- Console.write(“someString”);
- }
In this way we are shortening the code but can print statically only one value (in this case someString), each time. <ref name="reffour"> Refactorings-Inline Method: http://www.skorkin.com/2011/02/refactorings-inline-method/</ref>
Encapsulating Fields
Generally its not a good practice to expose the class attributes through public interface. Thus, Encapsulating such fields will make such fields as private or protected as appropriate and generate getters and setters for the associated field.
Consider the following piece of code. This class has an attribute which is public and can be accessed by any object directly.
Before Encapsulating Fields
- public class Class1 {
- public String attr1;
- }
- public class Class2 {
- public static void main(String [] args) {
- Class1 obj = new Class1();
- Obj.attr1 = “var”;
- System.out.println(“Value of attr1 is” + obj.attr1);
- }
- public static void main(String [] args) {
- }
On making attr1 private, we need to add getter and setter method to access it outside the class. This can be done using automated refactoring technique called Encapsulating a field technique. Now the object obj can access the attribute attr1 only through the defined getter and setter method.
After Encapsulating Fields
- public class Class1 {
- private String attr1;
- public String getAttr1() {
- return attr1;
- }
- public String getAttr1() {
- public setAttr1(String newAttr1Value) {
- attr1 = newAttr1Value;
- }
- public setAttr1(String newAttr1Value) {
- }
- public class Class2 {
- public static void main (String [] args) {
- Class1 obj = new Class1();
- obj.setAttr1(“var”);
- System.out .println(“Value of Attr1 is” + obj.getAttr1());
- }
- public static void main (String [] args) {
- }
Changing Method Signature
Here we change the change the parameters, visibility, and return type of a method.
Before Refactoring
Suppose our class Class1 is defined as below and it has a function funtion1 as shown:
- public class Class1 {
- public void funtion1(int a) {
- //do something
- }
- public void funtion1(int a) {
- }
The method call to function1 will be as follows:
- public class Class2 {
- public static void main(String [] args) {
- Class1 obj = new Class1();
- Obj.function1(5);
- }
- public static void main(String [] args) {
- }
After Refactoring
On change the number of arguments in function1 to 2 instead of 1 the function call should also change. This is done by a concept called Automated refactoring technique for changing method signature.
- public class Class1 {
- public void funtion1(int a, double b) {
- //do something
- }
- public void funtion1(int a, double b) {
- }
- public class Class2 {
- public static void main(String [] args) {
- Class1 obj = new Class1();
- Obj.function1(5, 0.0);
- }
- public static void main(String [] args) {
- }
When performing the refactoring a default value of 0.0 will be passed to the function as a parameter.
Other Refactorings
There are a multitude of refactorings. Those that are included with RubyMine<ref name="reffive">RubyMine :: Ruby and Ruby on Rails IDE with smart code completion, syntax highlighting and refactoring http://www.jetbrains.com/ruby/features/ruby_ide.html#Refactorings</ref> include:
- Rename refactoring
This refactoring technique allows renaming symbols. There are several rename refactoring available. These are rename package, rename class, rename method, rename variable etc.
Before refactoring- class abc() {
- puts “hello”
- }
- def message() {
- a= new abc()
- }
After refactoring- class xyz() {
- puts “hello”
- }
- def message() {
- a= new xyz()
- }
- class abc() {
- Extract Module
This refactoring technique creates a module from the methods of a ruby class.
Before refactoring- class abc() {
- def total_cost(item1, item2) {
- return(item1 +item2)
- }
- def total_cost(item1, item2) {
- }
After refactoring: in the abc.rb file we will have- class abc()
- include total_cost
- end
- module total_cost
- def total_cost(item1, item2) {
- return(item1 +item2)
- }
- def total_cost(item1, item2) {
- class abc() {
- Extract Superclass
It extract certain methods from a class in a superclass. For example if a class B contains two methods abc and xyz. We can do refactoring and move method xyz into superclass A.
Before refactoring- class B
- def abc
- //some code
- end
- def abc
- def xyz
- //some code
- end
- def xyz
- end
After refactoring- class B<A
- def abc
- //some code
- end
- def abc
- def xyz
- //some code
- end
- def xyz
- class B
- Introduce Variable
This is when an expression is replaced by a variable and the variable is used in place of the expression each time. A change to the expression will mean a change only at one place. Eg.- puts “My favorite team is #{get_team()}”
Then, :puts “My favorite team is #{myTeam}” - Introduce Constant
This is when a new constant is defined and initialized and that constant is used instead of a value. Eg. :a = b +3; CONSTANT = 3; a = b + CONSTANT; - Introduce Field
This is when a new field is defined and initialized and then that field is used instead of the value. Eg. :a = b + 3; @field = 3; a = b + @field; - Introduce Parameter
This type simply adds a new parameter to a function and updates the method calls according to that. Eg.- Method signature changes from def function1() to def function1(pass)
- Method call will change from a=function1() to a=funtion1(something)
- Inline Variable
This works similar to Inline refactoring explained above. Eg. Instead of writing a variable’s value to console, we will write a constant to the console, which is basically hard coding the value written to the console. - Pull Up Members
Refers to moving a field x from a class A to its superclass. Thus, x will be moved out of all of A’s siblings and will have just one copy in A’s superclass. Eg. If ‘type’ is an attribute of class Leopard and class Cheetah, then moving ‘type’ to super class of Leopard and Cheetah, that is, class Cat will result into one copy of ‘type’ being in class Cat and ‘type’ will get removed from both Cheetah and Leopard. - Push Down Members
These work opposite of pull up members: If ‘type’ is part of class Cat then it will be moved individually to class Cheetah and class Leopard and now no existence of ‘type’ will be found in class Cat. - Override Method
Refactoring support in popular IDE(s)
IDE | Language(s) | Refactoring Support | Automated Refactoring? | Number of Refactoring support | Support for Pattern | Example |
---|---|---|---|---|---|---|
Borland Delphi | Object Oriented Pascal | Rename refactoring, Refactor driven “Find References”, Introduce Variable refactoring, Introduce Field refactoring, Inline Variable refactoring, Change Parameters refactoring, Safe Delete refactoring, Push Members Up / Down refactoring, Pull Members Up refactoring, Extract Superclass refactoring, Extract Interface refactoring, Move Members refactoring, Declare variable refactoring, Declare field refactoring, Extract method refactoring, Find unit/import namespace refactoring, Refactor driven “Find in Files”, Extract to resource string refactoring<ref name="borlandrefactorings>Leveraging What You Have: 10 Top Things Added to Delphi Since Delphi 7 http://edn.embarcadero.com/article/37416</ref> | Yes | 17 | Declare Variable<ref name="borlanddecvar">Increased Productivity with Refactoring, Unit Testing, Help Insight, Error Insight, and Sync Edit in Borland Delphi 2005 http://edn.embarcadero.com/article/33278</ref> |
Before Refactoring procedure TWinForm1.btnOpen_Click(sender: System.Object; e: System.EventArgs); begin if (OpenFileDialog1.ShowDialog = System.Windows.Forms.DialogResult.OK) then begin Assign(f,OpenFileDialog1.FileName); Reset(f); try finally CloseFile(f); end; end; end; After Refactoring procedure TWinForm1.btnOpen_Click(sender: System.Object; e: System.EventArgs); var f: TextFile; begin if (OpenFileDialog1.ShowDialog = System.Windows.Forms.DialogResult.OK) then begin Assign(f,OpenFileDialog1.FileName); Reset(f); try finally CloseFile(f); end; end; end; |
Eclipse <ref name='refeclipse'>Eclipse Refactoring Actions http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Freference%2Fref-menu-refactor.htm </ref> | C, C++, Java | Rename selected Elements, Move selected elements, Change Method Signature, Extraction, Convert Anonymous Class to Nested, Convert Local Variable to Field, Extract Super class and Interface, push down, pull up, Introduce Indirection, Encapsulate Field, Migrate JAR File, Create & Apply Refatoring Scripts | Yes | 27 | Introduce Factory |
Before Refactoring int x= p * 5; p.x; After Refactoring int x= getFoo(p); (Method Extraction) p.getX() (Encapsulate Field) |
Kdevelop | C, C++ | Several<ref name="kdeveloprefactorings">Kdevelop4: Now with refactoring! http://zwabel.wordpress.com/2008/11/05/kdevelop4-now-with-refactoring/</ref>. Project wide search/replace and local search/replace. | Yes but very limited. | 2 | Rename Class | Right click on class name and select Rename |
NetBeans <ref name="refnetbeans">Refactoring in NetBeans 4.1 http://java.sun.com/developer/technicalArticles/tools/refactoring/</ref> | Java | Renaming fields, methods, classes, or packages, Encapsulating fields, Changing method parameters, Moving classes, | Yes but very limited | 4 | No |
Before Refactoring public class Foo{ public int n; public int Compute(int val); } After Refactoring (Encapsulating field) pubic class Foo{ private int n; public int getN(); public void setN(int val); public int Compute(int arg); } |
Padre | Perl | Lexically Rename Variable, Introduce Temporary Variable<ref name="padrerefactorings" >How to do it in Padre http://padre.perlide.org/howto.html</ref> | Yes | Unknown | Right click on item to refactor and select refactoring choice from the context menu | |
RubyMine | Ruby | Safe Delete, Rename Refactorings, Push Members Down, Pull Members Up, Move Refactorings, Introduce Variable, Introduce Parameter, Introduce Field, Introduce Constant, Inline, Extract Superclass, Extract Module, Extract Partial, Extract Method, Copy / Clone. | Yes | 15 | Introduce Temporary |
Before Refactoring c = a * b + 5 d = 2 * a * b After Refactoring t = a * b c = t + 5 d = 2 * t |
Visual Studio <ref name="refvstudio">7 Refactoring Methods in Visual Studio 2010 http://p2p.wrox.com/content/articles/7-refactoring-methods-visual-studio-2010 </ref> | C, C++, Java, C# | Extract Method, Encapsulate Field, Extract Interface, Rename, Promote Variable to Parameter(Changing method signature), Generate Method Stub(Used for testing purposes) | Yes | 7 | No | Promoting Variable to Parameter
Before Refactoring public void MethodA() { MethodB(); } public void MethodB() { string output = "Test String"; MessageBox.Show( output); } After Refactoring public void MethodA() { MethodB("Test String"); } public void MethodB(string output) { MessageBox.Show( output); } |
References
<references/>