第6章 リファクタリングはじめの一歩
関数の抽出
極めて頻繁に行うリファクタリング
コードの断片をみて、何をしているか理解した上で、独立した関数として抽出し、目的にふさわしい名前をつける
意図と実装の分離
何をしているか調べなければならないコードの断片があるとしたら、それに名前をつけて抽出するべき
手順
- 新しい関数を作り、その意図に沿って命名する
- 命名は、何をするかによって名前をつける
- 抽出したいコードをもとの関数から新しい関数にコピーする
- 抽出したいコードを調べて、元の関数でスコープ内だった変数をパラメータとして渡す
- 全ての変数を処理したらコンパイルする
- 元の関数に残った抽出前のコードを、抽出された関数への呼び出しに変える
- テストをする
- 残りのコードを見て、抽出したコードと同じまたは類似したコードを探し、新しい関数を呼び形にできないかを検討する
関数のインライン化
うまく分割できない関数があるときにする
手順
- 関数がポリモーフィックなメソッドではないことを確認する
- 関数の呼び出すもとをすべて見つける
- 関数の各呼び出し元を関数の中身で置き換える
- 一つ置き換えるごとにテストする
- 関数の定義を取り除く
変数の抽出
式は複雑で読みにくくなることがある
その場合、ローカル関数を活用して式を分解することで扱いやすくできる
式の一部に名前をつけることができるので、わかりやすくもなる
コード内の式に名前をつけたい場合に有効
作業中の関数の中だけで意味のある名前なら、正しい選択
手順
- 抽出しようとする式に副作用がないことを確認する
- 変更不可な変数を定義する
- 名付けたい式の値をその変数に設定する
- 元の式を新しい変数で置き換える
- テストする
変数のインライン化
変数がリファクタリングの邪魔になることもある
その場合、変数をインライン化するのが有効
手順
- 代入の右辺に副作用がないことを確認する
- その変数が変更不可と宣言されていなければ、変更不可にしてテストする
- その変数への最初の参照を探し、代入の右辺と置き換える
- テストする
- 変数を参照している箇所の置き換えを繰り返し、全ての参照箇所を更新する
- 変数の宣言と代入を取り除く
- テストする
関数宣言の変更
もし間違った名前の関数を見つけたら、少しでも良い名前が分かり次第変える必要がある
手順
- 簡易な手段
- パラメータを削除する場合、それが関数内部で参照されていないことを確認する
- 関数宣言を望ましいものに変更する
- 古い関数宣言へのすべての参照を探し、新しいものに更新する
- テストする
- 移行的手順
- (必要なら)関数本体をリファクタリングする
- 関数の抽出をして、新たな関数を作る
- 抽出した関数が追加のパラメータを必要とする場合、簡易な手順により追加を行う
- テストする
- 古い関数に関数のインライン化をする
- 一時的な名前を使った場合、変数宣言の変更をし、元の名前に戻す
- テストする
変数のカプセル化
広範囲で利用されるデータを移動したい場合に行う
副作用的に、データの変更や参照を監視できる
パプリックなフィールドを見つけた場合、カプセル化して可視性を下げる
手順
- 変数の参照・更新するためのカプセル化関数を作る
- 静的チェックを実行する
- 変数への参照を、一つの適切なカプセル化関数の呼び出しに置き換える
- 置き換えるごとにテストする
- 変数の可視性を成業する
- テストする
- 変数の値がレコードの場合、レコードのカプセル化を検討する
変数名の変更
良い名前をつけることは重要
手順
- 変数が広く使われている場合、変数のカプセル化を検討する
- 変数への参照をくまなく探し、それを全て変更する
- テストする
パラメータオブジェクトの導入
一纏まりのデータが関数から関数に渡されている場合、データを構造体に置き換える
手順
- ふさわしい構造体が存在しないなら、作成する
- テストする
- 関数宣言の変更を実施し、新たな構造体用のパラメータを埋め込む
- テストする
- 新しい構造体を渡すように変更する
- もとのパラメータを使用している箇所を、構造体のデータを使うように置き換える
関数群のクラスへの集約
共通のデータに対してお互いに関わりの深い処理を行う一群の関数があれば、クラスを定義するべきかもしれない
引数が大幅に減ったりするなどのメリットが有る
手順
- 関数感で共有しているデータのレコードにレコードのカプセル化を施す
- 関数の移動を適用して、共通レコードを扱う関数を新しいクラスに移す
- データを操作するロジックの断片があればそれぞれ、関数の抽出をしてクラスに移す
関数群の変換への集約
データ変換関数を利用して、元データを入力として全ての派生値を計算し、それらを出力のデータとしてフォールドとして設定する
派生値について調べたいときに、変換関数だけ見れば良い
わかりにくいのでサンプルソース
def foo(bar):
hoge_bar = hoge(bar)
fuga_bar = fuga(bar)
end
↓
def foo(bar)
bar.hoge = hoge(bar)
bar.fuga = fuga(bar)
return bar
end
手順
- 変換されるレコードを入力とし、同じ値を返す変換関数を作る
- ロジックを選んでその本体を変換関数似写し、レコードに新しいフィールドを設ける
- テストする
- 繰り返す
フェーズの分離
一つのコードが異なる2つの処理を行っている場合、別々のモジュールに分離するべき
この分離を綺麗にするために、振る舞いを順次的な二段階のフェーズに分ける
手順
- 後半となるフェーズのコードを関数として抽出する
- テストする
- 抽出した関数に追加される引数として、中間データ構造を導入する
- 抽出した後半フェーズのパラメーターを確認する
- それが前半でも使われていたら、中間データ構造へ移す
- 前半のフェーズに関数の抽出をして、中間データ構造を返すようにする
感想
変数のカプセル化を読んで、グローバル変数ってそんなに使うか?
普通にどこかのクラスのアクセサとかにまとめたほうが持ち運びしやすくなって良い気がする
でも以前の章で、データのためのクラスってやつになりそうだな…
作者はイギリス人なのに、紅茶党じゃない