第6回

Eclipseによるリファクタリング

縣俊貴
橋本正徳
Project Mobster/メディアファイブ株式会社

2003/5/16

 今回のテーマは、リファクタリングです。まずはリファクタリングの意義とその効果について解説します。後半はEclipseのリファクタリング機能使った例をご紹介します。

すべてはシンプルデザインのために

 システムは生命体のようなものです。コーディングを開始した瞬間に誕生し、バージョンを重ねるごとに成長していきます。バージョン1.0ですべてが終わるのならば、物事はうまくいきます。しかし、実際には機能拡張という名の試練が待っています。将来の機能拡張に対する戦略としては、次のようなカードが考えられます。

    A)将来の機能拡張を予想して注意深く設計を行う

    B)将来の機能拡張のために、コードを分かりやすくシンプルな状態に保つ

 XPでは主に、Bのカードを選択します。そのためのテクニックとしてリファクタリングを使います。

リファクタリングとは「コードの体質改善」である

 リファクタリングとは外部から見た振る舞いを保ったまま、内部の処理コードを改善していく手法です。複雑なコードをシンプルで分かりやすいコードへとリファクタリングしていくことで、将来の機能拡張や保守を行いやすい状態に保つことができます。リファクタリングを絶えず行っていくことで、結果的にシステムの寿命を延ばすことができます。

public int methodA(int a, int b){
        // 複雑なコード
        // 分かりにくいコード
        ○×△○×△○×△○×△○×△○×△
        ○×△○×△○×△○×△○×△○×△
        ○×△○×△○×△○×△○×△○×△
        ○×△○×△○×△○×△○×△○×△
        ○×△○×△○×△○×△○×△○×△
        ……
        return ……
}
リファクタリングすると……
public int methodA(int a, int b){
        // 外部から見た振る舞いは保ったまま
        // 内部をシンプルで分かりやすいコードへ変更
        ……
        methodB();
        ……
        return ……
}
private int methodB() {
        ……
}

リファクタリングの効果・・・・・・

 リファクタリングには次のような効果が期待できます。

 設計の向上:設計上の誤りを後で修正することができます。私たちはすべてを満たす設計を前もって考えがちですが、その時間をかけた設計が間違っている場合が往々にしてあります。リファクタリングをうまく実践することで、設計を修正しながらプロジェクトを進めていくことができます。もちろんXPの場合、「コードの共同所有」によって、だれでもその修正が行えます

 品質の向上:リファクタリングを行うことで、複雑なコードや重複コードがなくなり、バグやデグレードの発生を低下させることができます

 開発効率の向上:リファクタリング行為が設計を分かりやすくしてくれるため、コード全体の理解を早めてくれます。結果として、開発効率はリファクタリングを行わないシステムよりも上がることになります

リファクタリングとテストの蜜月な関係

 リファクタリングはソフトウェアの内部構造をよりよい状態に変更しますが、外部から見た振る舞いは変化しません。リファクタリングの前後にユニットテストを実行することで、『リファクタリングによる修正が正しかったこと』を確認することができます。逆にいうと、テストなしのリファクタリングは、変更が正しかったことを保証することができないのでお勧めできません。テストがあることで「リファクタリングを常に行うことができる」状態になります。

リファクタリングのタイミング

 リファクタリングは「リファクタリングの時間」などと時間を設けて行う作業ではありません。自然とその欲求がプログラマに芽生えるので、そのときにリファクタリングを行えばいいのです。プログラマは常にコードを安全な方に、理解しやすいようにと心掛けておくことが大切です。またリファクタリングと「機能追加」を同時に行わないようにしましょう。リファクタリングも機能追加もコードを修正しますが、リファクタリングでは機能追加を行いません。

リファクタリングをもっと知りたい方へ

 リファクタリングをもっと知りたい方には、次の書籍をお薦めします。リファクタリングの種類ごとにカタログ化されていて、Javaによるサンプルソースも豊富です。

