リファクタリング (プログラミング)

リファクタリング (refactoring) とは、コンピュータプログラミングにおいて、プログラムの外部から見た動作を変えずにソースコードの内部構造を整理することである。また、いくつかのリファクタリング手法の総称としても使われる。ただし、十分に確立された技術とはいえず、また「リファクタリング」という言葉に厳密な定義があるわけではない。 リファクタリングが登場する以前は、一度正常な動作をしたプログラムは二度と手を触れるべきではないと言われていた。なぜなら、下手に手を加えて動作が変わってしまうと、それに伴って関連する部分にも修正が加えられ、やがてその修正作業はプロジェクト全体に波及し対処しきれなくなる可能性があったからである。また、ソフトウェアテストを十分に行い、正常な動作が確認されたとしても、そのプログラムを少しでも改変してしまえば、その後バグ (欠陥) が見つかったときに、改変があったプログラムを疑わなければならない。 しかし、プログラムには必ず変更があり、プログラムはどうしても継ぎ接ぎだらけになることは避けられない。また、仕様が開発開始時から確定していることは少なく、開発をしている間にもソフトウェアに対する要求は日々変わり続けており、ソフトウェアには常に仕様変更に対応できる柔軟さが求められる。さらに、いくら厳密に設計しても実際に動作させないと分からない部分も多く、完璧な設計を行うことは不可能である。変更が必要になったとき、二度と手を触れられないほど煩雑になったソースコードを修正することは困難を極め、プログラマにも勇気が要求される作業になる。 そこで、Smalltalkプログラマなどの間で、常日頃からプログラムを整理し、仕様変更にも対応できる整理されたプログラムを書いていく考え方が生まれた。この過程では、ウォード・カニンガム、ケント・ベック、ラルフ・ジョンソン（GoFの一人）などの人々が大きな役割を果たした。この手法がリファクタリングと呼ばれている。また、リファクタリングは、プログラムの全容を捉えるためにも効果的である。例えば、バグが検出された場合でも、ソースコードが整理されているので、修正しやすい。また、プログラマとしても、普段から修正しているコードに手を入れるだけなので、修正にも積極的になれる。さらに、設計者も設計ミスによる心残りをなくすことができる。そのため、「リファクタリングは設計の代用にもなる」とする意見もあり、事前設計を非常に簡素化する役割も担っている。 リファクタリングは、オブジェクト指向設計と深く関係している。ほとんどのリファクタリングは、オブジェクト指向の性質に沿ったものであり、オブジェクト指向のコードの再利用性を最大限に引き出すことができる。また、オブジェクト指向プログラミングを行える言語であれば、プログラミング言語の種類に関わらず、リファクタリングを適用できる。 リファクタリングを行うことで、開発が停滞してしまうのではないか、という心配をされることも多い。たしかに、リファクタリングを行っている間は、何の機能追加も行われない。しかし、たいていの場合は、設計が向上することで機能追加やバグフィックスをしやすくなり、開発のスピードは安定するばかりか、速くなることもある。また、すでに機能しているコードを危険に晒すべきでない、とする意見もあるが、手順を守りテストを十分に行えば、ある程度危険を減らすことができる。 メソッドを抽出する 長すぎるメソッドは再利用性が低い。メソッドを抽出、細分化することで再利用性が高まり、呼び出し側メソッドの記述も読みやすくなる。処理の重複も減る。 双方向関連を単方向へ変更する 不要な参照は管理のための手間を増やし、オブジェクトの破棄を失敗させる。不要になった関連は消す。 クラスの抽出 大きくなりすぎたクラスを分割する。クラスを小さくすることで、そのクラスの役目を明確にできる。 switch文をポリモーフィズムに置き換える switch文をポリモーフィズムに置き換えることで、新たな条件が追加されても分岐部分には変更の必要がなくなる。 メンバの移動 フィールドやメソッドが不適切なクラスにある場合、他のクラスとの余計な関連が増える。メンバを移動し、クラスの責任を整理する。 継承を委譲に置き換える 継承では基底クラスのすべてのメンバを、サブクラスに許さなければならない。基底クラスの一部だけの機能を利用する場合は、継承の代わりに委譲を使う。 ダウンキャストをカプセル化する ダウンキャストは互換性のない型に変換してしまう可能性があるが、それをコンパイル時に察知することは出来ない。総称型（テンプレート）がない言語では、カプセル化してクライアント側にダウンキャストの手間を減らすようにする。コレクションクラスなどでは特に必要。 コンストラクタをFactory Methodに置き換える コンストラクタはそのクラスのオブジェクトを返すことしか出来ない。Factory Methodの導入によって柔軟なインスタンス化が可能になる。 引数オブジェクトの導入 たびたび一緒に受け渡しされる複数の値は、オブジェクトとしてまとめたほうが分かりやすい。 シンボルが指す対象をより適切に象徴するシンボル名に更新すること。 シンボル名（クラス名・関数名・属性名など）は対象がもつ役割を正確に象徴・説明すべきである（c.f. 命名規則 (プログラミング)）。当初は適切であった名称も、プログラムの変更によって不正確・曖昧になりうる。この場合、シンボル名の変更（リネーム）がおこなわれる。いくつかのエディタではファイルを跨いだシンボル名の一括変更をサポートとしており（VSCode: F2で一括リネーム）、リネームに必要なリファクタリングコストは非常に小さくなっている。 マーティン・ファウラーなどの人々が著したリファクタリングの解説書、『リファクタリング　プログラミングの体質改善テクニック』（以下『リファクタリング』）では、70種類ほどのリファクタリングが挙げられている。 いつでもなんでもリファクタリングをすればよいというものではない。例えば、納期がぎりぎりに迫った場合などにリファクタリングを行っている余裕はないし、リファクタリングは将来に備えて行うものであるため、そのリファクタリングが実を結ぶ可能性は少ない。また、リファクタリングといえども、やはりプログラミングであるので、常にミスをする危険性は拭えない。 『リファクタリング』では、機能追加するときと、リファクタリングするときをはっきり区別することを勧めている（このことを比喩して「実装の帽子をリファクタリングの帽子に被りなおす」という）。リファクタリングしてばかりいては開発は進まないし、どのリファクタリングをするべきかはある程度開発が進まないと分からない。リファクタリングを開始するタイミングとして、コードに「不吉なにおい」を感じ始めたら、と提案している。これは似たようなコードの重複や、長すぎるメソッド、ひとつの変更の度に複数のクラスが影響を受ける、などの症状が見つかったときを指している。また、機能追加の前、コードレビュー時、バグフィックス時にもリファクタリングを勧めている。 リファクタリングでは、プログラムの外観を変更してはならない。そのため、テストが非常に重要である。修正は段階的かつ小刻みに行い、わずかな変更であっても、その度にテストを行うことで、動作の異常をいち早く察知する。テストを行わずに一度にリファクタリングを行うと、プログラムの動作が気付かないうちに変わってしまい、その原因を突き止めることが難しくなる。プログラマにテストをサボらせないため、簡単にテストを実行できるツール (xUnit/JUnitなど) も必要である。また、テストを重要視することは、アジャイルソフトウェア開発のいくつかの開発手法（エクストリーム・プログラミングなど）における「テストファースト」や「テスト駆動開発」の考え方とも一致する。 リファクタリングには、いくつか課題が存在する。例えば、データベースに変更を加える場合、データを移行する必要がある。たしかに、中間層を挟むことで影響を緩和できるが、やはり時間が掛かることは否定できない。また、リファクタリングでは、従来のようにカプセル化されたクラス内だけでなく、インタフェースも変更することがある。それが広く公開されたインタフェースである場合、新しいインタフェースと古いインタフェースを両方保守しなければならない。また、修正するコードがあまりに酷い場合、新たに書き直したほうが早いこともある。リファクタリングは発展途上の技術であるため、これら以外の課題が見つかる可能性がある。 「リファクタリング」という用語が出版文献上初めて用いられたのはウィリアム・オプダイク（英語: William Opdyke）とラルフ・ジョンソンによる1990年9月の論文だった。1992年に著されたグリスウッド（英: Griswold）の博士論文、オプダイクの博士論文も、同じくこの用語を使った。計算機コードのリファクタリングは10年にわたり非公式に行われてきたけれども、オブジェクト指向プログラムのリファクタリングに関するウィリアム・オプダイクの1992年の論文が後続する、ウィリアム・グリスウッド（英語: William Griswold）の1991年の 博士論文は、機能的かつ手続的プログラムのリファクタリングに関する最初の主な学術的な仕事であり、そしてすべてこれらの理論と機械はずっとプログラム変換処理 （英語: program transformation ）システムとして可能だったけれども。すべてこれらの文献はリファクタリングの主だった手法の目録を与える；リファクタリングの方法はどのように方法を適用するか、そしていつその方法を適用すべきか（あるいはすべきでないか）について指し示す、記述を持っている。 最近の統合開発環境には、リファクタリング機能が備わっていることが多い。リファクタリングでは、修正対象のメソッドやクラスがどのクラスから利用されているかを調べる必要が発生する。これを単なるテキストエディタで調べようとすると、かなり面倒な作業になる上、見落としをする可能性も高い。 [脚注の使い方] ^ Renaming is a common operation related to refactoring source code and VS Code has a separate Rename Symbol command (F2). Visual Studio Code - USER GUIDE - Refactoring - Rename Symbol ^ Opdyke & Johnson 1990 ^ Griswold 1991 ^ a b Opdyke 1992 ^ a b Fowler 2003 Fowler, Martin (2003-09-10), EtymologyOfRefactoring  Opdyke, William F.; Johnson, Ralph E. (September 1990), Refactoring: An Aid in Designing Application Frameworks and Evolving Object-Oriented Systems, ACM  Griswold, William G (July 1991), Program Restructuring as an Aid to Software Maintenance, University of Washington  Opdyke, William F (June 1992) (compressed Postscript), Refactoring Object-Oriented Frameworks, University of Illinois at Urbana-Champaign  マーチン・ファウラー ほか 『リファクタリング : プログラミングの体質改善テクニック』 ピアソン・エデュケーション、2000年 ISBN 4894712288 Eclipse - 統合開発環境。数多くのリファクタリング機能が備わっている。 xUnit/JUnit - テストのためのフレームワーク。JUnitはJavaプログラムのテストフレームワークである。 アジャイルソフトウェア開発 - エクストリーム・プログラミングなどのいくつかの開発手法においては、開発における習慣のひとつとして、リファクタリングを挙げている。 改善パターン（英語: amelioration pattern） 関心の分離 コードの臭い コードレビュー ソフトウェア・ピアレビュー ソフトウェア保守 テスト駆動開発 難読コード（英語: obfuscated code） プリファクタリング（英語: prefactoring） 分解 (コンピュータ科学)（英語版）（英: decomposition） モジュラープログラミング（英語: modular programming） リファクタリング (データベース)