BOOK Preview

Code Complete 第2版 上・下
― 完全なプログラミングを目指して

第24章 リファクタリング

マイクロソフトプレスの書籍紹介ページ
書籍情報のページ
2005/05/10


24.3 リファクタリングの詳細

 ここでは、リファクタリングの一覧を紹介する。これらの多くは、『Refactoring』(Fowler 1999)で解説されている詳細な説明を要約したものである。ただし、この一覧ですべてのリファクタリングを網羅できるとは考えていない。本書で「悪いコード」の例や「良いコード」の例として紹介しているケースは、すべてがある意味、リファクタリングの候補である。ここでは、個人的に最も効果的であると考えているリファクタリングを中心に見ていく。

24.3.1 データレベルのリファクタリング

 次のリファクタリングは、変数やその他のデータの使用法を改善する。

■ マジックナンバーを名前付きの定数に置き換える
 3.14といった数値や文字列リテラルを使用している場合は、それらをPIのような名前付きの定数に置き換える。

■ 変数名をより明白なものにするか、その目的がわかりやすいものに変える
 変数名が明白でない場合、よりわかりやすい名前に変更する。もちろん、これは定数、クラス、ルーチンの名前にも当てはまる。

■ 式をインラインにする
 式の結果が代入される中間変数を、式そのものに置き換える。

■ 式をルーチンに置き換える
 式をルーチンに置き換える(通常は、コード内の重複した式をなくすため)。

■ 中間変数を導入する
 式の目的を要約した名前を持つ中間変数に式を代入する。

■ 多目的変数を複数の単一目的変数に変換する
 1つの変数を複数の目的に使用している場合(代表的な被疑者はi、j、temp、x)、目的ごとに別々の変数を作成して、それぞれに具体的な名前を付ける。

■ ローカルの目的には引数ではなくローカル変数を使用する
 ルーチンの入力専用の引数がローカル変数として使用されている場合は、ローカル変数を作成して、代わりにそれを使用する。

■ データプリミティブをクラスに変換する
 データプリミティブに関して振る舞い(より厳密な型チェックなど)やデータを追加する必要がある場合は、データをオブジェクトに変換し、必要な振る舞いを追加する。これはMoneyやTemperatureといった単純な数値型に当てはまる。また、Color、Shape、Country、OutputTypeといった列挙型にも当てはまる。

■ 一連の型コードをクラスまたは列挙に変換する
 従来のプログラムでは、次のような割り当てがよく行われる。

const int SCREEN = 0;
const int PRINTER = 1;
const int FILE = 2;

 スタンドアロンの定数を定義する代わりにクラスを作成すれば、より厳密な型チェックを実施することや、必要であればOutputTypeにより多くの意味を持たせることができる。クラスの代わりに列挙を作成するのもよいだろう。

■ 一連の型コードをスーパークラスとサブクラスに変換する
 要素の型に応じて振る舞いを変えたい場合は、型のスーパークラスを作成し、型コードごとにサブクラスを作成することを検討する。たとえば、OutputTypeというスーパークラスを作成すれば、Screen、Printer、Fileといったサブクラスが作成できる。

■ 配列をオブジェクトに変更する
 さまざまな型の要素から成る配列を使用している場合は、配列の各要素をフィールドに持つオブジェクトを作成する。

■ コレクションをカプセル化する
 クラスがコレクションを返す場合、コレクションのインスタンスが複数生成されると、それらの同期が問題となる。クラスから返されるコレクションを読み取り専用にし、コレクションの要素を追加したり削除したりするクラスルーチンの提供について検討する。

■ 従来のレコードをデータクラスに置き換える
 レコードのメンバで構成されたクラスを作成する。クラスを作成することにより、エラーチェックや永続性など、レコードに関連する処理を一元的に管理できるようになる。

24.3.2 ステートメントレベルのリファクタリング

 次のリファクタリングは、ステートメントの使用法を改善する。

■ 論理式を分解する
 式の意味を表す名前の付いた中間変数を導入することにより、論理式を単純にする。

■ 複雑な論理式をわかりやすい名前の付いた論理関数にする
 式が複雑すぎる場合は、このリファクタリングによって式が理解しやすくなる可能性がある。式が何度か使用されている場合は、このリファクタリングによって並行修正の必要がなくなり、式を使用する際のエラーも少なくなる。

