«1. Обзор

На сайте refactoring.com мы читаем, что «рефакторинг — это метод реструктуризации существующего кода, изменяющий его внутреннюю структуру без изменения его внешнего поведения».

Как правило, мы можем захотеть переименовывать переменные или методы, или мы можем захотеть сделать наш код более объектно-ориентированным, введя шаблоны проектирования. Современные IDE имеют множество встроенных функций, которые помогают нам достичь таких целей рефакторинга и многих других.

В этом руководстве мы сосредоточимся на рефакторинге в Eclipse, популярной бесплатной Java IDE.

Прежде чем мы начнем какой-либо рефакторинг, желательно иметь надежный набор тестов, чтобы убедиться, что мы ничего не сломали во время рефакторинга.

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

2.1. Переименование переменных и методов

Мы можем переименовать переменные и методы, выполнив следующие простые шаги:

    Выберите элемент Щелкните элемент правой кнопкой мыши Щелкните параметр Рефакторинг \u003e Переименовать
    Введите новое имя Нажмите 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
}

Теперь предположим, что мы хотим извлечь детали драйвера в другой класс. Мы можем сделать это, щелкнув правой кнопкой мыши в любом месте класса и выбрав параметр Refactor \u003e Extract Class:

Это откроет всплывающее окно, в котором мы можем назвать класс и выбрать, какие поля мы хотим переместить, а также несколько другие варианты:

Мы также можем предварительно просмотреть код, прежде чем двигаться дальше. Когда мы нажмем 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<Integer> ids, String message) {
    }
}

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

Это откроет всплывающее окно, в котором мы можем ввести имя интерфейса и решить, какие элементы объявить в интерфейсе:

В результате этого рефакторинга у нас будет интерфейс IEmpService, а также наш класс EmployeeService:

public class EmployeeService implements IEmpService {

    @Override
    public void save(Employee emp) {
    }

    @Override
    public void delete(Employee emp) {
    }

    public void sendEmail(List<Integer> ids, String message) {
    }
}

3.3. Извлечь суперкласс

Предположим, у нас есть класс Employee, содержащий несколько свойств, которые не обязательно относятся к занятости человека:

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

«

«Мы можем захотеть извлечь свойства, не связанные с работой, в суперкласс Person. Чтобы извлечь элементы в суперкласс, мы можем щелкнуть правой кнопкой мыши в любом месте класса и выбрать параметр «Рефакторинг» \u003e «Извлечь суперкласс» или использовать Alt+Shift+T, чтобы вызвать меню напрямую:

public class Employee extends Person {

    private int experienceInMonths;

    public int getExperienceInMonths() {
        return experienceInMonths;
    }
}

Это создаст новый класс Person с выбранные нами переменные и метод, а также класс Employee будут преобразованы в:

3.4. Извлечение метода

Иногда нам может понадобиться извлечь определенный фрагмент кода внутри нашего метода в другой метод, чтобы сохранить наш код чистым и простым в обслуживании.

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

Допустим, например, что в наш метод встроен цикл for:

    Чтобы вызвать мастер извлечения метода, нам нужно выполнить следующие шаги:

Выберите строки кода, которые мы хотите извлечь Щелкните правой кнопкой мыши выделенную область Выберите параметр Рефакторинг \u003e Метод извлечения

Последние два шага также можно выполнить с помощью сочетания клавиш Alt+Shift+M. Давайте посмотрим на диалоговое окно Extract Method:

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. Извлечение локальных переменных

Мы можем извлечь определенные элементы в качестве локальных переменных, чтобы сделать наш код более читаемым.

public class Test {

    public static void main(String[] args) {
        System.out.println("Number of Arguments passed =" + args.length);
    }
}

Это удобно, когда у нас есть строковый литерал:

и мы хотим извлечь его в локальную переменную.

    Для этого нам нужно:

Выбрать элемент Щелкните правой кнопкой мыши и выберите Рефакторинг \u003e Извлечь локальную переменную

Последний шаг также можно выполнить с помощью сочетания клавиш Alt+Shift+L. Теперь мы можем извлечь нашу локальную переменную:

public class Test {

    public static void main(String[] args) {
        final String prefix = "Number of Arguments passed =";
        System.out.println(prefix + args.length);
    }
}

А вот результат рефакторинга:

3.6. Извлечь константу

Или мы можем извлечь выражения и литеральные значения в статические окончательные атрибуты класса.

