この記事のポイント

  • ヘッダーファイル依存関係がなぜレビュー対象になるのか整理する
  • レビューでインクルード構造から設計の整理具合を読み取る
  • 依存縮小・循環防止の技術をレビューアー目線で解説する

ヘッダーファイルのインクルード構造は、
設計者の責務整理の具合がそのまま現れる場所 です。
レビューアーは構文を見るだけでなく、
依存が健全に整理されているか? を読めると設計レビュー力が一段上がります。


1. 依存関係とは何か?

1-1. ヘッダーは実質「貼り付け」

C++の#include文字列の貼り付けです。

#include "User.h"
#include "Order.h"

これは実際には以下が貼り付けられているのと同じです。

struct User { ... };
struct Order { ... };

1-2. 依存とは「誰が誰を必要としているか」

#include "User.h"
#include "Order.h"
struct UserOrder { ... };
  • UserOrderは User と Order に依存している

1-3. 依存が膨らみやすい原因

  • 気軽な#include追加
  • 責務分離が曖昧なまま統合
  • 循環包含を誘発しやすい構造

1-4. ビルドや保守の負債に直結する

  • 循環依存でビルドエラー
  • 変更時の再ビルド範囲が肥大化
  • 修正影響範囲が読みにくくなる

2. 実務レビューで読み取るべき「インクルード臭」

臭い 原因
ヘッダー肥大化 全部まとめて管理しようとする設計放棄
他責務インクルード 本来分けるべき責務が混在
不要依存 実際に使ってない型までインクルード
循環参照 責務のねじれが設計に現れている

3. 教材として扱うAPIログモデル

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;
};

シンプルですが、依存整理レビューの例題に使いやすい構成です。


4. 依存関係最小化の考え方

4-1. 最小化とは何を目指すのか?

  • 必要最低限の情報だけインクルードする
  • 前方宣言を活用して依存を減らす
  • 間接依存をできるだけ排除する

4-2. 依存関係を可視化する

UML Diagram
  • 依存が積み重なる部分をレビューで見抜く目を養いたいところです

4-3. 前方宣言で依存を切る

悪い例:完全型依存

#include "ClientInfo.h"

struct ApiRequestLog {
    int requestId;
    ClientInfo client;
};

改善例:前方宣言+ポインタ保持

class ClientInfo;  // 前方宣言

struct ApiRequestLog {
    int requestId;
    ClientInfo* client;
};
  • ポインタや参照なら型の完全定義が不要
  • ヘッダー間の直接依存を減らせる

5. 良い実装例(依存縮小できている状態)

#pragma once

class ClientInfo;
class ErrorDetail;
class AuditLog;

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;

    ClientInfo* client;
    ErrorDetail* errorDetail;
    AuditLog* auditLog;
};
  • ヘッダーに必要最低限の情報のみ
  • 依存先の変更が他ヘッダーに波及しにくい構造

6. レビュー観点まとめ

観点 確認ポイント
不要インクルード削減 直接使う型だけに整理できているか
前方宣言活用 ポインタ・参照は前方宣言で切れているか
循環依存防止 相互包含を防げているか
責務整理 複数責務が混ざっていないか

7. 良くない実装パターンとレビュー例

ケース1:インクルードしすぎ

#include "ClientInfo.h"
#include "ErrorDetail.h"
#include "AuditLog.h"

struct ApiRequestLog {
    ClientInfo client;
    ErrorDetail errorDetail;
    AuditLog auditLog;
};
@Reviewer
全型を完全依存させています。ポインタ+前方宣言で依存縮小してください

ケース2:循環包含発生

// ClientInfo.h
#include "ApiRequestLog.h"  // 循環依存を生む

struct ClientInfo {
    ApiRequestLog* request;
};
@Reviewer
循環包含が発生しています。前方宣言で切り離しましょう

改善例

// ClientInfo.h
#pragma once

class ApiRequestLog;

struct ClientInfo {
    ApiRequestLog* request;
};

8. CI統制と依存削減支援ツール

include-what-you-use 活用

「iwyu」は「Include What You Use(インクルード・ホワット・ユー・ユーズ)」の略で、主にC/C++開発で使用されるヘッダーファイルの最適化ツールです。
👉 iwyuをもっと詳しく

iwyu_tool.py -p build/
  • 不要インクルード検出ツール
  • 設計臭も可視化しやすい

clang-tidy設定例

Checks: '-*,modernize-use-pragma-once'
  • pragma once統一なども自動チェック可能

簡易スクリプトでの漏れ防止

grep -L "#pragma once" $(find include/ -name '*.h')
  • インクルードガード漏れもCIで検出できる

9. チェックリストまとめ

チェック項目 確認内容
インクルード整理 必要最小限に保たれているか
前方宣言適用 できるだけ前方宣言で切り離しているか
循環排除 相互包含が発生していないか
責務分離 ヘッダー単位が適切に整理されているか
include-what-you-use 過剰インクルード警告がないか
CI統制 自動検出が整備されているか

10. まとめ

インクルード整理レビューは「責務の境界線」を読む訓練に直結します。

  • 依存が膨らめば設計負債の芽
  • 依存が整理されていれば設計健全性の証拠

レビューアーは
構文を超えて設計意図の整理具合を読み取るレビュー
を意識していきたいところです。