0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【書評】新装版 リファクタリング―既存のコードを安全に改善する

0
Posted at

『リファクタリング 第2版』要約 ― コードを「動く」から「変えられる」へ

はじめに

Martin Fowler 著『Refactoring: Improving the Design of Existing Code(第2版)』は、
コードの「動作を変えずに設計を改善する」技術を体系化した書籍です。
サンプルコードは JavaScript ですが、考え方は言語を問いません。


第1章 リファクタリング:最初の例

小さな劇団の請求書計算プログラムを題材に、リファクタリングの手順を実演しています。

出発点は1つの巨大な statement() 関数。変更要件(HTML 出力対応、新ジャンル追加)が来たとき、
コードが複雑すぎて手が出せない状態です。

改善の流れは大きく3段階です。

  1. 関数の分解 ― switch 文や計算ロジックを小さな関数(amountForvolumeCreditsFor)に抽出する。
  2. フェーズの分離createStatementData() で計算データを生成し、renderPlainText() / renderHtml() で表示する構造に分ける。
  3. ポリモーフィズムの導入 ― 演劇ジャンルごとのサブクラス(TragedyCalculatorComedyCalculator)を作り、条件分岐を排除する。

ポイント:リファクタリングは「小さなステップ × コンパイル → テスト → コミット」の繰り返しが前提です。
一度に大きく変えると、バグが混入したとき原因を特定できなくなります。


第2章 リファクタリングの原則

定義

リファクタリング(名詞): 観察可能な動作を変えずに、ソフトウェアの内部構造を変更すること。

「コードを整理する作業」全般をリファクタリングと呼ぶのは誤りで、
Fowler は「動作を保ったまま行う小さなステップの連続」と明確に定義しています。
「数日コードが壊れていた」のはリファクタリングではありません。

なぜやるのか

  • 設計の劣化を防ぐ ― 手を入れるたびに設計が崩れていく。定期的なリファクタリングで健全性を保つ。
  • 可読性の向上 ― 頭の中の理解をコードに移植することで、将来の自分や同僚が読めるようになる。
  • バグ発見の補助 ― 構造が明確になるほど、仮定の誤りが浮かび上がる。
  • 開発速度の維持(設計スタミナ仮説) ― 内部品質が高いコードは、新機能追加が速い。泥沼化したコードは逆。

いつやるか

タイミング 内容
準備的 機能追加の直前に、変更しやすい形に整える
理解的 コードを読みながら、理解をコードに反映させる
ゴミ拾い 気づいた小さな問題をその場で直す
長期的 数週間かけて少しずつ改善する(チーム全体で)

基本は「機会主義的」なリファクタリングです。専用スプリントを組むのではなく、
日常的な機能追加・バグ修正の流れの中で行います。

リファクタリングしないほうがいいとき

  • 触る必要のないコード(API として扱える醜いコードはそのままでもよい)
  • リファクタリングより書き直しのほうが明らかに早い場合

注意点:マネージャーへの伝え方

技術的理解のないマネージャーには「リファクタリング」と言わず、
機能追加・バグ修正の一環として行うのが現実的だと Fowler は述べています(物議を醸す指摘)。


第3章 コードの悪臭

リファクタリングすべきタイミングを嗅ぎ分けるための「臭い」一覧です。
代表的なものをピックアップします。

臭い 概要
重複したコード 同じロジックが複数箇所にある
長い関数 処理を読み解くのに時間がかかる
長いパラメータリスト 引数が多すぎて意図が読めない
グローバルデータ どこからでも変更できる危険な状態
変更に要するショットガン手術 1つの変更で複数ファイルを修正する必要がある
データの泥団子 いつも一緒に使うデータが別々に定義されている
基本データ型への執着 クラスで表現すべきものをプリミティブで扱っている
スイッチ文 型ごとの条件分岐が複数箇所に重複する
怠慢なクラス ほとんど役割がない
推測による汎用性 将来使いそうだからと作った機能が実際には使われない

第4章 テストの構築

リファクタリングの大前提は 自己テストコード の存在です。

  • テストがなければリファクタリングは危険な綱渡り。
  • テストは頻繁に実行できるよう高速であることが必要。
  • テスト失敗時に「最後に通ったコードとの差分」が少なければ、バグの原因が一目瞭然。

xUnit 系フレームワーク(JUnit、JUnit5、テスト対象言語のテストライブラリ)の活用を推奨しています。


第5章 カタログの紹介

第6〜12章は、具体的なリファクタリング手法のカタログです。
各手法は「動機 → やり方 → 例」の形式で説明されています。


第6章 リファクタリングの最初のセット(主要手法)

