Рефакторинг в Eclipse

1. Общ преглед

На refactoring.com четем, че „рефакторингът е дисциплинирана техника за преструктуриране на съществуващ корпус от код, променящ вътрешната му структура, без да променя външното му поведение“.

Обикновено може да искаме да преименуваме променливи или методи или да направим кода си по-обектно ориентиран чрез въвеждане на дизайнерски модели. Съвременните IDE имат много вградени функции, които ни помагат да постигнем тези видове цели за рефакторинг и много други.

В този урок ще се съсредоточим върху рефакторинга в Eclipse, безплатна популярна Java IDE.

Преди да започнем каквото и да е рефакторинг, препоръчително е да имаме солиден набор от тестове, за да проверим дали не сме нарушили нищо по време на рефакторинга.

2. Преименуване

2.1. Преименуване на променливи и методи

Можем да преименуваме променливи и методи, като следваме тези прости стъпки :

    • Изберете елемента
    • Щракнете с десния бутон върху елемента
    • Щракнете върху Преструктуриране на> Преименуване вариант
  • Въведете новото име
  • Натиснете Enter

Можем също така да изпълнява втория и третия етапи от помощта на клавиша за бърз достъп, Alt + Shift + R .

Когато се извърши горното действие, Eclipse ще намери всяко използване на този елемент в този файл и ще ги замени на място.

Също така можем да използваме разширена функция, за да актуализираме референцията в други класове, като задържим курсора на мишката върху елемента, когато рефакторът е включен, и щракнем върху Опции :

Това ще отвори изскачащ прозорец, където можем да преименуваме променливата или метода и да имаме възможност да актуализираме референцията в други класове:

2.2. Преименуване на пакети

Можем да преименуваме пакет, като изберем името на пакета и извършим същите действия като в предишния пример. Веднага ще се появи изскачащ прозорец, където можем да преименуваме пакета, с опции като актуализиране на препратки и преименуване на подпакети.

Също така можем да преименуваме пакета от изгледа на Project Explorer, като натиснем F2 :

2.3. Преименуване на класове и интерфейси

Можем да преименуваме клас или интерфейс, като използваме същите действия или просто като натиснем F2 от Project Explorer. Това ще отвори изскачащ прозорец с опции за актуализиране на референции, заедно с няколко разширени опции:

3. Извличане

Сега, нека поговорим за добива. Извличането на код означава вземане на парче код и преместването му.

Например можем да извлечем код в различен клас, суперклас или интерфейс. Можем дори да извлечем код към променлива или метод в същия клас.

Eclipse предоставя разнообразни начини за постигане на екстракции, които ще демонстрираме в следващите раздели.

3.1. Клас на извличане

Да предположим, че имаме следния клас Car в нашата кодова база:

public class Car { private String licensePlate; private String driverName; private String driverLicense; public String getDetails() { return "Car [licensePlate=" + licensePlate + ", driverName=" + driverName + ", driverLicense=" + driverLicense + "]"; } // getters and setters }

Сега, да предположим, че искаме да извлечем подробностите за драйвера в различен клас. Ние можем да направим това, като щракнете с десния бутон някъде в рамките на класа и избора на Преструктуриране на> Извличане клас опция :

Това ще отвори изскачащ прозорец, където можем да дадем име на класа и да изберем кои полета искаме да преместим, заедно с няколко други опции:

Можем също да визуализираме кода, преди да продължим напред. Когато щракнем върху OK , Eclipse ще създаде нов клас с име Driver и предишният код ще бъде рефакториран на:

public class Car { private String licensePlate; private Driver driver = new Driver(); public String getDetails() { return "Car [licensePlate=" + licensePlate + ", driverName=" + driver.getDriverName() + ", driverLicense=" + driver.getDriverLicense() + "]"; } //getters and setters }

3.2. Извличане на интерфейс

Също така можем да извлечем интерфейс по подобен начин. Да предположим, че имаме следния клас EmployeeService :

public class EmployeeService { public void save(Employee emp) { } public void delete(Employee emp) { } public void sendEmail(List ids, String message) { } }

Можем да извлечем интерфейс, като щракнем с десния бутон на произволно място в класа и изберем опцията Refactor> Extract Interface , или можем да използваме клавишната комбинация Alt + Shift + T, за да изведем менюто директно:

Това ще отвори изскачащ прозорец, където можем да въведем името на интерфейса и да решим кои членове да декларираме в интерфейса:

As a result of this refactoring, we'll have an interface IEmpService, and our EmployeeService class will be changed as well:

public class EmployeeService implements IEmpService { @Override public void save(Employee emp) { } @Override public void delete(Employee emp) { } public void sendEmail(List ids, String message) { } }

3.3. Extract Superclass

Suppose we have an Employee class containing several properties that aren't necessarily about the person's employment:

public class Employee { private String name; private int age; private int experienceInMonths; public String getName() { return name; } public int getAge() { return age; } public int getExperienceInMonths() { return experienceInMonths; } }

We may want to extract the non-employment-related properties to a Person superclass. To extract items to a superclass, we can right-click anywhere in the class and choose the Refactor > Extract Superclass option, or use Alt+Shift+T to bring up the menu directly:

This will create a new Person class with our selected variables and method, and the Employee class will be refactored to:

public class Employee extends Person { private int experienceInMonths; public int getExperienceInMonths() { return experienceInMonths; } }

3.4. Extract Method

Sometimes, we might want to extract a certain piece of code inside our method to a different method to keep our code clean and easy to maintain.

Let's say, for example, that we have a for loop embedded in our method:

public class Test { public static void main(String[] args) { for (int i = 0; i < args.length; i++) { System.out.println(args[i]); } } }

To invoke the Extract Method wizard, we need to perform the following steps:

  • Select the lines of code we want to extract
  • Right-click the selected area
  • Click the Refactor > Extract Method option

The last two steps can also be achieved by keyboard shortcut Alt+Shift+M. Let's see the Extract Method dialog:

This will refactor our code to:

public class Test { public static void main(String[] args) { printArgs(args); } private static void printArgs(String[] args) { for (int i = 0; i < args.length; i++) { System.out.println(args[i]); } } }

3.5. Extract Local Variables

We can extract certain items as local variables to make our code more readable.

This is handy when we have a String literal:

public class Test { public static void main(String[] args) { System.out.println("Number of Arguments passedlazy" src="//www.baeldung.com/wp-content/uploads/2019/06/Eclipse-refactor-21.png">

The last step can also be achieved by the keyboard shortcut Alt+Shift+L. Now, we can extract our local variable:

And here's the result of this refactoring:

public class Test { public static void main(String[] args) { final String prefix = "Number of Arguments passedextracting-constants">3.6. Extract Constant

Or, we can extract expressions and literal values to static final class attributes.

We could extract the 3.14 value into a local variable, as we just saw:

public class MathUtil { public double circumference(double radius) { return 2 * 3.14 * radius; } }

But, it might be better to extract it as a constant, for which we need to:

  • Select the item
  • Right-click and choose Refactor > Extract Constant

This will open a dialog where we can give the constant a name and set its visibility, along with a couple of other options:

Now, our code looks a little more readable:

public class MathUtil { private static final double PI = 3.14; public double circumference(double radius) { return 2 * PI * radius; } }

4. Inlining

We can also go the other way and inline code.

Consider a Util class that has a local variable that's only used once:

public class Util { public void isNumberPrime(int num) { boolean result = isPrime(num); if (result) { System.out.println("Number is Prime"); } else { System.out.println("Number is Not Prime"); } } // isPrime method } 

We want to remove the result local variable and inline the isPrime method call. To do this, we follow these steps:

  • Select the item we want to inline
  • Right-click and choose the Refactor > Inline option

The last step can also be achieved by keyboard shortcut Alt+Shift+I:

Afterward, we have one less variable to keep track of:

public class Util { public void isNumberPrime(int num) { if (isPrime(num)) { System.out.println("Number is Prime"); } else { System.out.println("Number is Not Prime"); } } // isPrime method }

5. Push Down and Pull Up

If we have a parent-child relationship (like our previous Employee and Person example) between our classes, and we want to move certain methods or variables among them, we can use the push/pull options provided by Eclipse.

As the name suggests, the Push Down option moves methods and fields from a parent class to all child classes, while Pull Up moves methods and fields from a particular child class to parent, thus making that method available to all the child classes.

For moving methods down to child classes, we need to right-click anywhere in the class and choose the Refactor > Push Down option:

This will open up a wizard where we can select items to push down:

Similarly, for moving methods from a child class to parent class, we need to right-click anywhere in the class and choose Refactor > Pull Up:

This will open up a similar wizard where we can select items to pull up:

6. Changing a Method Signature

To change the method signature of an existing method, we can follow a few simple steps:

  • Select the method or place the cursor somewhere inside
  • Right-click and choose Refactor > Change Method Signature

The last step can also be achieved by keyboard shortcut Alt+Shift+C.

This will open a popup where you can change the method signature accordingly:

7. Moving

Sometimes, we simply want to move methods to another existing class to make our code more object-oriented.

Consider the scenario where we have a Movie class:

public class Movie { private String title; private double price; private MovieType type; // other methods }

And MovieType is a simple enum:

public enum MovieType { NEW, REGULAR }

Suppose also that we have a requirement that if a Customer rents a movie that is NEW, it will be charged two dollars more, and that our Customer class has the following logic to calculate the totalCost():

public class Customer { private String name; private String address; private List movies; public double totalCost() { double result = 0; for (Movie movie : movies) { result += movieCost(movie); } return result; } private double movieCost(Movie movie) { if (movie.getType() .equals(MovieType.NEW)) { return 2 + movie.getPrice(); } return movie.getPrice(); } // other methods }

Clearly, the calculation of the movie cost based on the MovieType would be more appropriately placed in the Movie class and not the Customer class. We can easily move this calculation logic in Eclipse:

  • Select the lines you want to move
  • Right-click and choose the Refactor > Move option

The last step can also be achieved by keyboard shortcut Alt+Shift+V:

Eclipse is smart enough to realize that this logic should be in our Movie class. We can change the method name if we want, along with other advanced options.

The final Customer class code will be refactored to:

public class Customer { private String name; private String address; private List movies; public double totalCost() { double result = 0; for (Movie movie : movies) { result += movie.movieCost(); } return result; } // other methods }

As we can see, the movieCost method has been moved to our Movie class and is being used in the refactored Customer class.

8. Conclusion

In this tutorial, we looked into some of the main refactoring techniques provided by Eclipse. We started with some basic refactoring like renaming and extracting. Later on, we saw moving methods and fields around different classes.

To learn more, we can always refer to the official Eclipse documentation on refactoring.