Exploration of automatic refactoring implementation in Eclipse

Source: Internet
Author: User

This article uses the automatic refactoring function of eclipse to reconstruct a program instance. The purpose is to explore how much Eclipse automatic Refactoring can assist in refactoring. The program instance uses the example in Refactoring: Improving the Design of Existing Code.

The automatic refactoring function of Eclipse supports renaming of various program elements and automatically updates references. Eclipse supports moving methods and fields between classes and automatically updating references. Eclipse supports updating and replacing inline fields and functions. Eclipse supports extraction methods, variables, and other program elements.

The reconstruction process is a process of constant experimentation and exploration. Eclipse refactoring supports undo and redo, and can preview refactoring results. These are very useful functions.

Eclipse's code structure-level reconstruction methods, such as renaming, extraction, moving, inline functions, and Modifying Method feature characters, are mature and worthwhile functions. Eclipse does not support the reconstruction of the design structure. However, the author believes that the idea of automatic refactoring should be "reconstruction work assisted by tools", and people still undertake most reconstruction work.

  I. Preparations

This article uses the example in chapter 1 of Refactoring: Improving the Design of Existing Code. The code before reconstruction and the code after each reconstruction can be found in the attachment. Readers are better at reading this article with Refactoring: Improving the Design of Existing Code.

Eclipse uses the following versions:


A Chinese Language Pack is installed.

  2. Reconstruction Step 1: break down and restructure statement ()

Purpose:

1. Extract the swich statement in the statement () function into the independent function amountFor.

2. Modify the amountFor () parameter name

Reconstruction Method:

Extract Method
Rename Method

Method:

1. Select the code block of the swich statement and select "reconstruction/extraction method" from the context menu. The parameter dialog box is displayed. Eclipse automatically analyzes the local variables in the code block and finds two local variables: each and thisAmount. Each is read in the code block, but thisAmount is modified in the code block. According to the rule summarized by refactoring Extract Method, each should be treated as the parameter of the extraction function, and thisAmount should be treated as the return value of the extraction function. However, Eclipse does not distinguish between them, and directly treats these two variables as parameters for extracting new methods ,.


Our goal is to take the each that will not be modified in the extraction function as the parameter; the modified thisAmount will be used as the return value. The solution is to move the double thisAmount = 0; this line of code to the switch statement, which becomes like this:

Double thisAmount = 0;
Switch (each. getMovie (). getPriceCode ()){
Case Movie. REGULAR:
ThisAmount + = 2;
If (each. getDaysRented ()> 2)
ThisAmount + = (each. getDaysRented ()-2) * 1.5;
Break;

Case Movie. NEW_RELEASE:
ThisAmount + = each. getDaysRented () * 3;
Break;

Case Movie. CHILDRENS:
ThisAmount + = 1.5;
If (each. getDaysRented ()> 3)
ThisAmount + = (each. getDaysRented ()-3) * 1.5;
Break;
}

Select this code and select "reconstruction/extraction method" from the right-click menu. eclipse becomes smarter this time ,.

Select the preview button to view the reconstruction result in advance, which meets our initial goal.


Select the "OK" button. The code snippet after reconstruction is as follows:

Public String statement (){
Double totalAmount = 0;
Int frequentRenterPoints = 0;
Enumeration events als = _ temporary ALS. elements ();
String result = "Marshal Record for" + getName () + "";

While (events ALS. hasMoreElements ()){
Marshal each = (Marshal) Marshals. nextElement ();

Double thisAmount = amountFor (each );

FrequentRenterPoints ++;
If (each. getMovie (). getPriceCode () = Movie. NEW_RELEASE & each. getDaysRented ()> 1)
FrequentRenterPoints ++;

Result + = "" + each. getMovie (). getTitle () + "" + String. valueOf (thisAmount) + "";
TotalAmount + = thisAmount;
}

Result + = "Amount owed is" + String. valueOf (totalAmount) + "";
Result + = "You earned" + String. valueOf (frequentRenterPoints) + "frequent renter points ";

Return result;
}

/**
* @ Param each
* @ Return
*/
Private double amountFor (Marshal each ){
Double thisAmount = 0;
Switch (each. getMovie (). getPriceCode ()){
Case Movie. REGULAR:
ThisAmount + = 2;
If (each. getDaysRented ()> 2)
ThisAmount + = (each. getDaysRented ()-2) * 1.5;
Break;

Case Movie. NEW_RELEASE:
ThisAmount + = each. getDaysRented () * 3;
Break;

Case Movie. CHILDRENS:
ThisAmount + = 1.5;
If (each. getDaysRented ()> 3)
ThisAmount + = (each. getDaysRented ()-3) * 1.5;
Break;
}
Return thisAmount;
}