コードが生まれた瞬間
 テストが「緑のバー」をのばした瞬間、それがコードが生まれた瞬間です。まだ赤ちゃん状態のコードなので、これからグレるのも、優等生になるのも育て方次第です。放っておけばグレることは目に見えています。筆者も過去に書いたコードがグレた経験があります。そのコードはなかなかの親不孝者で夜も寝ることができませんでした。どこかを修正すれば、ほかのところで人に迷惑を掛けるという暴れっぷりには親である私もお手上げ状態になりました。原因は「放っておいたから」以外には考えられませんでした。コードが生まれた瞬間、その瞬間から保守は始まっているのです。

Eclipseのリファクタリング機能

 Eclipseには安全にリファクタリングを行ってくれる機能があらかじめ備わっています。Eclipseで使用できるリファクタリング機能は次のようなものがあります。また、Eclipse 2.1ではさらにリファクタリングの追加が行われており、今後もリファクタリング機能の充実が予想されます。

  • Javaエレメントのコピーおよび移動
  • メソッドの抽出
  • パッケージの名前変更
  • コンパイル単位の名前変更
  • クラスまたはインターフェイスの名前変更
  • メソッドの名前変更
  • フィールドの名前変更
  • ローカル変数の名前変更
  • メソッド・パラメータの名前変更
  • メソッド・パラメータの再配列
  • ローカル変数の抽出
  • ローカル変数のインライン化
  • フィールドの自己カプセル化
  • ローカル変数を照会で置換
  • メンバーをスーパークラスに引き上げる
  • タイプ間での静的メンバーの移動

Eclipseによるリファクタリングの例

 では実際にEclipseのリファクタリング機能を使って、ソースコードをリファクタリングしてみましょう。ここで適用するリファクタリングは「メソッドの抽出」です。

(1)リファクタリング前のソースコード

 例としてビデオクラスにおけるビデオレンタル延滞料金計算メソッドcalcArrearsをリファクタリングしてみましょう。calcArrearsメソッドでは1日当たりの延滞料baseを求めるためにif文がありますが、この部分を別メソッドとして抽出します。

// リファクタリング前
/**
 * ビデオクラス。
 */
public class Video {
         /** 新作 */
         private static final int NEW = 0;
         /** 旧作 */
         private static final int OLD = 0;
         /**
          * 延滞料金の計算
          * @param type 種別
          * @param day 延滞日数
          */
         public int calcArrears(int type, int day) {
                  int base = 0; // 1日当たりの延滞料
                  if (type == NEW) {
                          base = 300;
                  } else if(type == OLD){
                          base = 100;
                  } else {
                          throw new IllegalStateException();
                  }
                  // 延滞料金の計算(延滞料金+消費税)
                  int arrears = (int) Math.round((base * day) + (base * day) * 0.05);
                  return arrears;
          }
}

(2)メソッドとして抽出したい範囲を指定

図1 メソッドの抽出

(3)[右クリック] - [リファクタリング] - [メソッドの抽出]を選択

 「リファクタリング」ダイアログが表示されます。

図2 メソッド名の入力

(4)メソッド名を入力して、[次へ]

 リファクタリング実行時のプレビューが表示されます。変更前と変更後のソースを見比べることができますので、問題がなければ[終了]をクリックします。

図3 プレビュー(クリックすると拡大)

 以下がリファクタリング後のソースです。抽出したメソッドに必要な引数の値typeがcalcArrearsの引数として渡されています。このようにリファクタリング時に必要な変更をすべて、Eclipseがやってくれます。実行したリファクタリングが気に入らなければ、[編集]-[元に戻す]でリファクタリング前の状態に復元することもできます。

// リファクタリング後
/**
 * ビデオクラス。
 */
