C++ヘッダーファイル依存関係の最小化レビュー|インクルード整理と責務分離を読み取る設計レビュー技術
この記事のポイント
- ヘッダーファイル依存関係がなぜレビュー対象になるのか整理する
- レビューでインクルード構造から設計の整理具合を読み取る
- 依存縮小・循環防止の技術をレビューアー目線で解説する
ヘッダーファイルのインクルード構造は、
設計者の責務整理の具合がそのまま現れる場所 です。
レビューアーは構文を見るだけでなく、
依存が健全に整理されているか? を読めると設計レビュー力が一段上がります。
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. 依存関係を可視化する
- 依存が積み重なる部分をレビューで見抜く目を養いたいところです
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. まとめ
インクルード整理レビューは「責務の境界線」を読む訓練に直結します。
- 依存が膨らめば設計負債の芽
- 依存が整理されていれば設計健全性の証拠
レビューアーは
構文を超えて設計意図の整理具合を読み取るレビュー
を意識していきたいところです。