コンピュータプログラミングとソフトウェア設計において、コードリファクタリングとは、既存のソースコードの外部動作を変更せずに、ファクタリングを変更して再構築するプロセスです。リファクタリングは、ソフトウェアの機能を維持しながら、ソフトウェアの設計、構造、または実装(非機能属性)を改善することを目的としています。リファクタリングの潜在的な利点には、コードの可読性の向上と複雑性の低減が含まれます。これらは、ソースコードの保守性を向上させ、拡張性を向上させるために、よりシンプルでクリーン、または表現力豊かな内部アーキテクチャまたはオブジェクトモデルを作成できます。リファクタリングのもう1つの潜在的な目標は、パフォーマンスの向上です。ソフトウェアエンジニアは、より高速に動作したり、メモリ使用量が少なくなったりするプログラムを作成するという継続的な課題に直面しています。このような属性を評価するための指標が利用可能であるにもかかわらず、開発者は、その実用性に関する懸念から、リファクタリングの決定を行う際にそれらに頼らないことがよくあります。[ 1 ]
一般的に、リファクタリングは一連の標準化された基本的なマイクロリファクタリングを適用します。それぞれのマイクロリファクタリングは、(通常)ソフトウェアの動作を維持するか、少なくとも機能要件への適合性を変更しない、コンピュータプログラムのソースコードに対するごく小さな変更です。多くの開発環境は、これらの基本的なリファクタリングの機械的な側面を実行するための自動化されたサポートを提供しています。適切に行われた場合、コードリファクタリングは、基盤となるロジックを簡素化し、不要な複雑さを排除することで、ソフトウェア開発者がシステム内の隠れたバグや脆弱性を発見し、修正するのに役立ちます。不適切に行われた場合、外部機能を変更しないという要件を満たせず、新たなバグを引き起こす可能性があります。
コードの設計を継続的に改善することで、コードの扱いやすさはますます向上します。これは、一般的に見られる状況とは大きく異なります。通常、リファクタリングはほとんど行われず、新機能の追加にばかり多くの時間が費やされます。継続的にリファクタリングを行うという習慣を身につければ、コードの拡張と保守が容易になることに気づくでしょう。
—ジョシュア・ケリエフスキー、『パターンへのリファクタリング』[ 2 ]
リファクタリングは通常、コードの臭いに気付いたことがきっかけで行われます。[ 3 ]例えば、対象のメソッドが非常に長かったり、近くにある別のメソッドとほぼ同じ内容だったりする場合があります。このような問題が認識されると、ソースコードをリファクタリングしたり、以前と同じように動作するが「臭い」しない新しい形式に変換したりすることで対処できます。
長いルーチンの場合、1 つ以上のより小さなサブルーチンを抽出できます。重複するルーチンの場合、重複を削除して 1 つの共有関数に置き換えることができます。リファクタリングを実行しないと、技術的負債が蓄積される可能性があります。一方、リファクタリングは技術的負債を返済する主要な手段の 1 つです。[ 4 ]
リファクタリングという活動には、大きく分けて2つのメリットがあります。
パフォーマンスエンジニアリングは、アプリケーションの実行時間よりも開発時間を最小限に抑えることを目的とした従来のソフトウェア開発戦略から生じる、ソフトウェアの肥大化と呼ばれるプログラムの非効率性を排除することができます。パフォーマンスエンジニアリングは、並列プロセッサやベクトルユニットを活用するなど、ソフトウェアが実行されるハードウェアに合わせてソフトウェアを調整することもできます。 [ 6 ]
リファクタリングを行うべきタイミングは2つ考えられます。
予防的リファクタリングと修正的リファクタリングのバランスを取る方法として、「リファクタリングの責任共有」があります。このアプローチでは、リファクタリング作業を2つの段階と2つの役割に分けます。コードの元の開発者はリファクタリングのためにコードを準備するだけで、コードに問題が生じた場合は、後続の開発者が実際のリファクタリング作業を実行します。[ 7 ]
リファクタリングでは、既存のソフトウェアシステムの知識を取り戻すために、ソフトウェアシステムの構造、データモデル、およびアプリケーション間の依存関係を抽出する必要があります。[ 8 ] チームの交代は、システムの現在の状態や、退職した開発者が行った設計上の決定に関する知識が欠落または不正確になることを意味します。さらなるコードリファクタリング活動では、この知識を取り戻すために追加の労力が必要になる場合があります。[ 9 ] リファクタリング活動は、ソフトウェアシステムの構造アーキテクチャを劣化させるアーキテクチャの変更を生成します。このような劣化は、保守性や理解しやすさなどのアーキテクチャ特性に影響を与え、ソフトウェアシステムの完全な再開発につながる可能性があります。 [ 10 ]
リファクタリングの前に自動単体テストを設定して、ルーチンが期待どおりに動作することを保証する必要があります。[ 11 ]単体テストは、単一のアトミックコミットで実行すれば、大規模なリファクタリングでも安定性をもたらすことができます。複数のプロジェクトにまたがる安全でアトミックなリファクタリングを可能にする一般的な戦略は、モノレポと呼ばれる単一のリポジトリにすべてのプロジェクトを保存することです。[ 12 ]
単体テストが実施されると、リファクタリングは、小さなプログラム変換を行い、その正しさを確認するためにテストを行い、さらに小さな変換を行うという反復的なサイクルになります。テストが失敗した場合は、最後の小さな変更が元に戻され、別の方法で繰り返されます。多くの小さなステップを経て、プログラムは元の状態から目的の状態へと移行します。この反復的なプロセスが実用的であるためには、テストが非常に高速に実行される必要があります。そうでないと、プログラマはテストの終了を待つために多くの時間を費やすことになります。エクストリームプログラミングやその他のアジャイルソフトウェア開発の支持者は、この活動をソフトウェア開発サイクルの不可欠な部分として説明しています。リファクタリングは、テストコードだけでなく本番コードにも適用されます。実証研究によると、テストリファクタリングは主にテストスメルを示す低品質のテストコードを対象とし、テストコードの結合度、凝集度、サイズを改善しますが、必ずしもコードカバレッジを向上させるわけではありません。[ 13 ]
以下にマイクロリファクタリングの例をいくつか示します。これらの例の中には、特定の言語または言語タイプにのみ適用されるものもあります。より長いリストは、Martin Fowler氏のリファクタリングに関する書籍[ 3 ]および Web サイト[ 14 ]で確認できます。多くの開発環境は、これらのマイクロリファクタリングを自動的にサポートします。たとえば、プログラマーは変数名をクリックし、コンテキスト メニューから「フィールドをカプセル化」リファクタリングを選択できます。IDE は、通常、適切なデフォルト値とコード変更のプレビューとともに、追加の詳細情報を要求します。プログラマーによる確認後、必要な変更がコード全体に実行されます。
静的プログラム解析(厳密性の低いインタプリタ型言語で実行される場合は「リンティング」と呼ばれる)は、有効ではあるものの標準を満たしていないプログラム内の問題を検出する。
変換とは、プログラムの構文表現を変更することです。一部の変換は、プログラムのセマンティクスや構造を変更し、柔軟性や堅牢性を向上させます。このような変換には、問題領域と意図されたロジックに関する知識が必要となるため、自動化は困難です。一方、プログラムの読みやすさや変更しやすさを向上させる変換も存在しますが、これらはプログラムの根底にあるロジックを変更しません。これらの変換は自動化が可能です。
リファクタリングという用語は元々ソフトウェアコードのリファクタリングのみを指していましたが、近年ではハードウェア記述言語で書かれたコードもリファクタリングされるようになりました。ハードウェアリファクタリングという用語は、ハードウェア記述言語のコードのリファクタリングの略語として使用されます。ハードウェア記述言語はほとんどのハードウェアエンジニアによってプログラミング言語とはみなされていないため、[ 20 ]ハードウェアリファクタリングは従来のコードリファクタリングとは別の分野として考えられます。
ZengとHussは、アナログハードウェア記述(VHDL-AMS)の自動リファクタリングを提案した。[ 21 ]彼らのアプローチでは、リファクタリングによってハードウェア設計のシミュレーション動作が維持される。改善される非機能的な指標は、リファクタリングされたコードは標準的な合成ツールで処理できるが、元のコードは処理できないことである。デジタルハードウェア記述言語のリファクタリング(手動リファクタリングではあるが)も、SynopsysのフェローであるMike Keatingによって研究されている。[ 22 ] [ 23 ]彼の目標は、複雑なシステムを理解しやすくすることで、設計者の生産性を向上させることである。
出版された文献で「リファクタリング」という用語が初めて使用されたのは、1990 年 9 月にWilliam OpdykeとRalph Johnsonが発表した記事です。[ 24 ] コードのリファクタリングは数十年にわたって非公式に行われてきましたが、William Griswoldの 1991 年の博士論文[ 25 ] は、関数型プログラムと手続き型プログラムのリファクタリングに関する最初の主要な学術研究の 1 つです。続いて、 オブジェクト指向プログラムのリファクタリングに関するWilliam Opdykeの 1992 年の博士論文[ 26 ]が発表されました[ 27 ]。ただし、理論と仕組みはすべて、プログラム変換システムとしてずっと前から利用可能でした。これらのリソースはすべて、リファクタリングの一般的な方法のカタログを提供しています。リファクタリングの方法には、その方法を適用する方法の説明と、その方法を適用すべき (または適用すべきでない) タイミングを示す指標があります。
マーティン・ファウラーの著書『リファクタリング:既存コードの設計改善』は、この分野における決定的な参考書である。
「ファクタリング」と「ファクタリングアウト」という用語は、少なくとも1980年代初頭からフォースコミュニティでこのように使用されてきました。レオ・ブロディの著書『Thinking Forth』(1984年)[ 28 ]の第6章はこのテーマに捧げられています。
エクストリームプログラミングにおいて、メソッド抽出リファクタリング手法は、Forthにおけるファクタリングと本質的に同じ意味を持ちます。つまり、「ワード」(または関数)をより小さく、保守しやすい関数に分解することです。
リファクタリングは事後的に再構築することもでき[ 29 ]、 Gitのようなソフトウェアリポジトリに記録された複雑なソフトウェア変更の簡潔な説明を生成します。
多くのソフトウェア開発ツールには、リファクタリング機能が備わっています。例としては、以下のようなものがあります。