public class Video {
         /** 新作 */
         private static final int NEW = 0;
         /** 旧作 */
         private static final int OLD = 0;
         /**
          * 延滞料金の計算
          * @param type 種別
          * @param day 延滞日数
          */
         public int calcArrears(int type, int day) {
                  int base = getBaseArrears(type);
                  // 延滞料金の計算(延滞料金+消費税)
                  int arrears = (int) Math.round((base * day) +(base * day) * 0.05);
                  return arrears;
         }
         /**
          * 延滞料金の基本料金を求めます。
          * @param type 種別
          */
         private int calcArrears(int type) {
                  int base = 0; // 1日当たりの延滞料
                  if (type == NEW) {
                            base = 300;
                  } else if(type == OLD){
                            base = 100;
                  } else {
                            throw new IllegalStateException();
                  }
                  return base;
         }
}

 そのほかにもEclipseには強力なリファクタリング機能が備わっています。詳しいEclipseのリファクタリング機能についてはヘルプの[Java開発者ガイド]-[タスク]-[リファクタリング]をご覧ください。

ユニットテストがないシステムへの機能拡張
 ユニットテストがないシステムに機能拡張をする場合も、テストとリファクタリングの組み合わせは有効です。特に、変更個所の設計・コードが複雑な場合、機能の追加の前にリファクタリングを施すことで、機能拡張をスムーズに行うことができます。
  1. 機能拡張を行う部分を中心にユニットテストを作成
  2. テストを実行
  3. 機能拡張前の設計・コードをリファクタリング
  4. テストを実行
  5. 機能の追加。必要に応じてテストも追加
  6. テストを実行
  7. 機能拡張後の設計・コードをリファクタリング
  8. テストを実行
 すべての変更の前後にテストを実行しているのが特徴です。結果として、機能拡張したシステムには次の成果が残ることになります。
  • 機能拡張
  • ユニットテスト一式
  • シンプルにリファクタリングされたコード
 ユニットテストとリファクタリングは、実践するのに遅すぎるという言葉は当てはまりません。すでに複雑で悲惨な状況だからこそ、実践してほしいプラクティスだと思います。

 いかがでしたか? リファクタリングは機能拡張を伴わないため、進ちょく率を重視する管理者の方から軽視される傾向にあります。しかし「品質」「将来の機能拡張」という顧客のニーズを満たすためには必須のプラクティスであるといえるでしょう。まさに「明日のために打つべし」ですね。

 次回は「継続的な結合」です。CVS、JUnit、Antを使った「定期的な自動ビルド&テスト環境の構築」についてご紹介する予定です。お楽しみに。


プロフィール
縣俊貴(あがた としたか)
 メディアファイブ株式会社所属。XML,フレームワークを中心に開発業務に携わる。Javaのコミュニティー団体であるMobsterを主催。現在MonsterにてJavaベースのWikiシステム「MobWiki」を開発している。

橋本正徳(はしもと まさのり)
 メディアファイブ株式会社所属。XML、フレームワーク等の開発業務に携わる。Javaのコミュニティー団体である「Mobster」を縣と共に発起、運営。現在mobsterにてバグトラッキングシステム「mobbug」等を開発している。「日本XPユーザーグループ関西支部 九州分科会」にも参加。ちなみにこの記事自身もCVSでバージョン管理し、縣と橋本とで共同所有されて書かれている。

Project Mobster(ぷろじぇくと もぶすたー)
福岡県福岡市を中心にJava言語を研究追求し、その成果物をWeb上に公開していく団体です。年齢・スキル・会社などを超えてボーダーレスに活動しております。

IT Architect 連載記事一覧

この記事に対するご意見をお寄せください managemail@atmarkit.co.jp

「ITmedia マーケティング」新着記事

生成AIの米中依存、地政学リスクに――BCGが警告
ボストンコンサルティンググループ(BCG)の戦略シンクタンクであるBCGヘンダーソン研究...

Webサイトリニューアル時のSEOチェックポイント 順位を落とさないために必須の12の対応を解説
何らかの目的があって進めるリニューアルではあるものの、検索順位がその代償になってし...

ハッシュタグはオワコン? イーロン・マスク氏も「使うな」と投稿、その意図は……
ハッシュ記号(#)とキーワードを連結させることで投稿のトピックを明示する「ハッシュタ...