テストとコード品質

[コードスメル #4] God Object(神オブジェクト)徹底解説

God Objectは周囲の責務とデータを吸収し続けて肥大化するクラスで、チームで最も変更と競合が多いファイルになります。肥大化の兆候、分解の順序、分割に失敗する2つの方法を整理します。

読了 8 分
[コードスメル #4] God Object(神オブジェクト)徹底解説のカバー画像

どのプロジェクトにも、そんなファイルが1つあります。開いた瞬間、スクロールバーが糸のように細くなるファイルです。

前回の記事 コードスメル #3 から続く内容です。

名前は AppManager、MainViewController、DataStore などです。チームで最も頻繁に変更され、最も頻繁に競合するファイルでもあります。

God Objectです。

1998年刊行の『AntiPatterns』では、これをThe Blobと呼びます(出版情報)。周囲の責務とデータを吸収し続けて肥大化する塊を指します。

God Objectとは何か

行数だけで決まるわけではありません。2,000行の型でも、1つの仕事だけをするなら単に大きな型です(God Class判定に関する研究)。

God Objectを分けるのは、何を知っているかの範囲です。3つが重なると、ほぼ確実です。

  • 知りすぎています。 ネットワーク、データベース、画面状態、ログイン情報、決済まで1つの型に入っています。import一覧だけでも明らかです。
  • それを知っているものが多すぎます。 プロジェクトのあちこちからこの型を参照します。シングルトンなら参照経路がコード上さらに見えにくくなります。
  • 状態が分散しています。 プロパティは30個あるのに、どの組み合わせが有効か誰にも分かりません。

isLoadingこれがtrueのときに、errorこれもnilではない状態が可能か、同じ質問に答えられなくなります。

iOSでは、この役割はたいてい決まっています。ビューコントローラです。

1つの画面のレイアウト、ネットワーク呼び出し、テーブルのデータソース、画面遷移、状態管理がすべて1ファイルに集まります。

この構造をMassive View Controllerと呼びます。名前にAppDelegateやManagerが付く型も定番です。

なぜ肥大化し続けるのか

最初からそう作ろうとした人はいません。そこが本質です。

God Objectは設計判断ではなく、重力の結果です。

新機能を追加するとします。必要なデータはすでにこのクラスにすべてあります。

選択 コスト
既存クラスにメソッドを1つ追加 20分
新しい型を作成 依存性の受け渡し、初期化箇所、ライフサイクルまで半日

毎回合理的な選択をしますが、その選択はいつも同じ方向を向きます。200行だったファイルが3年後には4,000行になります。

この成長には段階ごとに正当な理由が付きます。だから戻すのが難しくなります。

そこに割れ窓効果も加わります。すでに3,000行あるファイルに50行追加しても、誰も反対しません。

200行のファイルに50行追加するときとは、心理的な抵抗が違います。

ネットワーク・DB・セッション・決済をすべて把握する4,000行のビューコントローラの依存関係図
この矢印の数が、そのままテストの難しさです

どこが問題なのか

被害は4つの形で現れます。

  • テストできなくなります。 この型1つを作るには、ネットワーク、データベース、ユーザーセッションがすべて必要です。純粋な計算ロジックを検証したいのに、サーバーが起動していなければならない状況になります。
  • 並行作業が止まります。 3人のチームメンバーが別々の機能を作っているのに、全員が同じファイルを変更します。競合が常態化し、レビューでは変更が混ざって見えます。
  • 変更の影響範囲が分かりません。 1つのプロパティを変更して何が壊れるか予測するには、4,000行すべてを理解する必要があります。実際には誰も分からないので、新しいプロパティを追加します。さらに肥大化します。
  • 再利用できません。 中の日付フォーマット処理が別の画面でも必要なのに、取り出すにはクラス全体が付いてきます。だからコピーして貼り付けます。

特に最初の問題が致命的です。テストがないから分割できず、分割できないから肥大化し続けます。

この循環こそが、God Objectが長く生き残る本当の理由です。

肥大化をどう見抜くか

感覚ではなく、兆候を使います。

兆候1 · Gitの変更頻度

最も実用的な指標です。過去1年間に最も多く変更されたファイルを抽出すると、God Objectが上位に出てきます。

git log --format=format: --name-only --since=1.year.ago \
  | grep '\.swift$' | sort | uniq -c | sort -rn | head -20

変更が多いのは、そのファイルが異なる理由で変更されている兆候です。単一責任原則違反の実測値です。

兆候2 · 凝集度

メソッドが互いに異なるプロパティ集合だけを操作しているなら、その型は実質的に2つである可能性が高いです。

プロパティA・Bだけを使うメソッドが5つ、C・Dだけを使うメソッドが6つあるなら、境界の候補はすでに見えています。

兆候3 · 型の長さのルール

SwiftLintを使うなら、type_body_lengthとfile_lengthがデフォルトで有効です。

型本体250行、ファイル400行がデフォルトの警告ラインで、プロジェクトに合わせて調整できます。数字自体は任意ですが、線を越えた瞬間に会話が始まることに価値があります。

分解の順序

一度に書き直すと、ほぼ失敗します。順序があります。

# 段階 何をするか
1 網を張る テストがなければ、まず特性テストを作ります
2 ステートレスなロジックを分離 日付フォーマット、文字列検証、金額計算から切り出します
3 状態の塊を分離 一緒に変わるプロパティを1つの型にまとめます
4 役割ごとに分離 データソース、コーディネータ、ビューモデル、子ビューコントローラに分けます
5 吸収経路を断つ 新しいコードの置き場所を決め、境界を設けます

特性テストは、コードが正しいかではなく、今どう動いているかをそのまま記録するテストです。マイケル・フェザーズが『レガシーコード改善ガイド』で整理した手法です。

動作を保ったまま移すのが目的なので、現在の動作が基準線になります。

2段階目はインスタンス状態を使わないロジックなので、最も簡単です。この段階でファイルが目に見えて短くなることがよくあります。

3段階目ではSwiftのenumが役立ちます。画面状態3つがいつも一緒に変わるなら、それは1つの状態オブジェクトです。

// 前: 無効な組み合わせを表現できる
var isLoading = false
var items: [Item] = []
var error: Error?

// 後: 3つのうち1つだけが存在する
enum ViewState {
    case loading
    case loaded([Item])
    case failed(Error)
}

enumでまとめれば、あり得ない組み合わせそのものをなくせます。

4段階目のiOSの道筋は決まっています。テーブルのデータソースは別型に、画面遷移はコーディネータに、画面状態と表示ルールはビューモデルに、大きな画面は子ビューコントローラに分けます。

5段階目を省くと、6か月後には元に戻ります。次の機能が再びそのファイルに入らないよう、新しいコードの場所を明確にし、ファイル長のルールやレビュー合意で境界を作ります。

巨大な単一クラスを段階的に分離し、小さな型に分ける分解過程の画像
目標は断片の数ではなく、変更理由を1つにすることです

分割に失敗する2つの方法

失敗パターン 起きること
名前だけ変える AppManagerをUserManager・DataManager・NetworkManagerの3つに分けても、3つが互いをすべて参照します。God ObjectがGod Clusterになっただけです
細かく分けすぎる 4,000行のクラスを40個の型に分けると、今度は流れが消えます。前回見たRavioli Codeです

分けるときは、依存の向きが一方向だけに流れているかも確認する必要があります。

分解の目的は断片を作ることではなく、各断片が自分の理由だけで変わるようにすることです。断片の数は結果にすぎません。

まとめ

  • God Objectは大きさではなく、知っている範囲で判定します。知りすぎていて、多くのものに知られており、状態が分散していれば該当します。
  • 悪い設計だからではなく、毎回最も安い選択をした結果として成長します。
  • 最大の被害はテスト不能です。テストがないから分割できず、分割できないから肥大化し続けます。
  • Gitの変更頻度上位リストは、実測指標として使えます。
  • 特性テスト → ステートレスなロジック → 状態の塊 → 役割分離の順に分解し、最後に再び肥大化する経路を塞ぎます。

次回は、God Objectを分割した後に出会う問題です。1つの機能を変えるのに12ファイルを修正する状態、Shotgun Surgeryを見ます。

出典と確認基準

  • AntiPatterns — Wiley · 公式データ · 確認 2026-08-17 · 根拠: 1998年のAntiPatterns刊行情報とThe Blobの文脈
  • Human Perception on God Class Detection — Springer Nature · 論文原文 · 確認 2026-08-17 · 根拠: God Class判定における人とツールの違いに関する統制実験

あわせて読みたい