リファクタリング (プログラミング)
テンプレート:ページ番号 リファクタリング (refactoring) とはコンピュータプログラミングにおいて、プログラムの外部から見た動作を変えずにソースコードの内部構造を整理すること。いくつかのリファクタリング手法の総称としても使われる。十分に確立された技術とはいえず、「リファクタリング」の語にも厳密な定義があるわけではない。
目次
リファクタリング登場の経緯と目的
リファクタリングが登場する以前は、一度正常な動作をしたプログラムは二度と手を触れるべきではないと言われていた。下手に手を加えて動作が変わってしまうと、それに伴って関連する部分にも修正が加えられ、やがて修正はプロジェクト全体に波及し対処しきれなくなるかも知れない。またソフトウェアテストを十分に行い正常な動作が確認されたとしても、そのプログラムをわずかでも改変すれば、バグ (欠陥) が見つかったときに改変があったプログラムを疑わなければならない。
しかし、プログラムには必ず変更があり、プログラムはどうしてもつぎはぎだらけになることは避けられない。仕様が開発開始時から確定していることは少なく、開発をしている間にもソフトウェアに対する要求は日々変わり続けており、ソフトウェアには常に仕様変更に対応できる柔軟さが求められる。また、いくら厳密に設計しても実際に動作させないと分からない部分も多く、完璧な設計を行うことは不可能である。変更が必要になったときに、二度と手を触れられないほど煩雑になったソースコードを修正することは困難で、プログラマにも非常に勇気が要求される作業になる。
そこで、Smalltalkプログラマなどの間で、常日頃からプログラムを整理し仕様変更にも対応できる整理されたプログラムを書いていく考え方が生まれた。この過程では、ウォード・カニンガム、ケント・ベック、ラルフ・ジョンソン(GoFの一人)などの人々が大きな役割を果たした。この手法がリファクタリングと呼ばれている。またリファクタリングはプログラムの全容を捉えるためにも効果的である。バグが見つかったときも、ソースコードが整理されているので修正しやすい。プログラマとしても、普段から修正しているコードにまた手を入れるだけなので、修正にも積極的になれる。また設計者も設計ミスによる心残りをなくすことができる。設計の代用にもなる、とする意見もあり、事前設計を非常に簡素化する役目も持つ。
リファクタリングはオブジェクト指向設計と深くかかわっている。リファクタリングのほとんどはオブジェクト指向の性質に沿ったものであり、オブジェクト指向のコードの再利用性を最大限に引き出すことができる。オブジェクト指向プログラミングを行える言語であれば、プログラミング言語の種類にかかわらずリファクタリングを適用できる。
リファクタリングを行うことで、開発が停滞してしまうのではないか、という心配をされることも多い。リファクタリングを行っている間は、何の機能追加も行われない。しかしたいていの場合は、設計が向上することで機能追加やバグフィックスをしやすくなり、開発のスピードは安定するばかりか、速くなることもある。また、すでに機能しているコードを危険に晒すべきでない、とする意見もあるが、手順を守りテストを十分に行えばある程度危険を減らすことができる。
主なリファクタリング
- メソッドを抽出する
- 長すぎるメソッドは再利用性が低い。メソッドを抽出、細分化することで再利用性が高まり、呼び出し側メソッドの記述も読みやすくなる。処理の重複も減る。
- 双方向関連を単方向へ変更する
- 不要な参照は管理のための手間を増やし、オブジェクトの破棄を失敗させる。不要になった関連は消す。
- クラスの抽出
- 大きくなりすぎたクラスを分割する。クラスを小さくすることで、そのクラスの役目を明確にできる。
- switch文をポリモーフィズムに置き換える
- switch文をポリモーフィズムに置き換えることで、新たな条件が追加されても分岐部分には変更の必要がなくなる。
- メンバの移動
- フィールドやメソッドが不適切なクラスにある場合、他のクラスとの余計な関連が増える。メンバを移動し、クラスの責任を整理する。
- 継承を委譲に置き換える
- 継承では基底クラスのすべてのメンバを、サブクラスに許さなければならない。基底クラスの一部だけの機能を利用する場合は、継承の代わりに委譲を使う。
- ダウンキャストをカプセル化する
- ダウンキャストは互換性のない型に変換してしまう可能性があるが、それをコンパイル時に察知することは出来ない。総称型(テンプレート)がない言語では、カプセル化してクライアント側にダウンキャストの手間を減らすようにする。コレクションクラスなどでは特に必要。
- コンストラクタをFactory Methodに置き換える
- コンストラクタはそのクラスのオブジェクトを返すことしか出来ない。Factory Methodの導入によって柔軟なインスタンス化が可能になる。
- 引数オブジェクトの導入
- たびたび一緒に受け渡しされる複数の値は、オブジェクトとしてまとめたほうが分かりやすい。
- クラス・メソッド・属性の名称を変更する
- クラス名・メソッド名・属性名は、そのクラスやメソッドの役割を正確に説明したものにする必要がある。名称が不正確なものであったり、曖昧なものである時は名称の変更が行なわれる。しばしば、当初は適切であった名称も、他のリファクタリング実行後に適切で無くなることがある。このときは、続けて名称変更のリファクタリングも行なわれる(⇒命名規則 (プログラミング))。
マーティン・ファウラーなどの人々が著したリファクタリングの解説書、『リファクタリング プログラミングの体質改善テクニック』(以下『リファクタリング』)では70種類ほどのリファクタリングが挙げられている。
リファクタリングを行うタイミング
いつでもなんでもリファクタリングをすればよいというものではない。納期がぎりぎりに迫った場合などにリファクタリングを行っている余裕はないし、リファクタリングは将来に備えて行うものであるため、そのリファクタリングが実を結ぶ可能性は少ない。また、リファクタリングといえどもやはりプログラミングであるので、常にミスをする危険性は拭えない。
『リファクタリング』では、機能追加するときと、リファクタリングするときをはっきり区別することを勧めている(この事を比喩して「実装の帽子をリファクタリングの帽子に被りなおす」という)。リファクタリングしてばかりいては開発は進まないし、どのリファクタリングをするべきかはある程度開発が進まないと分からない。リファクタリングを開始するタイミングとして、コードに「不吉なにおい」を感じ始めたら、と提案している。これは似たようなコードの重複や、長すぎるメソッド、ひとつの変更のたびに複数のクラスが影響を受ける、などの症状が見つかったときを指している。また、機能追加の前、コードレビュー時、バグフィックス時にもリファクタリングを勧めている。
テストの重要性
プログラムの外観を変更してはならないため、リファクタリングにおいてはテストが非常に重要になる。修正は段階的・小刻みに行い、わずかな変更のたびにテストを行うことで、動作の異常をいち早く察知する。テストを行わずに一度にリファクタリングを行うと、プログラムの動作がいつの間にか変わってしまっても、原因がある箇所を突き止めることが難しくなる。プログラマにテストをサボらせないため、簡単にテストを実行できるツール (xUnit/JUnitなど) も必要である。またテストを重要視することは、アジャイルソフトウェア開発のいくつかの開発手法(エクストリーム・プログラミングなど)における「テストファースト」や「テスト駆動開発」の考え方とも一致する。
リファクタリングの課題
リファクタリングにもいくつか課題が存在する。たとえば、データベースにおいて変更をする場合、データの移行をする必要がある。中間層を挟むことで影響を緩和できるが、やはり時間が掛かることは否定できない。また、リファクタリングでは従来のようにカプセル化されたクラス内だけでなく、インタフェースも変更することがある。それが広く公開されたインタフェースである場合、新しいインタフェースと古いインタフェースを両方保守しなければならない。また、修正するコードがあまりに酷い状態の場合、新たに書き直したほうが早い事もある。まだ発展途上の技術のため、これからいくつもの課題が見つかる可能性はある。
統合開発環境のリファクタリング機能
最近の統合開発環境にはリファクタリング機能が備わっていることが多い。リファクタリングでは、修正対象のメソッドやクラスがどのクラスから利用されているかを調べる必要が発生する。これを単なるテキストエディタで調べようとするとかなり面倒な作業になる上、見落としをする可能性も高い。
関連項目
- アジャイルソフトウェア開発 - エクストリーム・プログラミングなどのいくつかの開発手法においては、開発における習慣のひとつとして、リファクタリングを挙げている。
- xUnit/JUnit - テストのためのフレームワーク。JUnitはJavaプログラムのテストフレームワークである。
- Eclipse - 統合開発環境。数多くのリファクタリング機能が備わっている。
- ソフトウェア保守
- コードの臭い
参考文献
- マーチン・ファウラー ほか 『リファクタリング : プログラミングの体質改善テクニック』 ピアソン・エデュケーション、2000年 ISBN 4894712288