Цель данной статьи — показать доступные способы рефакторинга для Java-файлов (многие способы будут работать и для других языков). Как использовать эти приемы в реальной жизни показано в замечательном видео Тагира Валеева (ссылка в списке источников).
Думаю, каждый, кто работает в IDEA, знает, что в ней куча способов для рефакторинга кода. И почти уверен, что каждый второй смотрит анонсы новой версии, где красиво показаны новые способы рефакторинга и заглядывал в раздел Refaсtor:
Но не уверен, что все точно знают что и как делают все элементы этого списка, хотя они все детально описаны в справки к idea
В статье представлены фрагменты кода, порядок действий и анимации почти для каждого пункта. Также постарался добавить, где возможно, ссылку на замечательную книгу Refactoring: Improving the Design of Existing Code (Martin Fowler). Чтобы не сильно раздувать трафик пришлось довольно сильно обрезать много gif-картинок, поэтому обязательно смотрите использованный код под катом. Горячие клавиши приведены для Windows/LInux по умолчанию.
Раздел «Refaсtor»
Пойдем сверху вниз по порядку.
Пункт «Refactor This» (Ctrl+Alt+Shift+T)
Данный пункт используется для быстрого доступа к списку доступных способов рефакторинга. Заметьте, список зависит от места, где вы его вызываете. Здесь и далее в коде указывает на место каретки в коде, при вызове.
Пункт «Rename» (Shift+F6)
Позволяет переименовать практически любой идентификатор в коде, будь то переменная или названия класса. Изменения распространяются по всему проекту, в некоторых случаях, включая и комментарии. (У Фаулера переименованию посвящено 2 главы — “Rename Field” и “Rename Variable”)
Использованный код
До
public class Main { public static void main(String[] args) { System.out.print("Hello"); invo<caret/>ke(", World"); } private static void invoke(String text) { //text System.out.println(text); } }
После
public class Main { public static void main(String[] args) { System.out.print("Hello"); newFunctionName(", World"); } private static void newFunctionName(String text) { //text System.out.println(text); } }
Переименование переменной
Использованный код
До
public class Main { public static void main(String[] args) { System.out.print("Hello"); invoke(", World"); } private static void invoke(String te<caret>xt) { //text System.out.println(text); } }
После
public class Main { public static void main(String[] args) { System.out.print("Hello"); invoke(", World"); } private static void invoke(String newText) { //newText System.out.println(newText); } }
Переименование вложенного класса
Использованный код
До
public class Main { public static void main(String[] args) { System.out.print("Hello"); invoke(", World"); } private static void invoke(String text) { //text System.out.println(text); throw new MyExc<caret>eption(); } public static class MyException extends RuntimeException { } }
После
public class Main { public static void main(String[] args) { System.out.print("Hello"); invoke(", World"); } private static void invoke(String text) { //text System.out.println(text); throw new NewMyException (); } public static class NewMyException extends RuntimeException { } }
Переименование класса
Использованный код
До
public class Main { public static void main(String[] args) { MyS<caret>ervice service = new MyService(); service.service(); } }
После
public class Main { public static void main(String[] args) { NewMyService myService = new NewMyService (); myService.service(); } }
Переименование пакета
Использованный код
package gen<caret>eral; public class Main { public static void main(String[] args) { NewMyService service = new NewMyService(); service.service(); } }
После
package org.test.java.src; public class Main { public static void main(String[] args) { NewMyService service = new NewMyService(); service.service(); } }
Пункт «Rename File»
Переименовывает файл и ссылки на этот файл. В принципе можно вызывать через Shift+F6 если выделен файл. В диалоговом окне можно указать область поиска для переименований (Scope), искать ли ссылки или в комментариях и строчках
Использованный код
До
public class Main { public static void main(String[] args) throws IOException { Path path = Paths.get("src/general/TestFile.txt"); String read = Files.readAllLines(path).get(0); System.out.println(read); } }
После
public class Main { public static void main(String[] args) throws IOException { Path path = Paths.get("src/general/TestFile2.txt"); String read = Files.readAllLines(path).get(0); System.out.println(read); } }
Пункт «Change Signature» (Ctrl+F6)
У Фаулера этому посвящена глава “Change Function Declaration”. В новой версии IDEA «Change Signature» был немного доработан. Я знаю два пути:
первый путь — прямой — изменить сигнатуру метода и вызвать обновление,
второй — через диалоговое окно.
Пример для первого способа (через изменения сигнатуры метода)
Изменяем сигнатуру метода. В этот момент слева появляется «R» и в контекстном меню появляется пункт «Update usages to reflect signature change», который позволяет обновить все использования метода.
Использованный код
До
public class ChangeSignature { public static void main(String[] args) { invokeMethod("Hello"); invokeMethod("World"); } private static void invokeMethod(String text) { System.out.println(text); } }
После
public class ChangeSignature { public static void main(String[] args) { invokeMethod("Hello", null); invokeMethod("World", null); } private static void invokeMethod(String text, String newType) { System.out.println(text); } }
Пример для второго способа (через диалоговое окно)
В диалоговом окне можно изменить состав переменных, exception, и даже сгенерировать переопределенный метод.
Использованный код
До
public class ChangeSignature { public static void main(String[] args) { invokeMethod("Hello"); invokeMethod("World"); } private static void invokeMethod(String<caret> text) { System.out.println(text); } }
На данный момент (Idea 2020.2) экспериментальная функция и по умолчанию не включена. Включить можно параметром property.value.inplace.editing=true Поэтому примеры не привожу.
Пункт «Type Migration» (Ctrl + Shift + F6)
Позволяет изменить тип переменной, включая сигнатуры методов, возвращаемый тип переменной.
Использованный код
До
public class ChangeSignature { public static void main(String[] args) { Inte<caret>ger hello = 1; print(hello); } private static void print(Integer text) { System.out.println(text); } }
После
public class ChangeSignature { public static void main(String[] args) { Number hello = 1; print(hello); } private static void print(Number text) { System.out.println(text); } }
Пункт «Make Static» (Ctrl + Shift + F6)
Позволяет сконвертировать метод или внутренний класс в статический. (Противоположность Convert To Instance Method)
Использованный код
До
public class MakeStatic { public static void main(String[] args) { MakeStatic makeStatic = new MakeStatic(); makeStatic.sayHello(); } public void say<caret>Hello() { System.out.println("Hello, World"); } }
После
public class MakeStatic { public static void main(String[] args) { MakeStatic makeStatic = new MakeStatic(); MakeStatic.sayHello(); } public static void sayHello() { System.out.println("Hello, World"); } }
Пункт «Convert To Instance Method»
Позволяет сконвертировать статический метод в нестатический (противоположность ”Make Static”). При этом можно указать к какому классу будет относится новый метод.
Использованный код
До
public class MakeStatic { public static void main(String[] args) { sayHello(); } public static void sa<caret>yHello() { System.out.println("Hello, World"); } }
После
public class MakeStatic { public static void main(String[] args) { new MakeStatic().sayHello(); } public void sayHello() { System.out.println("Hello, World"); } }
Пункт «Move Classes» (F6)
В принципе делает, что и написано, перемещает классы.
Использованный код
До
package org.example.test.service; public class TestService { <caret> }
После
package org.example.test; public class TestService { }
Пункт «Copy Classes» (F5)
Многие программисты любят копировать файлы, а не начинать с чистого листа. Для этого прекрасно подходит F5. Меняем название на нужноe, указываем пакет и готово.
Пункт «Safe Delete» (Alt+Delete)
По функциональности почти повторяет то, что можно получить через контекстное меню (Alt + Enter), но позволяет удалять чуть больше. Поэтому, я заметил, у многих знакомых любимый способ рефакторинга — F2(следующая ошибка) и Alt + Enter или Alt + Delete. Можно удалять классы, переменные, методы. Перед удалением IDEA выполнит поиск использования удаляемых элементов, и если IDEA найдет, что они где-то используется покажет диалоговое окно Usages Detected. Про удаление неиспользуемого кода у Фаулера есть целая глава — “Remove Dead Code”
Добавить комментарий