public class MathUtil {

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

Мы могли бы извлечь значение 3.14 в локальную переменную, как мы только что видели:

    Но может быть лучше извлечь его как константу, для чего нам нужно:

Выбрать элемент Щелкните правой кнопкой мыши и выберите Refactor \u003e Extract Constant

Откроется диалоговое окно, в котором мы можем дать константе имя и настроить ее видимость, а также несколько других параметров:

public class MathUtil {

    private static final double PI = 3.14;

    public double circumference(double radius) {
        return 2 * PI * radius;
    }
}

Теперь наш код выглядит немного более читабельным. :

4. Инлайнинг

Мы также можем пойти другим путем и инлайнить код.

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
}

Рассмотрим класс Util, который имеет локальную переменную, которая используется только один раз:

    Мы хотим удалить локальную переменную результата и встроить вызов метода isPrime. Для этого выполните следующие шаги:

Выберите элемент, который мы хотим встроить. Щелкните правой кнопкой мыши и выберите параметр «Рефакторинг» \u003e «Встроить»

Последний шаг также можно выполнить с помощью сочетания клавиш Alt+Shift+I: ~~ ~ После этого у нас есть на одну переменную меньше, которую нужно отслеживать:

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/pull, предоставляемые Eclipse.

Как следует из названия, параметр «Вниз» перемещает методы и поля из родительского класса во все дочерние классы, а параметр «Вытягивание вверх» перемещает методы и поля из определенного дочернего класса в родительский, что делает этот метод доступным для всех дочерних классов. .

Чтобы переместить методы в дочерние классы, нам нужно щелкнуть правой кнопкой мыши в любом месте класса и выбрать параметр Refactor \u003e Push Down:

Это откроет мастер, в котором мы можем выбрать элементы для push down: ~~ ~ Аналогично, для перемещения методов из дочернего класса в родительский нам нужно щелкнуть правой кнопкой мыши в любом месте класса и выбрать Refactor \u003e Pull Up:

Откроется аналогичный мастер, в котором мы можем выбрать элементы для извлечения:

6. Изменение сигнатуры метода

Чтобы изменить сигнатуру существующего метода, мы можем выполнить несколько простых шагов:

Выберите метод или поместите курсор где-нибудь внутри Щелкните правой кнопкой мыши и выберите Refactor \u003e Change Подпись метода

    Последний шаг также можно выполнить с помощью сочетания клавиш Alt+Shift+C.

Откроется всплывающее окно, в котором вы можете соответствующим образом изменить сигнатуру метода:

7. Перемещение

Иногда мы просто хотим переместить методы в другой существующий класс, чтобы сделать наш код более объектно-ориентированным.

«Рассмотрим сценарий, в котором у нас есть класс Movie:

А MovieType — это простое перечисление:

public class Movie {

    private String title;
    private double price;
    private MovieType type;

    // other methods
}

Предположим также, что у нас есть требование, согласно которому, если клиент берет напрокат НОВЫЙ фильм, он будет на два доллара больше, и что наш класс Customer имеет следующую логику для вычисления totalCost(): класс Клиент. Мы можем легко переместить эту логику вычислений в Eclipse:

public enum MovieType {
    NEW, REGULAR
}

Выберите строки, которые вы хотите переместить Щелкните правой кнопкой мыши и выберите параметр Refactor \u003e Move

public class Customer {

    private String name;
    private String address;
    private List<Movie> 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
}

Последний шаг также можно выполнить с помощью сочетания клавиш Alt+Shift+V: ~ ~~ Eclipse достаточно умен, чтобы понять, что эта логика должна быть в нашем классе Movie. Мы можем изменить имя метода, если захотим, наряду с другими дополнительными параметрами.

    Окончательный код класса Customer будет изменен на:

Как мы видим, метод movieCost был перемещен в наш класс Movie и используется в рефакторинге класса Customer.

8. Заключение

В этом руководстве мы рассмотрели некоторые основные методы рефакторинга, предоставляемые Eclipse. Мы начали с базового рефакторинга, такого как переименование и извлечение. Позже мы увидели перемещение методов и полей по разным классам.

public class Customer {

    private String name;
    private String address;
    private List<Movie> movies;

    public double totalCost() {
        double result = 0;
        for (Movie movie : movies) {
            result += movie.movieCost();
        }
        return result;
    }

    // other methods
}

Чтобы узнать больше, мы всегда можем обратиться к официальной документации Eclipse по рефакторингу.

«

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.