手法名 概要
関数抽出(Extract Function) コード片を独立した関数に切り出す
関数のインライン化(Inline Function) 呼び出しよりコードを直接書いたほうが明確なときに逆を行う
変数の抽出(Extract Variable) 複雑な式に名前を付けて変数に格納する
変数のインライン化(Inline Variable) 変数がかえって邪魔なとき、式に戻す
関数宣言の変更(Change Function Declaration) 関数名・引数名を意図を表す名前に変える
変数のカプセル化(Encapsulate Variable) 可変データへのアクセスを関数経由にする
変数名の変更(Rename Variable) コンテキストに合った名前に変える
パラメータオブジェクトの導入(Introduce Parameter Object) 関連する引数をまとめてオブジェクト化する
関数群のクラスへの集約(Combine Functions into Class) 共通データを扱う関数をクラスにまとめる
フェーズの分離(Split Phase) 処理を「入力の変換」と「出力の整形」に分離する

第7章 カプセル化

設計上の重要原則として、データを直接公開しない ことが強調されています。

  • レコードのカプセル化 ― 生のデータ構造をクラスに包む。
  • コレクションのカプセル化 ― リストをそのまま返さず、追加・削除メソッドを通じてのみ変更させる。
  • 一時変数をクエリ関数に置換(Replace Temp with Query) ― 長い関数を分解するために、まず一時変数を関数化する。
  • クラスの抽出(Extract Class) ― 責務が増えたクラスを分割する。
  • 委譲の隠蔽(Hide Delegate) ― 呼び出し元が内部構造に依存しないよう、メソッドで中継する。

第8章 移動機能

コードを「あるべき場所」に移す手法群です。

  • 関数の移動(Move Function) ― 使う側のコンテキストに関数を移す。
  • フィールドの移動(Move Field) ― データを参照される場所に近づける。
  • ループのパイプライン化(Replace Loop with Pipeline)for ループを filtermapreduce に置き換える。
  • デッドコードの削除(Remove Dead Code) ― 使われていないコードは躊躇なく消す(バージョン管理があるから)。

第9章 データの整理

手法 概要
変数の分割(Split Variable) 複数の役割を持つ変数を分割する
フィールド名の変更(Rename Field) データ構造のフィールド名を明確にする
マジックナンバーの置換(Replace Magic Literal) 数値リテラルを意味のある名前の定数に置き換える
変数のカプセル化 再掲。グローバル変数は特に注意が必要
参照から値への変更(Change Reference to Value) オブジェクトを値として扱い、変更不可にする

第10章 条件付きロジックの簡素化

  • 条件の分解(Decompose Conditional) ― 複雑な条件式を関数に抽出して名前を付ける。
  • 条件式の統合(Consolidate Conditional Expression) ― 同じ結果の条件をまとめる。
  • ガード節による入れ子条件の置換(Replace Nested Conditional with Guard Clauses) ― 早期リターンで深いネストを解消する。
  • ポリモーフィズムによる条件の置換(Replace Conditional with Polymorphism) ― 型ごとの switch をサブクラスの override に変える。
  • 特殊ケースの導入(Introduce Special Case) ― null チェックや特殊値の処理を専用クラスにまとめる(Null Object パターン)。

第11章 API のリファクタリング

  • クエリと修飾子の分離(Separate Query from Modifier) ― 値を返す関数と副作用を起こす関数を分ける(コマンドクエリ分離)。
  • パラメータの統合(Parameterize Function) ― 似た関数を引数で吸収する。
  • フラグパラメータの削除(Remove Flag Argument)boolean 引数で挙動を変えるのをやめ、関数を分ける。
  • コンストラクタをファクトリ関数に置換(Replace Constructor with Factory Function) ― インスタンス生成の柔軟性を上げる。
  • コマンドオブジェクトへの置換(Replace Function with Command) ― 複雑な関数をクラスに変えることで、undo やサブクラス化を可能にする。

第12章 相続の扱い

継承に関するリファクタリングです。

  • メソッドのプルアップ/プッシュダウン(Pull Up / Push Down Method) ― スーパークラスとサブクラスの間でメソッドを移す。
  • フィールドのプルアップ(Pull Up Field) ― 重複したフィールドを親に集約する。
  • 継承をデリゲートに置き換える(Replace Subclass with Delegate) ― 継承より委譲のほうが柔軟なケースに対応する。
  • スーパークラスをデリゲートに置き換える(Replace Superclass with Delegate) ― 「is-a」が不適切なとき、「has-a」に変える。

全体を通じての感想

本書のメッセージを一言でまとめると、

「リファクタリングは品質のためではなく、スピードのためにやる」

です。コードが美しいこと自体が目的ではなく、変更しやすい設計が「次の機能追加を速くする」という経済的合理性の話として一貫して語られています。

カタログ部分(第6〜12章)は辞書的な使い方が合っており、
実際に「このコード、臭うな」と感じたときに手法名を引くのが実用的です。

継続的インテグレーション(CI)・テスト駆動開発(TDD)・YAGNI(You Aren't Gonna Need It)との親和性が高く、
アジャイル開発の文脈で読むと全てが繋がります。


参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?