intellij-refactoring
IntelliJ IDEAを使用したリファクタリングの概要
-
link:/category/programming/ [プログラミング]
1. 概要
コードを整頓することは必ずしも簡単ではありません。 幸いなことに、最近のIDEは非常にスマートで、これを達成するのに役立ちます。 このチュートリアルでは、JetBrains JavaコードエディターであるIntelliJ IDEAに焦点を当てます。
変数の名前変更からメソッドシグネチャの変更まで、コードをリファクタリングするためにエディターによって提供されるいくつかの機能が表示されます。
2. 改名
2.1. 基本的な名前の変更
まず、基本から始めましょう:https://www.jetbrains.com/help/idea/2018.3/rename-dialogs.html[renaming]。 * IntelliJは、コードのさまざまな要素(型、変数、メソッド、さらにはパッケージ)の名前を変更する可能性を提供します。*
要素の名前を変更するには、次の手順を実行する必要があります。
-
要素を右クリック
-
* _Refactor> Rename_オプションをトリガー*
-
新しい要素名を入力します
-
_Enter_を押します
ちなみに、*最初の2つのステップは、要素を選択して_Shift + F6 _。*を押すことで置き換えることができます。
トリガーされると、名前変更アクションは*要素を使用するたびにコード全体を検索し、指定された値でそれらを変更します*。
_main_メソッドで呼び出される、名前が不十分な追加メソッド_someAdditionMethod_を持つ_SimpleClass_クラスを想像してみましょう。
public class SimpleClass {
public static void main(String[] args) {
new SimpleClass().someAdditionMethod(1, 2);
}
public int someAdditionMethod(int a, int b) {
return a + b;
}
}
したがって、このメソッドの名前を_add_に変更すると、IntelliJは次のコードを生成します。
public class SimpleClass() {
public static void main(String[] args) {
new SimpleClass().add(1, 2);
}
public int add(int a, int b) {
return a + b;
}
}
2.2. 高度な名前変更
ただし、IntelliJは、要素のコード使用を検索して名前を変更するだけではありません。 実際のところ、さらにいくつかのオプションが利用可能です。 IntelliJは、コメントや文字列、さらにはソースコードを含まないファイルの出現箇所も検索できます*。 パラメーターに関しては、オーバーライドされたメソッドの場合、クラス階層でそれらの名前を変更できます。
これらのオプションを使用するには、エレメントの名前を変更する前にもう一度_Shift + F6_を押すと、ポップアップが表示されます。
link:/uploads/renaming-100x61.png%20100w []
[コメントと文字列で検索]オプションは、名前の変更に使用できます。 「テキストの出現の検索」オプションについては、メソッドのパラメーターとローカル変数には使用できません。 最後に、hierarchy_オプションの_Renameパラメーターは、メソッドパラメーターでのみ使用できます。
したがって、*これら2つのオプションのいずれかと一致するものが見つかった場合、IntelliJはそれらを表示し、変更の一部をオプトアウトする可能性を提供します*(たとえば、名前の変更に関係のないものと一致する場合)。
メソッドにJavadocを追加して、最初のパラメーター_a_の名前を変更しましょう。
/**
* Adds a and b
* @param a the first number
* @param b the second number
*/
public int add(int a, int b) {...}
確認ポップアップで最初のオプションをチェックすることにより、IntelliJはメソッドのJavadocコメント内のパラメーターの言及に一致し、それらの名前も変更するよう提案します。
/**
* Adds firstNumber and b
* @param firstNumber the first number
* @param b the second number
*/
public int add(int firstNumber, int b) {...}
最後に、* IntelliJはスマートであり、ほとんどの場合、名前を変更した要素のスコープ内で使用法を検索することに注意する必要があります。名前を変更することは考慮されていなかったでしょう。
3. 抽出
では、https://www.jetbrains.com/help/idea/2018.3/extract-dialogs.html [extraction]について話しましょう。 *抽出により、コードを取得して変数、メソッド、またはクラスに入れることができます。* IntelliJは、同様のコードを検索し、同じ方法でそれらを抽出するため、これを非常にスマートに処理します。
そのため、このセクションでは、IntelliJが提供する抽出機能を活用する方法を学習します。
3.1. 変数
まず、変数の抽出から始めましょう。 *これはローカル変数、パラメーター、フィールド、および定数を意味します。*変数を抽出するには、次の手順に従う必要があります。
-
変数に適合する式を選択します
-
選択した領域を右クリックします
-
* _Refactor> Extract> Variable / Parameter / Field / Constant_をトリガーします
オプション* -
_この発生のみを置換_または_すべてを置換xから選択します
occurrences_オプション(提案されている場合) -
抽出した式の名前を入力します(選択した式がそうでない場合
私たちに合う) -
_Enter_を押します
名前の変更については、メニューを使用する代わりにキーボードショートカットを使用することができます。 *デフォルトのショートカットは、それぞれ、_Ctrl + Alt + V、Ctrl + Alt + P、Ctrl + Alt + F_および_Ctrl + Alt + C_です。*
IntelliJは、式が返すものに基づいて、抽出された式の名前を推測しようとします。 ニーズに合わない場合は、抽出を確認する前に自由に変更できます。
例で説明しましょう。 _SimpleClass_クラスにメソッドを追加して、現在の日付が指定された2つの日付の間にあるかどうかを通知することを想像できます。
public static boolean isNowBetween(LocalDate startingDate, LocalDate endingDate) {
return LocalDate.now().isAfter(startingDate) && LocalDate.now().isBefore(endingDate);
}
_LocalDate.now()_を2回使用し、両方の評価でまったく同じ値になるようにしたいので、実装を変更したいとしましょう。 式を選択して、ローカル変数_now:_に抽出してみましょう。
link:/uploads/extract_local_variable-100x16.png%20100w []
次に、_LocalDate.now()_呼び出しがローカル変数にキャプチャされます。
public static boolean isNowBetween(LocalDate startingDate, LocalDate endingDate) {
LocalDate now = LocalDate.now();
return now.isAfter(startingDate) && now.isBefore(endingDate);
}
_Replace all_オプションをチェックすることにより、両方の式が一度に置換されたことを確認しました。
3.2. 方法
IntelliJを使用してメソッドを抽出する方法を確認しましょう。
-
目的のメソッドに適合する式またはコード行を選択します
つくる -
選択した領域を右クリックします
-
トリガーリファクタリング>抽出> Method_オプション
-
メソッド情報を入力します:名前、可視性、
パラメーター -
_Enter_を押します
*メソッド本体を選択した後、_Ctrl + Alt + M_を押しても同様に機能します。*
前の例を再利用して、日付が他の日付の間にあるかどうかをチェックするメソッドが必要だとしましょう。 次に、_isNowBetween_メソッドの最後の行を選択し、メソッド抽出機能をトリガーするだけです。
開いたウィンドウで、IntelliJが_startingDate、endingDate_、_now_の3つの必要なパラメーターを既に見つけていることがわかります。 このメソッドをできるだけ汎用的にしたいので、_now_パラメーターの名前を_date_に変更します。 そして、結合のために、それを最初のパラメーターとして配置します。
最後に、メソッドに_isDateBetween_という名前を付け、抽出プロセスを完了します。
link:/uploads/extract_method-100x94.png%20100w []
その後、次のコードを取得します。
public static boolean isNowBetween(LocalDate startingDate, LocalDate endingDate) {
LocalDate now = LocalDate.now();
return isDateBetween(now, startingDate, endingDate);
}
private static boolean isDateBetween(LocalDate date, LocalDate startingDate, LocalDate endingDate) {
return date.isBefore(endingDate) && date.isAfter(startingDate);
}
ご覧のとおり、アクションはnew__isDateBetween_メソッドの作成をトリガーしました。これは、_isNowBetween_メソッドでも呼び出されます。 このメソッドはデフォルトでプライベートです。 もちろん、これは可視性オプションを使用して変更できます。
3.3. クラス
結局、日付管理に焦点を当てた特定のクラスで日付関連のメソッドを取得したい場合があります:_DateUtils_。 繰り返しますが、これは非常に簡単です。
-
移動したい要素があるクラスを右クリックします
-
* _Refactor> Extract> Delegate_オプションをトリガー*
-
クラス情報を入力します:名前、パッケージ、要素
デリゲート、それらの要素の可視性 -
_Enter_を押します
デフォルトでは、この機能に使用できるキーボードショートカットはありません。
機能をトリガーする前に、_main_メソッドで日付関連のメソッドを呼び出すとしましょう。
isNowBetween(LocalDate.MIN, LocalDate.MAX);
isDateBetween(LocalDate.of(2019, 1, 1), LocalDate.MIN, LocalDate.MAX);
次に、デリゲートオプションを使用して、これら2つのメソッドをa__DateUtils_クラスに委任します。
link:/uploads/delegate-100x83.png%20100w []
この機能をトリガーすると、次のコードが生成されます。
public class DateUtils {
public static boolean isNowBetween(LocalDate startingDate, LocalDate endingDate) {
LocalDate now = LocalDate.now();
return isDateBetween(now, startingDate, endingDate);
}
public static boolean isDateBetween(LocalDate date, LocalDate startingDate, LocalDate endingDate) {
return date.isBefore(endingDate) && date.isAfter(startingDate);
}
}
_isDateBetween_メソッドが_public_になっていることがわかります。 これは可視性オプションの結果であり、デフォルトでは_escalate_に設定されています。 * _Escalate_は、委任された要素への現在の呼び出しがまだコンパイルされていることを保証するために、可視性が変更されることを意味します。*
この例では、_isDateBetween_が_SimpleClass:_の___main ___methodで使用されています
DateUtils.isNowBetween(LocalDate.MIN, LocalDate.MAX);
DateUtils.isDateBetween(LocalDate.of(2019, 1, 1), LocalDate.MIN, LocalDate.MAX);
したがって、メソッドを移動するときは、プライベートではないようにする必要があります。
ただし、他のオプションを選択することにより、要素に特定の可視性を与えることができます。
4. インライン展開
抽出について説明したので、その対応物についてお話ししましょう:https://www.jetbrains.com/help/idea/2018.3/inline-dialogs.html[inlining]。 *インライン化とは、コード要素を取得して、それを構成要素で置き換えることです。*変数の場合、これは割り当てられた式になります。 メソッドの場合、それはその本体になります。
要素をインライン化するには、この要素(定義またはその参照)を右クリックして、* _ Refactor> _ _Inline_ *オプションをトリガーする必要があります。 要素を選択して* _Ctrl + Alt + N_ *キーを押すことでもこれを実現できます。
この時点で、IntelliJは、変数をインライン化するかメソッドをインライン化するか、定義を選択するか参照を選択するかに関係なく、複数のオプションを提供します。 これらのオプションは次のとおりです。
-
すべての参照をインライン化し、要素を削除します
-
すべての参照をインライン化しますが、要素は保持します
-
選択した参照のみをインライン化し、要素を保持します
_isNowBetween_メソッドを使用して、_now_変数を削除しましょう。
link:/uploads/inline_local_variable-100x33.png%20100w []
この変数をインライン化すると、次の結果が得られます。
public static boolean isNowBetween(LocalDate startingDate, LocalDate endingDate) {
return isDateBetween(LocalDate.now(), startingDate, endingDate);
}
私たちの場合、唯一のオプションは、すべての参照を削除し、要素を削除することでした。 しかし、_isDateBetween_呼び出しを取り除き、インライン化することも選択したいと考えてみましょう。 次に、IntelliJは、前に述べた3つの可能性を提供します。
link:/uploads/extract_method-1-100x94.png%20100w []
*最初の呼び出しを選択すると、すべての呼び出しがメソッド本体に置き換えられ、メソッドが削除されます。 2番目のものについては、すべての呼び出しをメソッド本体で置き換えますが、メソッドは保持します。 最後に、最後のメソッドは、現在の呼び出しをメソッドbody *に置き換えるだけです:
public class DateUtils {
public static boolean isNowBetween(LocalDate startingDate, LocalDate endingDate) {
LocalDate date = LocalDate.now();
return date.isBefore(endingDate) && date.isAfter(startingDate);
}
public static boolean isDateBetween(LocalDate date, LocalDate startingDate, LocalDate endingDate) {
return date.isBefore(endingDate) && date.isAfter(startingDate);
}
}
_SimpleClass_の_main_メソッドも変更されていません。
5. 動く
前に、新しいクラスを作成し、コード要素の一部をそのクラスに委任する方法を見ました。 ただし、https://www.jetbrains.com/help/idea/2018.3/move-dialogs.html [既存のクラスにメソッドを委任する] *が必要な場合があります。 それがこのセクションの目的です。
要素を移動するには、次の手順を実行する必要があります。
-
移動する要素を選択します
-
要素を右クリック
-
* _Refactor> Move_オプションをトリガー*
-
受信者クラスとメソッドの可視性を選択します
-
_Enter_を押します
*要素を選択した後に_F6_を押すことでもこれを達成できます。*
method_SimpleClass _、_ isDateOutstide()_に新しいメソッドを追加するとします。これは、日付が日付の間隔の外側にあるかどうかを示します。
public static boolean isDateOutside(LocalDate date, LocalDate startingDate, LocalDate endingDate) {
return !DateUtils.isDateBetween(date, startingDate, endingDate);
}
次に、その場所が_DateUtils_クラスにある必要があることに気付きます。 そこで、それを移動することにします。
link:/uploads/move_method-100x93.png%20100w []
このメソッドは_DateUtils_クラスに含まれています。 メソッド内の_DateUtils_への参照は、不要になったため消えていることがわかります。
public static boolean isDateOutside(LocalDate date, LocalDate startingDate, LocalDate endingDate) {
return !isDateBetween(date, startingDate, endingDate);
}
先ほどの例は、静的メソッドに関するものなのでうまく機能します。 ただし、インスタンスメソッドの場合、物事はそれほど単純ではありません。
https://medium.com/@pelensky/refactoring-in-intellij-move-method-d9f43e108e9a [インスタンスメソッドの移動]を実行する場合、* IntelliJは現在のクラスのフィールドで参照されているクラスを検索し、メソッドをそれらのクラスの1つに移動します*(変更する必要がある場合)。
フィールドで変更可能なクラスが参照されていない場合、IntelliJはメソッドを移動する前に_static_にすることを提案します。
6. メソッドシグネチャの変更
最後に、https://www.jetbrains.com/help/idea/2018.3/change-signature-dialog.html [メソッドシグネチャの変更]を可能にする機能について説明します。 *この機能の目的は、メソッドシグネチャのあらゆる側面を操作することです。*
通常どおり、機能をトリガーするにはいくつかの手順を実行する必要があります。
-
変更する方法を選択してください
-
メソッドを右クリック
-
* [リファクタリング]> [署名の変更]オプションをトリガー*
-
メソッドシグネチャに変更を加える
-
_Enter_を押します
キーボードショートカットを使用する場合は、* _ Ctrl + F6_も使用できます。*
この機能は、メソッド抽出機能に非常によく似たウィンドウを開きます。 したがって、*メソッドを抽出するときと同じ可能性があります*:名前、可視性を変更し、パラメータを追加/削除し、それらを微調整します。
_isDateBetween_の実装を変更して、日付の境界を包括的または排他的と見なすことを想像してみましょう。 そのためには、メソッドにa _boolean_パラメーターを追加します。
link:/uploads/change_signature-100x84.png%20100w []
メソッドのシグネチャを変更することで、このパラメーターを追加し、名前を付けてデフォルト値を与えることができます。
public static boolean isDateBetween(LocalDate date, LocalDate startingDate,
LocalDate endingDate, boolean inclusive) {
return date.isBefore(endingDate) && date.isAfter(startingDate);
}
その後、必要に応じてメソッド本体を調整する必要があります。
必要に応じて、現在のメソッドを変更する代わりに、パラメーターを使用して別のメソッドを作成するために、オーバーロードmethod_オプションを介して_Delegateをチェックできます。
7. 結論
この記事では、IntelliJが提供するリファクタリング機能のいくつかを詳しく掘り下げる機会がありました。 もちろん、IntelliJは非常に強力なツールであるため、すべての可能性を網羅したわけではありません。 このエディターの詳細については、https://www.jetbrains.com/help/idea/2018.3/meet-intellij-idea.html [そのドキュメント]をいつでも参照できます。
コード要素の名前を変更する方法や、いくつかの動作を変数、メソッド、またはクラスに抽出する方法など、いくつかのことを見ました。 また、一部の要素を単独で使用する必要がない場合にインライン化する方法、一部のコードを別の場所に移動する方法、または既存のメソッドシグネチャを完全に変更する方法についても学びました。