Раздел «Refactor» в IDEA
Эту статью можно рассматривать как краткий обзор c gif-ками по рефакторингам Java-файлов в IDEA для начинающих.
Осторожно, много тяжелых gif-картинок.
«Any fool can write code that a computer can understand. Good programmers write code that humans can understand.» —M. Fowler (1999)
Содержание
Введение
Раздел «Refaсtor»
— Refactor This
— Rename
— Rename File
— Change Signature
— Edit Property Value (без примера)
— Type Migration
— Make Static
— Convert To Instance Method
— Move Classes
— Copy Classes
— Safe Delete
— Extract/Introduce
- — Variable
- — Constant
- — Field
- — Parameter
- — Functional Parameter
- — Functional Variable
- — Parameter Object
- — Method
- — Type Parameter (без примера)
- — Interface
- — Superclass
- — Subquery as CTE (без примера)
- — RSpec 'let' (без примера)
— Inline
— Find and Replace Code Duplicate
— Pull Member Up
— Pull Member Down
— Push ITds In
— Use Interface Where Possible
— Replace Inheritance with Delegation
— Remove Middleman
— Wrap Method Return Value
— Encapsulate Field
— Replace Temp with Query
— Replace Constructor with Factory Method
— Replace Constructor with Builder
— Generify
— Migrate
— Lombok и Delombok (без примера)
— Internationalize Список источников
Введение
Цель данной статьи — показать доступные способы рефакторинга для Java-файлов (многие способы будут работать и для других языков). Как использовать эти приемы в реальной жизни показано в замечательном видео Тагира Валеева (ссылка в списке источников).
Думаю, каждый, кто работает в IDEA, знает, что в ней куча способов для рефакторинга кода. И почти уверен, что каждый второй смотрит анонсы новой версии, где красиво показаны новые способы рефакторинга и заглядывал в раздел Refaсtor:
Рис. 1. Раздел Refactoring IDEA
Но не уверен, что все точно знают что и как делают все элементы этого списка, хотя они все детально описаны в справки к idea
В статье представлены фрагменты кода, порядок действий и анимации почти для каждого пункта. Также постарался добавить, где возможно, ссылку на замечательную книгу Refactoring: Improving the Design of Existing Code (Martin Fowler). Чтобы не сильно раздувать трафик пришлось довольно сильно обрезать много gif-картинок, поэтому обязательно смотрите использованный код под катом. Горячие клавиши приведены для Windows/LInux по умолчанию.
Раздел «Refaсtor»
Пойдем сверху вниз по порядку.
Пункт «Refactor This» (Ctrl+Alt+Shift+T)
Данный пункт используется для быстрого доступа к списку доступных способов рефакторинга. Заметьте, список зависит от места, где вы его вызываете. Здесь и далее в коде указывает на место каретки в коде, при вызове.
Рис. 2. «Refactor This» для имени функцииРис. 3. «Refactor This» для аргументов функции
Пункт «Rename» (Shift+F6)
Позволяет переименовать практически любой идентификатор в коде, будь то переменная или названия класса. Изменения распространяются по всему проекту, в некоторых случаях, включая и комментарии. (У Фаулера переименованию посвящено 2 главы — «Rename Field» и «Rename Variable»)
Рис. 4. Переименование методаИспользованный код
До
public class Main {
public static void main(String[] args) {
System.out.print("Hello");
invo 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);
}
}
Рис. 5. Переименование переменнойИспользованный код
До
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);
}
}
После
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);
}
}
Рис. 6. Переименование вложенного классаИспользованный код
До
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 MyException();
}
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 {
}
}
Рис. 7. Переименование классаИспользованный код
До
public class Main {
public static void main(String[] args) {
MyService service = new MyService();
service.service();
}
}
После
public class Main {
public static void main(String[] args) {
NewMyService myService = new NewMyService ();
myService.service();
}
}
Рис. 8. Переименование пакетаИспользованный код
package general;
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), искать ли ссылки или в комментариях и строчках
Рис. 9. Пример использования «Rename File«Использованный код
До
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», который позволяет обновить все использования метода.
Рис. 10. Пример использования «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, и даже сгенерировать переопределенный метод.
Рис. 11. Пример использования «Change Signature«Использованный код
До
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");
invokeMethod("World");
}
private static void invokeMethod(String text) {
invokeMethod(text, null);
}
private static void invokeMethod(String text, String newName) {
System.out.println(text);
}
}
Пункт «Edit Property Value» (Alt + F6)
На данный момент (Idea 2020.2) экспериментальная функция и по умолчанию не включена. Включить можно параметром property.value.inplace.editing=true Поэтому примеры не привожу.
Пункт «Type Migration» (Ctrl + Shift + F6)
Позволяет изменить тип переменной, включая сигнатуры методов, возвращаемый тип переменной.
Рис. 12. Пример использования «Type Migration«Использованный код
До
public class ChangeSignature {
public static void main(String[] args) {
Integer 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)
Рис. 13. Пример использования «Make Static«Использованный код
До
public class MakeStatic {
public static void main(String[] args) {
MakeStatic makeStatic = new MakeStatic();
makeStatic.sayHello();
}
public void sayHello() {
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»). При этом можно указать к какому классу будет относится новый метод.
Рис. 14. Пример использования «Convert To Instance Method«Использованный код
До
public class MakeStatic {
public static void main(String[] args) {
sayHello();
}
public static void sayHello() {
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)
В принципе делает, что и написано, перемещает классы.
Рис. 15. Пример использования «Move Classes«Использованный код
До
package org.example.test.service;
public class TestService {
}
После
package org.example.test;
public class TestService {
}
Пункт «Copy Classes» (F5)
Многие программисты любят копировать файлы, а не начинать с чистого листа. Для этого прекрасно подходит F5. Меняем название на нужноe, указываем пакет и готово.
Рис. 16. Пример использования «Copy Classes»
Пункт «Safe Delete» (Alt+Delete)
По функциональности почти повторяет то, что можно получить через контекстное меню (Alt + Enter), но позволяет удалять чуть больше. Поэтому, я заметил, у многих знакомых любимый способ рефакторинга — F2(следующая ошибка) и Alt + Enter или Alt + Delete. Можно удалять классы, переменные, методы. Перед удалением IDEA выполнит поиск использования удаляемых элементов, и если IDEA найдет, что они где-то используется покажет диалоговое окно Usages Detected. Про удаление неиспользуемого кода у Фаулера есть целая глава — «Remove Dead Code»
Рис. 17. Пример использования «Safe Delete«Использованный код
До
package org.example.test;
public class MainClass {
public static void main(String[] args) {
start();
}
private static void start() {
String unUsedVariable;
System.out.println("Hello, World!");
}
private static void unUsedMethod() {
}
}
После
Пункт «Extract/Introduce»
Следующий блок — Extract/Introduce. Думаю, является одним из самых популярных. Позволяет извлекать разные части программы.
Рис. 18. Список доступных способов рефакторинга «Extract/Introduce»
Пункт «Variable» (Ctrl+Alt+V)
Создает новую переменную из выделенного фрагмента. (Этому способу у Фаулера посвящена глава «Extract Variable»).
public class ExtractVariable {
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
System.out.println("He Послеpublic class ExtractVariable {
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
String text = "Hello, World!";
System.out.println(text);
}
}
Пункт «Constant» (Ctrl+Alt+C)
Создает новую константу из выделенного фрагмента.
public class ExtractVariable {
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
System.out.println("He Послеpublic class ExtractVariable {
public static final String HELLO_WORLD = "Hello, World!";
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
System.out.println(HELLO_WORLD);
}
}
Пункт «Field» (Ctrl+Alt+F)
Создает новое поле класса из выделенного фрагмента.
public class ExtractVariable {
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
System.out.println("He Послеpublic class ExtractVariable {
private static String x;
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
x = "Hello, World!";
System.out.println(x);
}
}