■ 条件文に分散している重複するコードを1つにまとめる
 if文の最後にあるelse文の最後で同じコードが繰り返されている場合は、それらのコードをif-then-elseブロック全体の後に移動する。

■ ループ制御変数ではなくbreakやreturnを使用する
 ループを制御するためにループ内でdoneといった変数を使用している場合は、代わりにbreakまたはreturnを使ってループを終了する。

■ ネストしたif-then-elseブロックで答えがわかったら、戻り値を代入せずに、すぐに制御を戻す
 戻り値がわかった時点ですぐにルーチンを終了すると、コードが読みやすくなり、エラーが少なくなることが多い。戻り値を設定してから多数のロジックを逆戻りする方法は理解しにくいだろう。

■ 条件文、特にcase文の繰り返しをポリモーフィズムで書き換える
 構造化プログラムでよくcase文に含まれていたロジックの大半は、継承の階層にまとめて、ポリモーフィックなルーチン呼び出しを使って実行することができる。

■ null値を評価するのではなくnullオブジェクトを生成して使用する
 nullオブジェクトが汎用的な振る舞いやそれに関連するデータを持つことがある。たとえば、名前が確認されていない顧客を「不明」として参照するような場合、このようなnull値の処理責任をクライアントコードからクラスへ移動する。そして、クライアントコードで顧客の名前が確認されているかどうかを1つずつ評価して、確認されなければ「不明」に置き換えるのではなく、Customerというクラスで名前が確認されていない顧客を「不明」として定義する。

24.3.3 ルーチンレベルのリファクタリング

 次のリファクタリングは、個々のルーチンレベルでコードを改良する。

■ ルーチンやメソッドを抽出する
 ルーチンからインラインコードを削除し、それを1つのルーチンとして独立させる。

■ ルーチンのコードをインラインにする
 説明を必要としない単純な本体を持つルーチンのコードを抜き出し、それを使用する場所でインラインコードにする。

■ 長いルーチンをクラスに変換する
 ルーチンが長すぎる場合は、それをクラスに書き換えて複数のルーチンに分解すると、コードが読みやすくなることがある。

■ 複雑なアルゴリズムを単純なアルゴリズムで代用する
 複雑なアルゴリズムをより単純なアルゴリズムに置き換える。

■ 引数を追加する
 ルーチンが呼び出し元からの情報をさらに必要とする場合は、引数を追加して情報を提供できるようにする。

■ 引数を削除する
 ルーチンが使用しなくなった引数は削除する。

■ 照会と変更を分離する
 通常、照会処理ではオブジェクトの状態は変更されない。GetTotal()といった処理でオブジェクトの状態が変更される場合は、照会機能と状態変更機能を切り離し、2つのルーチンとして独立させる。

■ 同じようなルーチンは引数を介在させてまとめる
 同じようなルーチンが2つあり、ルーチン内で使用する定数値だけが異なるような場合は、2つのルーチンを1つにまとめ、ルーチンが使用する値を引数として渡す。

■ 入力引数によって振る舞いの異なるルーチンを分離する
 ルーチンが入力引数の値に応じて別々のコードを実行する場合は、ルーチンを分けて、特定の引数を渡さなくても個別に呼び出せるようにすることを検討する。

■ 特定のフィールドではなくオブジェクト全体を渡す
 同じオブジェクトの複数の値をルーチンに渡している場合は、ルーチンのインターフェイスを変更して、オブジェクト全体を渡すことを検討する。

■ オブジェクト全体ではなく特定のフィールドを渡す
 ルーチンに引数として渡すためだけにオブジェクトを生成している場合は、ルーチンのインターフェイスを変更して、オブジェクト全体ではなく特定のフィールドを渡すことを検討する。

■ ダウンキャストをカプセル化する
 ルーチンがオブジェクトを返すとしたら、通常は最も具体的な型のオブジェクトを返さなければならない。これは特に、イテレータ、コレクション、コレクションの要素などを返すルーチンに当てはまる。


 INDEX
  Code Complete 第2版 上・下
  第24章 リファクタリング
    1.24.1 ソフトウェアの進化の種類
    2.24.2 リファクタリング概論
  3.24.3 リファクタリングの詳細(1)
    4.24.3 リファクタリングの詳細(2)
    5.24.4 安全なリファクタリング
    6.24.5 リファクタリング戦略
 
インデックス・ページヘ  「BOOK Preview」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間