第8章 特性の移動
これまでのリファクタリングは、プログラム要素の変更・削除・名前の変更に関するもの
リファクタリングのもう一つの重要な役割は、要素をコンテキスト間で移動させること
関数の移動
優れたソフトウェア設計の核心はモジュール性
モジュール性を実現するために、関連するソフトウェア要素をグループにまとめて、それらの結びつきを簡単に特定して把握できるようにする必要がる
決まった方法があるわけではないので、都度要素を移動させる
関数を移動させる理由
- その関数が存在するコンテキストの要素よりも他のコンテキストの要素を多く参照している場合
- 呼び出し元が存在する場所や、次の拡張時に呼び出したい場所に移動させる時
手順
- 移動対象の関数が現在のコンテキストで使用しているプログラム要素を全て調べる
- それらも移動するか検討
- 選択した関数がポリモーフィックなメソッドかどうか確認する
- 関数を移動どう先のコンテキストにコピーする
- 静的解析
- 元のコンテキストから参照できるようにする
- 元の関数を委譲関数に変更
- テスト
- 元の関数に対して、関数のインライン化の適用を検討する
フィールドの移動
プログラミングにおいてデータ構造が重要
適切なデータ構造は、振る舞いを記述するコードが単純になる
データ構造が適切でないなら、すぐ移動させるべき
フィールドの移動は通常、より広範囲な変更の一部として行う
移動完了後、そのフィールドの利用プログラムの多くは異動先のオブジェクトの方が移動元より適切なアクセス先になる
手順
- 移動元のフィールドをカプセル化する
- テストする
- 異動先にフィールド(およびアクセサ)を作成する
- せい的解説
- 移動元のオブジェクトから、移動先オブジェクトを参考できるようにする
- 異動先のフィールドを使うようにアクセサを調整する
- テスト
- 移動元のフィールドを削除
- テスト
ステートメントの関数内への移動
特定の関数を呼び出すたびに、同じコードが実行されていたら、反復コードを関数自体に取り込むことを検討する
そうした結果、反復コード将来変更が生じた場合でも、一箇所だけ修正するだけで済む
手順
- 反復コードが異動先の関数呼び出しに隣接してない場合、ステートメントのスライドを使って隣接させる
- 移動元の関数の呼び出し元が、移動先の関すだけだった場合、移動元の関数からからコードを異動先の関数にコピペする
- この場合、テストだけ行い、残りの手順はスキップ
- 複数の呼び出し元がある場合、いずれかの呼び出しに対して関数の抽出を行い、異動先の関数呼び出しと移動したいステートメントを抽出する
- 他の全ての呼び出しを変更し、新しい関数を使うようにする
- 元の呼び出しの全てで新しい関数を使うようになったら、関数のインライン化で新しい関数を完全にインライン化し、元の関数を削除する
- 関数名の変更を行なって、新しい関数の名前を元の関数と同じ名前にする
ステートメントの呼び出し側への移動
複数箇所で利用してた共通の振る舞いを、一部の呼び出しに対してだけ変更する必要が出てきた場合
まずステートメントのスライドを使って、関数から呼び出し側に移動する必要がある
その後、ステートメントの呼び出し側への移動を行う
手順
- 呼び出し元が一つか二つで、呼び出される関数が単純な場合、その関数から最初の行をコピペして終わり
- 多い場合、移動したくない全てのステート群を対象に関数の抽出を行う
- 元の関数に対して、関数のインライン化を行う
- 抽出した関数に関数宣言の変更を行なって元の名前に変更する
関数呼び出しによるインラインコードの置き換え
既存の関数と同じ処理をしているインラインコードがある場合、それを関数呼び出しに置き換える
ただし、たまたま類似している場合は例外(例の動画のUserクラスのイメージ)
手順
- インラインコードを既存の関数の呼び出しで置き換える
- テスト
ステートメントのスライド
関係する処理が並んでいると、コードが理解しやすくなる
ステートメントのスライドはもっとも素朴な、コードを集める方法
関数の抽出など、別のリファクタリングの準備として行われる
手順
- コード断片の移動先を特定する
- 干渉が起きるか調べる、起きる場合は諦める
- コード断片を移動先にコピペする
- テスト
ループの分離
同じループの中で異なる処理をしていると、ループの修正の際には、複数の処理内容を理解をする必要がある
処理ごとにループを分離することで変更すべき処理だけを理解すれば良くなる
手順
- ループをコピーする
- 重複による副作用を特定して排除する
- テスト
パイプラインによるループの置き換え
ループより優れた仕組みとして、コレクションのパイプラインがある
手順
- ループ処理のコレクションように新しい変数を作成する
- ループ内の各処理からひとつづつ、コレクションのパイプライン操作に置き換える
- 全ての処理をループから削除できたら、ループを消す
デッドコードの削除
コードが使用されなくなったら削除するべき
必要になったらVCSから掘り起こせば良い
手順
- デッドコードが外部から参照できる場合、呼び出しが残っていないかを確認するため、検索する
- デッドコードを削除する
- テスト
感想
同じコードを関数内に取り込むのって、こんな名前だったんか
パイプラインによるループの置き換えは、ループに比べて副作用が少なくて好き
コードが使用されなくなったら削除するべき
ほんこれ、どれが不要かどうかわからなくなる