2. Select the amountFor () parameter each, right-click the parameter, select "refactor/rename", enter a new name in the dialog box: aRental, and select OK. amountFor () all each references in are replaced with new names. In the same way, modify the local variable thisAmount in amountFor () to result. The reconstructed amountFor () code is as follows:

/**
* @ Param aRental
* @ Return
*/
Private double amountFor (Marshal aRental ){
Double result = 0;
Switch (aRental. getMovie (). getPriceCode ()){
Case Movie. REGULAR:
Result + = 2;
If (aRental. getDaysRented ()> 2)
Result + = (aRental. getDaysRented ()-2) * 1.5;
Break;

Case Movie. NEW_RELEASE:
Result + = aRental. getDaysRented () * 3;
Break;

Case Movie. CHILDRENS:
Result + = 1.5;
If (aRental. getDaysRented ()> 3)
Result + = (aRental. getDaysRented ()-3) * 1.5;
Break;
}
Return result;
}

  3. Reconstruction Step 2: Move the "Amount Calculation" code

Purpose:

1. Transfer the function amountFor () to the marshal class and rename it getCharge ().

2. Update and replace all references to amountFor.

Reconstruction Method:

Move Method
Change Method signatrue
Inline Method
Inline Temp

Method:

1. Select the definition of the function amountFor (), right-click the function, and select "refactor/move". The Parameter Setting dialog box is displayed. Change the new method name to getCharge. Press the "OK" button. The amountFor () function in the Customer Class is moved to the marshal Class and renamed to getCharge ().


At the same time, eclipse automatically adds a line of "delegate" code to the new function in the Customer's amountFor () function:

Private double amountFor (Marshal aRental ){
Return aRental. getCharge ();
}

This line of code produces a compilation error because the private type of amountFor () is passed to the new method:

/**
* @ Param this
* @ Return
*/
Private double getCharge (){
......
}

2. Continue refactoring! Select the getCharge () method and select "refactor/modify method feature" from the context menu. In the displayed Parameter Selection dialog box, change the access modifier from private to public. The Eclipse compilation error message automatically disappears.


3. Return to the Customer class and replace all references to amountFor () with references to getCharge () directly. Select the Customer class function amountFor (Marshal aRental) and right-click the function and choose "refactor/inline". The parameter selection dialog box is displayed.


Select the "OK" button to replace the reference location of amountFor () with the reference of getCharge.

Public String statement (){
......
Double thisAmount = each. getCharge ();
......
}

4. Remove the Temporary Variable thisAmount.

Select the thisAmount variable and choose "refactor/inline" from the context menu. The refactoring preview window is as follows. Click "OK" to refactor the code.


Statement () code:

Public String statement (){
Double totalAmount = 0; // total consumption amount
Int frequentRenterPoints = 0; // frequent customer points
Enumeration events als = _ temporary ALS. elements ();
String result = "Marshal Record for" + getName () + "";

While (events ALS. hasMoreElements ()){
Marshal each = (Marshal) Marshals. nextElement (); // gets a lease record

// Add frequent renter points (accumulate frequent customer points)
FrequentRenterPoints ++;
// Add bouns for a two day new release marshal
If (each. getMovie (). getPriceCode () = Movie. NEW_RELEASE & each. getDaysRented ()> 1)
FrequentRenterPoints ++;

// Show figures for this lease Al (display the lease data)
Result + = "" + each. getMovie (). getTitle () + "" +
String. valueOf (each. getCharge () + "";
TotalAmount + = each. getCharge ();
}

// Add footer lines (print at the end)
Result + = "Amount owed is" + String. valueOf (totalAmount) + "";
Result + = "You earned" + String. valueOf (frequentRenterPoints) + "frequent renter points ";

Return result;
}
  
   4. Step 3 of reconstruction: extract the code of "Frequent customer point calculation"

Objective: To extract the code of frequent customer point calculation and put it in the marshal class. The Code of "Frequent customer point calculation" is as follows.

Public String statement (){
......
// Add frequent renter points
FrequentRenterPoints ++;
// Add bouns for a two day new release marshal
If (each. getMovie (). getPriceCode () = Movie. NEW_RELEASE & each. getDaysRented ()> 1)
FrequentRenterPoints ++;
......
}

The code after reconstruction is as follows:

FrequentRenterPoints + = each. getFrequentRenterPoints ();

Reconstruction Method:

Extract Method
Move Method
Change Method signatrue
Inline Method

Method:

1. First, extract the code to an independent function.

Use the "Extraction Method" to reconstruct the code. Function Name: getFrequentRenterPoints. Unfortunately, eclipse cannot generate code such as frequentRenterPoints + = getFrequentRenterPoints (Marshal aRental. The reason is that the local variable frequentRenterPoints that executes the auto-increment operation must appear on the right of the equation. Therefore, the extraction function getFrequentRenterPoints () must take frequentRenterPoints as the parameter. Manually modify the function and reference the function. The reconstructed code is as follows:

Public String statement (){
......
While (events ALS. hasMoreElements ()){
......
FrequentRenterPoints + = getFrequentRenterPoints (each );
......
}
......
}

/**
* @ Param each
* @ Return
*/
Private int getFrequentRenterPoints (Marshal each ){
If (each. getMovie (). getPriceCode () = Movie. NEW_RELEASE & each. getDaysRented ()> 1)
Return 2;
Else
Return 1;
}

2. Move getFrequentRenterPoints () to the marshal class.

3. Set getFrequentRenterPoints () "change method feature character" to public.

4. perform inline operations on the Customer's function getFrequentRenterPoints () to reconstruct the target.

   5. Reconstruction Step 4: remove temporary variables (totalAmount and frequentRenterPoints)

Objective: To remove temporary variables (totalAmount and frequentRenterPoints)

Method:

1. Analyze the definition and reference structure of totalAmount and frequentRenterPoints as follows:

// Declaration and definition
Double totalAmount = 0;
Int frequentRenterPoints = 0;
......
// Modify in the loop
While (events ALS. hasMoreElements ()){
......
FrequentRenterPoints + = each. getFrequentRenterPoints ();
......
TotalAmount + = each. getCharge ();
......
}
......
// Used outside the loop
Result + = "Amount owed is" + String. valueOf (totalAmount) + "";
Result + = "You earned" + String. valueOf (frequentRenterPoints) + "frequent renter points ";
......

The above two variables are defined and used outside the loop body and modified in the loop. Using the Replace Temp with Query method to remove these two temporary variables is a slightly complicated refactoring. Unfortunately, eclipse currently does not support such refactoring.

2. manually modify the code.

   6. Reconstruction Step 5: Replace the price-related conditional logic with Polymorphism

Purpose:

1. Move the function getCharge () in the marshal class to the Movie class.

2. Move the getFrequentRenterPoints () function in the marshal class to the Movie class.

Reconstruction Method:

Move Method
Inline Method

Method:

1. Select the function getCharge () in the marshal class, right-click the function, and select "refactor/move" from the menu. eclipse prompts that the recipient cannot be found and cannot be moved. The reason is that this line of statements:

Switch (getMovie (). getPriceCode () {// get the video rental price

Select getMovie (), right-click the menu, select "refactor/inline", and click "OK". The Code will be:

Switch (_ movie. getPriceCode () {// get the video rental price

Select getCharge () and execute "refactoring/moving". Then, the function is moved to the Movie class. However, this only partially achieves the purpose of refactoring. We found that the Mobile Code passed the marshal as a parameter to getCharge () and manually changed the code:

Class Movie ......
/**
* @ Param this
* @ Return
*/
Public double getCharge (int _ daysRented ){
Double result = 0;
Switch (getPriceCode () {// get the video rental price
Case Movie. REGULAR: // normal film
Result + = 2;
If (_ daysRented> 2)
Result + = (_ daysRented-2) * 1.5;
Break;

Case Movie. NEW_RELEASE: // new film
Result + = _ daysRented * 3;
Break;

Case Movie. CHILDRENS: // Children's Film
Result + = 1.5;
If (_ daysRented> 3)
Result + = (_ daysRented-3) * 1.5;
Break;
}
Return result;
}

Class Marshal ......
/**
* @ Param this
* @ Return
*/
Public double getCharge (){
Return _ movie. getCharge (_ daysRented );
}

2. Use the same steps to process getFrequentRenterPoints (). The reconstructed code is as follows:

Class Movie ......
/**
* @ Param frequentRenterPoints
* @ Param this
* @ Return
*/
Public int getFrequentRenterPoints (int daysRented ){
If (getPriceCode () = Movie. NEW_RELEASE & daysRented> 1)
Return 2;
Else
Return 1;
}
Class Marshal ......
/**
* @ Param frequentRenterPoints
* @ Param this
* @ Return
*/
Public int getFrequentRenterPoints (int daysRented ){
If (getPriceCode () = Movie. NEW_RELEASE & daysRented> 1)
Return 2;
Else
Return 1;
}

   7. Reconstruction Step 6: finally ...... We came to inherit

Purpose: To introduce the state mode to the switch statement.

Method:

Unfortunately, we have to end the eclipse automatic refactoring journey ahead of schedule. Eclipse almost cannot be restructured. Perhaps the automatic reconstruction tool called by Martin Fowler in the book is not limited to the idea of "reconstruction work assisted by tools. Art is the patent of mankind, and the dream of programming art will continue.

Interested readers can view the final code of manual refactoring. Rebuild to the end!

Appendix: Reconstruction Methods supported by eclipse (from eclipse Chinese help)

Name Function
Undo the last refactored "undo ". As long as no source changes have been made except for refactoring, the refactoring undo buffer is valid.
Redo the last "redo" to undo the redo ". As long as no source changes have been made except for rebuild, the rebuild Undo/Redo buffer takes effect.
Rename start "RENAME" rebuild dialog box: Rename the selected element and correct all references to the element (if enabled) (also in other files ). It can be used for: Methods, fields, local variables, method parameters, types, compilation units, packages, source folders and projects, and Text Selection sections parsed into one of these element types.
Move start "move" refactoring dialog box: Move the selected element and correct all references to the element (if enabled) (also in other files ). Applicable to: an instance method (which can be moved to a component), one or more static methods, static fields, types, compilation units, packages, source folders, and projects, and the text selection section parsed to one of these element types.
Change Method feature character start the "change method feature character" Reconstruction dialog box. Change the parameter name, type, and sequence, and update all references to the corresponding method. In addition, parameters can be removed or added, and the type of the method return and its visibility can be changed. This refactoring can be applied to methods or text selection parsed as methods.
Convert Anonymous class to nested class start "Convert Anonymous class to nested class" refactoring dialog box. It helps you convert an anonymous internal class to a member class. You can apply this refactoring to anonymous internal classes.
Convert nested type to top-level start "Convert nested type to top-level type" Reconstruction dialog box. Create a new Java compilation unit for the selected member type and update all references as needed. For non-static member types, fields are added to allow access to the previous peripheral instance. You can apply this refactoring to the member type or parse the text to the member type.
Push-down start "Push-down" Reconstruction dialog box. Move a constructor and field from a class to its subclass. You can apply this refactoring to text selection of one or more methods, fields, or fields declared in the same type.
One or more methods, fields, and member types declared in the "pull up" refactoring type can also be applied to Text Selection in fields, methods, or member types. Wizard. Move the field or method to the superclass of the declared class or (for methods) Declare the method as an abstract class in the superclass. You can apply this refactoring to the same class.
Start the extraction interface reconstruction dialog box. Use a set of methods to create a new interface and enable the selected class to implement this interface, and change the reference to the new interface as much as possible (optional ). You can apply this refactoring to the type.
Use the supertype to start the "use as much as possible" dialog box. Replace the occurrence of a type with one of its super types. Before performing this replacement, You need to identify all possible locations for this replacement. This refactoring can be used for types.
Start the "inline" refactoring dialog box inline. Inline local variables, methods, or constants. This reconstruction can be used to select methods, static final state fields, and text parsed as methods, static final state fields, or local variables.
Start the "Extraction Method" Reconstruction dialog box. Create a new method that contains the selected statement or expression, and replace the method with a reference to the new method. You can use the expanded selection option in the editing menu to obtain a valid selection range. This feature is useful for clearing lengthy, messy, or overly complex methods.
Extract local variables and start the "extract variables" Reconstruction dialog box. Create a new variable specified for the selected expression and replace the variable with a reference to the new variable. This refactoring can be used to parse the text selected as a local variable. You can use the expanded selection option in the editing menu to obtain a valid selection range.
Extract constants start the "extract constants" Reconstruction dialog box. Create a static final state field from the selected expression and replace the field reference. You can also choose to override other occurrences of the same expression. This reconstruction can be used to select static final state fields and text parsed to static final state fields.
Convert local variables to fields start the "convert local variables to fields" Reconstruction dialog box. Convert a local variable to a field. If the variable is initialized at creation, this operation moves the initialization to the declaration of the new field or the constructor of the class. This refactoring can be used to parse the text selected as a local variable.
Start the "self-encapsulated field" Reconstruction dialog box. Replace all references to fields with the getting and setting methods. It applies to the selected field or text selection parsed as a field.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.