Эту статью можно рассматривать как краткий обзор 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<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); }}
Рис.
5. Переименование переменнойИспользованный код
До
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); }}
Рис. 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 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 { }}
Рис. 7.
Переименование классаИспользованный код
До
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(); }}
Рис. 8.
Переименование пакетаИспользованный код
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), искать ли ссылки
или в комментариях и строчках
Рис. 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<caret> 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) { 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)
Рис. 13. Пример использования Make StaticИспользованный код
До
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). При этом можно
указать к какому классу будет относится новый метод.
Рис. 14. Пример использования Convert To
Instance MethodИспользованный код
До
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)
В принципе делает, что и написано, перемещает классы.
Рис. 15. Пример использования Move ClassesИспользованный код
До
package org.example.test.service;public class TestService {<caret>}
После
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() { }}
После
<empty>
Пункт Extract/Introduce
Следующий блок - Extract/Introduce. Думаю, является одним из
самых популярных. Позволяет извлекать разные части программы.
Рис. 18. Список доступных способов
рефакторинга Extract/Introduce
Пункт Variable (Ctrl+Alt+V)
Создает новую переменную из выделенного фрагмента. (Этому
способу у Фаулера посвящена глава Extract Variable).
Рис. 19. Пример использования
Extract/Introduce->VariableИспользованный код
До
public class ExtractVariable { public static void main(String[] args) { sayHello(); } private static void sayHello() { System.out.println("He<caret>llo, World!"); }}
После
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)
Создает новую константу из выделенного фрагмента.
Рис. 20. Пример использования
Extract/Introduce->ConstantИспользованный код
До
public class ExtractVariable { public static void main(String[] args) { sayHello(); } private static void sayHello() { System.out.println("He<caret>llo, World!"); }}
После
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)
Создает новое поле класса из выделенного фрагмента.
Рис. 21. Пример использования
Extract/Introduce->FieldИспользованный код
До
public class ExtractVariable { public static void main(String[] args) { sayHello(); } private static void sayHello() { System.out.println("He<caret>llo, World!"); }}
После
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); }}
Пункт Parameter (Ctrl+Alt+P)
Создает новый параметр (функции) из выделенного фрагмента.
Рис. 22. Пример использования
Extract/Introduce->ParameterИспользованный код
До
public class ExtractVariable { public static void main(String[] args) { sayHello(); } private static void sayHello() { System.out.println("He<caret>llo, World!"); }}
После
public class ExtractVariable { public static void main(String[] args) { sayHello("Hello, World!"); } private static void sayHello(String x) { System.out.println(x); }}
Пункт Functional Parameter
Очень похож на пункт Parameter, но теперь в
функцию мы передаем или java.util.function.Supplier, или
javafx.util.Builder. Обратите внимание, данный рефакторинг может
привести к нежелательным эффектам.
Рис. 23. Пример использования
Extract/Introduce->Functional ParameterИспользованный код
До
public class ExtractParameter { public static void main(String[] args) { System.out.println(generateText()); } private static String generateText() { return "Hello, Wor<caret>ld!".toUpperCase(); }}
После
public class ExtractParameter { public static void main(String[] args) { System.out.println(generateText(() -> "Hello, World!")); } private static String generateText(final Supplier<string> getText) { return getText.get().toUpperCase(); }}
Пункт Functional Variable
Очень похож на пункт Variable, но теперь мы
получаем или java.util.function.Supplier или
javafx.util.Builder.
Рис. 24. Пример использования
Extract/Introduce->Functional Variable Использованный код
До
public class ExtractParameter { public static void main(String[] args) { System.out.println(generateText()); } private static String generateText() { return "Hello, W<caret>orld!".toUpperCase(); }}
После
public class ExtractParameter { public static void main(String[] args) { System.out.println(generateText()); } private static String generateText() { Supplier<string> getText = () -> "Hello, World!"; return getText.get().toUpperCase(); }}
ParameterObject
Пункт Parameter Object
Удобный способ, когда в функцию передается много аргументов и
вам надо обернуть их в класс. (У Фаулера это глава Introduce
Parameter Object).
Рис. 25. Пример использования
Extract/Introduce->Parameter Object Использованный код
До
public class ExtractParameter { public static void main(String[] args) { System.out.println(generateText("Hello", "World!")); } private static String generateText(Str<caret>ing hello, String world) { return hello.toUpperCase() + world.toUpperCase(); }}
После
public class ExtractParameter { public static void main(String[] args) { System.out.println(generateText(new HelloWorld("Hello", "World!"))); } private static String generateText(HelloWorld helloWorld) { return helloWorld.getHello().toUpperCase() + helloWorld.getWorld().toUpperCase(); } private static class HelloWorld { private final String hello; private final String world; private HelloWorld(String hello, String world) { this.hello = hello; this.world = world; } public String getHello() { return hello; } public String getWorld() { return world; } }}
Пункт Method (Ctrl+Alt+M)
Извлекаем метод из выделенного фрагмента. (У Фаулера есть глава
про похожий способ рефакторинга - Extract Function).
Рис. 26. Пример использования
Extract/Introduce->Method Использованный код
До
public class ExtractMethod { public static void main(String[] args) { String text = "Hello, World!"; System.out.prin<caret>tln(text); }}
После
public class ExtractMethod { public static void main(String[] args) { String text = "Hello, World!"; print(text); } private static void print(String text) { System.out.println(text); }}
Пункт Type Parameter
Рефакторинг из мира Kotlin, и для Java не применим (буду рад
добавить, если кто-то сделает пример).
Пункт Replace Method With Method Object
Оборачивает выделенный фрагмент в объект. Может использоваться,
если надо вернуть несколько объектов из метода (возвращает
объект-обертку).
Рис. 27. Пример использования
Extract/Introduce->Replace Method With Method Object
Использованный код
До
public class ExtractMethod { public static void main(String[] args) { String text = "Hello, World!"; print(text); } private static void print(String text) { System.out.p<caret>rintln(text); }}
После
public class ExtractMethod { public static void main(String[] args) { String text = "Hello, World!"; print(text); } private static void print(String text) { new Printer(text).invoke(); } private static class Printer { private String text; public Printer(String text) { this.text = text; } public void invoke() { System.out.println(text); } }}
Пункт Delegate
Позволяет извлечь методы и поля в отдельный класс.
Рис. 28. Пример использования
Extract/Introduce->Delegate Использованный код
До
public class Delegate { public static void main(String[] args) { new Delegate().print(); } private void print() { System.ou<caret>t.println("Hello, World!"); }}
После
public class Delegate { private final Printer printer = new Printer(); public static void main(String[] args) { new Delegate().print(); } private void print() { printer.print(); } public static class Printer { public Printer() { } private void print() { System.out.println("Hello, World!"); } }}
Пункт Interface
Для заданного класса и его методов создает интерфейс. (Особенно
удобно, когда при работе со Spring, когда кто-то забыл для
компонента создать соответствующий интерфейс)
Рис. 29. Пример использования
Extract/Introduce->Interface Использованный код
До
public class ExtractImpl { public static void main(String[] args) { new ExtractImpl().print(); } public void print() { System.out.println("Hello, World!"); }}
После
public class ExtractImpl implements ExtractInterface { public static void main(String[] args) { new ExtractImpl().print(); } @Override public void print() { System.out.println("Hello, World!"); }}public interface ExtractInterface { void print();}
Пункт Superclass
Аналогично пункту Interface, только теперь
создается класс-родитель (Superclass). Фаулер описывает этот способ
рефакторинга в главе Extract Superclass.
Рис. 30. Пример использования
Extract/Introduce->Superclass Использованный код
До
public class ExtractImpl { public static void main(String[] args) { new ExtractImpl().print(); } public void print() { System.out.println("Hello, World!"); }}
После
public class ExtractImpl extends ExtractAbstr { public static void main(String[] args) { new ExtractImpl().print(); }}public class ExtractAbstr { public void print() { System.out.println("Hello, World!"); }}
Пункт Subquery as CTE
Относится к Sql, поэтому пропускаю. Если кто-то пришлет пример,
хотя бы в виде кода - с удовольствием дополню.
Пункт RSpec 'let'
Относится к Ruby, поэтому пропускаю Если кто-то пришлет пример,
хотя бы в виде кода - с удовольствием дополню.
Пункт Inline
Возможно один из самых крутых методов рефакторинга, Инлайнить
можно почти все. Фаулер описывает этот способ рефакторинга в главах
Inline Class, Inline Function, Inline Variable.
Рис. 31. Пример использования пункта InlineИспользованный
код
До
public class Inline { public static void main(String[] args) { print(); } private static void print() { new Printer().print(); } private static class Printer { public void print() { String text = "Hello, World!"; System.out.println(t<caret>ext); } }}
После
public class Inline { public static void main(String[] args) { System.out.println("Hello, World!"); }}
Пункт Find and Replace code duplicate
Ищет похожие фрагменты кода и предлагает заменить их, например,
вызовом метода или константой.
Рис. 32. Пример использования пункта Find
and Replace code duplicateИспользованный код
До
public class Replace { public static void main(String[] args) { System.out.println("Hello, World!"); } public void print() { System.out.println("Hello, World!"); } public void print2() { System.out.prin<caret>tln("Hello, World!"); }}
После
public class Replace { public static void main(String[] args) { print2(); } public void print() { print2(); } public static void print2() { System.out.println("Hello, World!"); }}
Пункт Invert Boolean
Позволяет инвертировать булевые переменные.
Рис. 33. Пример использования пункта Invert
BooleanИспользованный код
До
public class Invert { public static void main(String[] args) { boolean co<caret>ndition = true; if (condition) { System.out.println("Hello, World!"); } }}
После
public class Invert { public static void main(String[] args) { boolean condition = false; if (!condition) { System.out.println("Hello, World!"); } }}
Пункт Pull Member Up
Позволяет перемещать методы или поля по иерархии вверх. Зачем
это нужно написано у Фаулера в главах Pull Up Field и Pull Up
Method. Выполняет обратную задачу пункта Pull Member Down.
Рис. 34. Пример использования пункта Pull Member
UpИспользованный код
До
public class PullMethod { public static void main(String[] args) { new InnerClass().print(); } private static class InnerClass extends AbstClass { public void print() { System.out.pri<caret>ntln("Hello, World"); } } private static abstract class AbstClass { }}
После
public class PullMethod { public static void main(String[] args) { new InnerClass().print(); } private static class InnerClass extends AbstClass { } private static abstract class AbstClass { public void print() { System.out.println("Hello, World"); } }}
Пункт Pull Member Down
Выполняет обратную задачу пункта Pull Member Up. Позволяет
перемещать методы или поля по иерархии вниз. (У Фаулера - глава
Push Down Method)
Рис. 35. Пример использования пункта Pull Member
DownИспользованный код
До
public class PullMethod { public static void main(String[] args) { new InnerClass().print(); } private static class InnerClass extends AbstClass { } private static abstract class AbstClass { public void print() { System.out.prin<caret>tln("Hello, World"); } }}
После
public class PullMethod { public static void main(String[] args) { new InnerClass().print(); } private static class InnerClass extends AbstClass { @Override public void print() { System.out.println("Hello, World"); } } private static abstract class AbstClass { public abstract void print(); }}
Пункт Push ITds In
Используется при работе с AsperctJ.
Рис. 36. Пример использования пункта Push ITds
InИспользованный код
До
aspect myAspect { boolean Account.closed = <caret>false; void Account.close() { closed = true; }}class Account {}
После
aspect myAspect { boolean Account.closed = false;}class Account { void close() { closed = true; }}
Пункт Use Interface Where Possible
IDEA старается заменить, где это возможно, указания классов на
указание интерфейсов.
Рис. 37. Пример использования пункта Use
Interface Where PossibleИспользованный код
До
public class ExtractInterface { public static void main(String[] args) { InnerClass innerClass = new InnerClass(); print(innerClass); } private static void print(InnerClass innerClass) { innerClass.print(); } private static class InnerClass implements InnerInterface{ @Override public void print() { System.out.println("Hello, World!"); } } private static interface InnerInterface{ void print(); }}
После
public class ExtractInterface { public static void main(String[] args) { InnerInterface innerClass = new InnerClass(); print(innerClass); } private static void print(InnerInterface innerClass) { innerClass.print(); } private static class InnerClass implements InnerInterface{ @Override public void print() { System.out.println("Hello, World!"); } } private static interface InnerInterface{ void print(); }}
Пункт Replace Inheritance with Delegation
Заменяет наследование делегированием. У Фаулера про это главы
Replace Subclass with Delegate и Replace Superclass with
Delegate.
Рис. 38. Пример использования пункта
Replace Inheritance with DelegationИспользованный код
До
public class InheritanceDelegation { public static void main(String[] args) { InnerClass innerClass = new InnerClass(); print(innerClass); } private static void print(InnerClass innerClass) { innerClass.print(); } private static class In<caret>nerClass extends AbstractClass { } private static class AbstractClass { public void print() { System.out.println("Hello, World!"); } }}
После
public class InheritanceDelegation { public static void main(String[] args) { InnerClass innerClass = new InnerClass(); print(innerClass); } private static void print(InnerClass innerClass) { innerClass.print(); } private static class InnerClass { private final AbstractClass abstractClass = new AbstractClass(); public void print() { abstractClass.print(); } } private static class AbstractClass { public void print() { System.out.println("Hello, World!"); } }}
Пункт Remove Middleman
Заменяет все делегированные вызовы на прямые. (У Фаулера - глава
Remove Middle Man).
Рис. 39. Пример использования пункта Remove
MiddlemanИспользованный код
До
public class Middleman { public static void main(String[] args) { InnerClass innerClass = new InnerClass(); innerClass.print(); } private static class InnerClass { private final NextClass next<caret>Class = new NextClass(); public void print() { nextClass.print(); } } private static class NextClass { public void print() { System.out.println("Hello, World!"); } }}
После
public class Middleman { public static void main(String[] args) { InnerClass innerClass = new InnerClass(); innerClass.getNextClass().print(); } private static class InnerClass { private final NextClass nextClass = new NextClass(); public NextClass getNextClass() { return nextClass; } } private static class NextClass { public void print() { System.out.println("Hello, World!"); } }}
Пункт Wrap Method Return Value
Оборачивает возвращаемое значение в объект-обертку. Удобно,
когда нужно возвращать несколько связанных значений.
Рис. 40. Пример использования пункта Wrap
Method Return ValueИспользованный код
До
public class WrapMethodReturnValue { public static void main(String[] args) { System.out.println(new MessageFolder().get()); } private static class MessageFolder { public String get() { ret<caret>urn "Hello, World!"; } }}
После
public class WrapMethodReturnValue { public static void main(String[] args) { System.out.println(new MessageFolder().get().getValue()); } private static class MessageFolder { public Message get() { return new Message("Hello, World!"); } public class Message { private final String value; public Message(String value) { this.value = value; } public String getValue() { return value; } } }}
Пункт Encapsulate Field
Скрывает поле за getter, setter.
Рис. 41. Пример использования пункта
Encapsulate FieldИспользованный код
До
public class EncapsulateField { public static void main(String[] args) { System.out.println(new InnerClass().message); } private static class InnerClass { public String m<caret>essage = "Hello, World!"; }}
После
public class EncapsulateField { public static void main(String[] args) { System.out.println(new InnerClass().getMessage()); } private static class InnerClass { private String message = "Hello, World!"; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }}
Пункт Replace Temp with Query
Пусть у вас есть
int size = getActualSize()
с помощью этого способа рефакторинга вы можете заменить
использование size на использование метода
getActualSize(). Это приведет к увеличению количества
вызовов методов, но в некоторых случаях может оказаться полезным.
Пример так же показывает возможность извлечения метода с
одновременным его дальнейшим использованием.
Рис. 42. Пример использования пункта
Replace Temp with QueryИспользованный код
До
public class ReplaceTemp { public static void main(String[] args) { String hello = "Hello"; String mes<caret>sage = hello + ", World!"; System.out.println(message); }}
После
public class ReplaceTemp { public static void main(String[] args) { String hello = "Hello"; System.out.println(message(hello)); } private static String message(String hello) { return hello + ", World!"; }}
Пункт Replace Constructor with Factory Method
Генерирует фабричный метод для указанного конструктора.
Идеально, если у вас нет Lombok. (У Фаулера этому посвящена глава
Replace Constructor with Factory Function).
Рис. 43. Пример использования пункта
Replace constructor with factory methodИспользованный код
До
public class ReplaceConstructor { public static void main(String[] args) { new InnerClass("Hello", "World").print(); } private static class InnerClass { private String message; public Inner<caret>Class(String hello, String world) { message = hello + ", " + world; } public void print() { System.out.println(message); } }}
После
public class ReplaceConstructor { public static void main(String[] args) { InnerClass.createInnerClass("Hello", "World").print(); } private static class InnerClass { private String message; private InnerClass(String hello, String world) { message = hello + ", " + world; } public static InnerClass createInnerClass(String hello, String world) { return new InnerClass(hello, world); } public void print() { System.out.println(message); } }}
Пункт Replace Constructor with Builder
Генерирует builder для указанного конструктора. Идеально, если у
вас нет Lombok.
Рис. 44. Пример использования пункта
Replace Constructor with BuilderИспользованный код
До
public class ReplaceConstructor { public static void main(String[] args) { new InnerClass("Hello", "World").print(); } private static class InnerClass { private String message; public InnerC<caret>lass(String hello, String world) { message = hello + ", " + world; } public void print() { System.out.println(message); } }}
После
public class ReplaceConstructor { public static void main(String[] args) { new InnerClassBuilder().setHello("Hello").setWorld("World").createInnerClass().print(); } static class InnerClass { private String message; public InnerClass(String hello, String world) { message = hello + ", " + world; } public void print() { System.out.println(message); } }}public class InnerClassBuilder { private String hello; private String world; public InnerClassBuilder setHello(String hello) { this.hello = hello; return this; } public InnerClassBuilder setWorld(String world) { this.world = world; return this; } public ReplaceConstructor.InnerClass createInnerClass() { return new ReplaceConstructor.InnerClass(hello, world); }}
Пункт Generify
Пытается код с raw-типами превратить в код с Generic-типами.
Актуален при миграции с java версий ранее 1.5 на современные
версии.
Рис. 45. Пример использования пункта GenerifyИспользованный
код
До
public class Generify { public static void main(String[] args) { List list = getList(); Object message = list.get(0); System.out.println(message); } private static List getList() { ArrayList arrayList = new ArrayList(); arrayList.add("Hello, World!"); return arrayList; }}
После
public class Generify { public static void main(String[] args) { List<string> list = getList(); String message = list.get(0); System.out.println(message); } private static List<string> getList() { ArrayList<string> arrayList = new ArrayList<>(); arrayList.add("Hello, World!"); return arrayList; }}
Пункт Migrate
Предоставляет готовые миграции для следующего списка:
Рис. 46. Список доступных миграций пункта Migrate
А также предоставляет возможность делать свои. Вот, например,
правила миграции для JUnit(4.x -> 5.0):
Рис. 47. Правила миграции для JUnit(4.x -> 5.0)
Вот здесь есть подробное видео
про миграцию для JUnit(4.x -> 5.0).
Пункт Lombok и Delombok
Предоставляются плагином Lombok. Недавно было объявлено, что
теперь он будет входить в стандартную поставку IDEA. Используется
при работе с библиотекой кодогенерации Lombok.
Пункт Internationalize
Используется для интернационализации. К сожалению, в справке не
нашел на данный момент информации. IDEA сейчас активно локализуется
на другие языки, скорее всего для этого был разработан этот
метод.
Список источников