Eclipseでのリファクタリング
1. 概要
refactoring.com で、「リファクタリングは、既存のコード本体を再構築し、外部の動作を変更せずに内部構造を変更するための統制のとれた手法です」と読みました。
通常、変数やメソッドの名前を変更したり、デザインパターンを導入してコードをよりオブジェクト指向にしたりすることができます。 最新のIDEには、この種のリファクタリングの目的やその他の多くの目的を達成するのに役立つ多くの組み込み機能があります。
このチュートリアルでは、無料で人気のあるJavaIDEであるEclipseでのリファクタリングに焦点を当てます。
リファクタリングを開始する前に、リファクタリング中に何も壊れていないことを確認するために、一連の堅実なテストを行うことをお勧めします。
2. 名前の変更
2.1. 変数とメソッドの名前の変更
次の簡単な手順に従って、変数とメソッドの名前を変更できます。
-
- 要素を選択します
- 要素を右クリックします
- リファクタリング>名前の変更オプションをクリックします
- 新しい名前を入力します
- Enterを押します
ショートカットキーAlt+Shift +Rを使用してで2番目と3番目の手順を実行することもできます。
上記のアクションが実行されると、Eclipseはそのファイル内のその要素のすべての使用法を検出し、それらをすべて所定の位置に置き換えます。
高度な機能を使用して、リファクタリングがオンのときにアイテムにカーソルを合わせ、オプションをクリックすることで、他のクラスの参照を更新することもできます。
これによりポップアップが開き、変数またはメソッドの名前を変更したり、他のクラスの参照を更新したりすることができます。
2.2. パッケージの名前変更
パッケージ名を選択し、前の例と同じアクションを実行することで、パッケージの名前を変更できます。 参照の更新やサブパッケージの名前変更などのオプションを使用して、パッケージの名前を変更できるポップアップがすぐに表示されます。
F2 を押して、プロジェクトエクスプローラービューからパッケージの名前を変更することもできます。
2.3. クラスとインターフェイスの名前の変更
同じアクションを使用するか、プロジェクトエクスプローラーから F2 を押すだけで、クラスまたはインターフェイスの名前を変更できます。 これにより、参照を更新するためのオプションと、いくつかの高度なオプションを含むポップアップが開きます。
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
}
ここで、ドライバーの詳細を別のクラスに抽出するとします。 これを行うには、クラス内の任意の場所を右クリックし、[リファクタリング]>[クラスの抽出]オプションを選択します。
これによりポップアップが開き、クラスに名前を付けて、移動するフィールドを選択できます。また、他のいくつかのオプションもあります。
先に進む前にコードをプレビューすることもできます。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) {
}
}
クラス内の任意の場所を右クリックし、[リファクタリング]> [インターフェイスの抽出]オプションを選択するか、 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スーパークラスに抽出したい場合があります。 アイテムをスーパークラスに抽出するには、クラス内の任意の場所を右クリックして、[リファクタリング]> [スーパークラスの抽出]オプションを選択するか、Alt + Shift +Tを使用してメニューを直接表示します。
これにより、選択した変数とメソッドを使用して新しい Person クラスが作成され、Employeeクラスは次のようにリファクタリングされます。
public class Employee extends Person {
private int experienceInMonths;
public int getExperienceInMonths() {
return experienceInMonths;
}
}
3.4. 抽出方法
コードをクリーンで保守しやすい状態に保つために、メソッド内の特定のコードを別のメソッドに抽出したい場合があります。
たとえば、メソッドにforループが埋め込まれているとします。
public class Test {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
メソッドの抽出ウィザードを呼び出すには、次の手順を実行する必要があります。
- 抽出するコード行を選択します
- 選択した領域を右クリックします
- リファクタリング>メソッドの抽出オプションをクリックします
最後の2つの手順は、キーボードショートカットのAlt + Shift +Mでも実行できます。 メソッドの抽出ダイアログを見てみましょう。
これにより、コードが次のようにリファクタリングされます。
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);
}
}
それをローカル変数に抽出したいと思います。
これを行うには、次のことを行う必要があります。
- アイテムを選択してください
- 右クリックして、リファクタリング>ローカル変数の抽出を選択します
最後の手順は、キーボードショートカットの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. 定数の抽出
または、式とリテラル値を staticfinalクラス属性に抽出できます。
今見たように、3.14値をローカル変数に抽出できます。
public class MathUtil {
public double circumference(double radius) {
return 2 * 3.14 * radius;
}
}
ただし、定数として抽出する方がよい場合があります。そのためには、次のことを行う必要があります。
- アイテムを選択してください
- 右クリックして、リファクタリング>定数の抽出を選択します
これにより、定数に名前を付けてその可視性を設定できるダイアログが開き、他のいくつかのオプションがあります。
これで、コードがもう少し読みやすくなりました。
public class MathUtil {
private static final double PI = 3.14;
public double circumference(double radius) {
return 2 * PI * radius;
}
}
4. インライン化
逆の方法でインラインコードを作成することもできます。
一度だけ使用されるローカル変数を持つUtilクラスについて考えてみます。
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
}
result ローカル変数を削除し、isPrimeメソッド呼び出しをインライン化します。 これを行うには、次の手順に従います。
- インライン化するアイテムを選択します
- 右クリックしてリファクタリング>インラインオプションを選択します
最後のステップは、キーボードショートカット Alt + Shift +Iでも実行できます。
その後、追跡する変数が1つ少なくなります。
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. プッシュダウンとプルアップ
クラス間に親子関係(前のEmployeeとPersonの例のような)があり、それらの間で特定のメソッドまたは変数を移動したい場合は、プッシュを使用できますEclipseが提供する/pullオプション。
名前が示すように、 Push Down オプションはメソッドとフィールドを親クラスからすべての子クラスに移動し、 PullUpはメソッドとフィールドを特定の子クラスから親に移動します。そのメソッドをすべての子クラスで使用できるようにします。
メソッドを子クラスに移動するには、クラス内の任意の場所を右クリックして、[リファクタリング]>[プッシュダウン]オプションを選択する必要があります。
これにより、プッシュダウンするアイテムを選択できるウィザードが開きます。
同様に、メソッドを子クラスから親クラスに移動するには、クラス内の任意の場所を右クリックして、[リファクタリング]>[プルアップ]を選択する必要があります。
これにより、プルアップするアイテムを選択できる同様のウィザードが開きます。
6. メソッドシグネチャの変更
既存のメソッドのメソッドシグネチャを変更するには、いくつかの簡単な手順に従います。
- メソッドを選択するか、カーソルを内部のどこかに置きます
- 右クリックして、リファクタリング>メソッドシグネチャの変更を選択します
最後のステップは、キーボードショートカット Alt + Shift+C。でも実行できます。
これによりポップアップが開き、それに応じてメソッドシグネチャを変更できます。
7. 引っ越し
コードをよりオブジェクト指向にするために、メソッドを別の既存のクラスに移動したい場合があります。
Movieクラスがあるシナリオを考えてみましょう。
public class Movie {
private String title;
private double price;
private MovieType type;
// other methods
}
そして、MovieTypeは単純な列挙型です。
public enum MovieType {
NEW, REGULAR
}
また、CustomerがNEWの映画をレンタルした場合、さらに2ドルが請求され、Customerクラスには totalCost ()を計算するための次のロジック:
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
}
明らかに、 MovieType に基づく映画のコストの計算は、 Customer クラスではなく、Movieクラスに適切に配置されます。 この計算ロジックはEclipseで簡単に移動できます。
- 移動する行を選択します
- 右クリックして、リファクタリング>移動オプションを選択します
最後のステップは、キーボードショートカット Alt + Shift +Vでも実行できます。
Eclipseは、このロジックがMovieクラスに含まれている必要があることを理解するのに十分なほど賢いです。 必要に応じて、他の高度なオプションとともにメソッド名を変更できます。
最終的なCustomerクラスコードは次のようにリファクタリングされます。
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
}
ご覧のとおり、movieCostメソッドはMovieクラスに移動され、リファクタリングされたCustomerクラスで使用されています。
8. 結論
このチュートリアルでは、Eclipseが提供する主なリファクタリング手法のいくつかを調べました。 名前の変更や抽出など、いくつかの基本的なリファクタリングから始めました。 後で、さまざまなクラスの周りでメソッドとフィールドを移動するのを見ました。
詳細については、リファクタリングに関するEclipseの公式ドキュメントをいつでも参照できます。