イントロダクション:AI時代の「壊れ方」は速い
AIで実装が速くなるほど、設計の甘さも高速で増幅されます。
特にカプセル化をサボると、AIが内部実装へ直接依存したコードを量産し、
1箇所の変更で広範囲が崩れる事故が起きやすくなります。
カプセル化は、人間とAIの両方に「ここから先は触らない」境界線を示す設計ガードレールです。
このページでは、悪い例と良い例を比較しながら、現場で使える引き方を整理します。
1. カプセル化とは?(情報隠蔽+インターフェース)
カプセル化(Encapsulation)とは、
オブジェクトの内部状態や実装詳細を隠し、外部には必要最小限の操作だけ公開する考え方です。
- 情報隠蔽:内部データや内部手順を外から直接触れないようにする。
- インターフェース:外部が使ってよい入口(public API)だけを明示する。
実務では、private / public を「アクセス制御」ではなく、
変更コストを守るための境界設計として扱うと効果が出ます。
2. AI時代に効く3つの理由
2-1. ガードレールになる(内部依存の量産を止める)
public は「使ってよい入口」、private は「立ち入り禁止」です。
この線引きがないと、AIは見えているものを使って最短実装しがちで、内部依存が増えます。
2-2. AIへのコンテキストを最適化できる
AIに見せるべき中心は「使い方(API契約)」です。内部実装まで露出すると、
AIがどこを触るべきか迷いやすくなり、誤依存やハルシネーション(嘘)を呼び込みます。
- 触って良い public API:calculateTotal() / addItem() など(=入口はここだけ)
- 触ってはいけない内部:taxRate / workflowStep / cache など(=中身は変更OK、外から触らない)
- 追加・変更はAPI経由:機能追加は「新しいpublicメソッド」か「既存APIの拡張」で。内部を直に参照する変更はNG
この3行があるだけで、AIが「どこまで触っていいか」を誤解しにくくなります。
2-3. 役割分担を固定できる(人間が設計、AIが実装)
人間は「殻(境界と責務)」を決め、AIは中身を実装・差し替える。
この分担ができるほど、外部APIを壊さず内部を更新できます。
これがAIリファクタリング耐性(実装差し替え耐性)です。
3. 悪い例:内部実装に依存して壊れる
Beforeは、AIがやりがちな「public露出」「内部状態の直接参照」「内部手順への依存」が混ざった例です。
Before:外部から内部を直接いじれる設計
// 悪い例: 内部状態を public で露出
class AiBillingService {
constructor() {
this.taxRate = 0.1; // 本来は隠したい内部ルール
this.subtotal = 0; // 外部が直接書き換え可能
this.workflowStep = 'init';// 手順の内部状態まで見えている
}
prepare(subtotal) {
this.subtotal = subtotal;
this.workflowStep = 'prepared';
}
applyTax() {
if (this.workflowStep !== 'prepared') throw new Error('step error');
this.subtotal = Math.floor(this.subtotal * (1 + this.taxRate));
this.workflowStep = 'taxed';
}
finalize() {
if (this.workflowStep !== 'taxed') throw new Error('step error');
return this.subtotal;
}
}
const billing = new AiBillingService();
// AIが短絡的に内部を直接変更しがちな箇所
billing.taxRate = 0.2; // 内部ルールを書き換える
billing.workflowStep = 'taxed'; // 手順を飛ばす
billing.subtotal = 1000; // 内部状態を外から改ざん
console.log(billing.finalize()); // たまたま動いても壊れやすい
何が壊れるか:内部状態・手順・計算ルールに外部が依存するため、
workflowStep の名前変更や内部フロー変更だけで呼び出し側が連鎖的に破綻します。
4. 良い例:公開APIだけで完結させる
Afterは、外部が触れる入口を最小化し、状態遷移と計算手順をカプセル内に閉じ込めた例です。
After:public APIを最小化して内部を private 化
class BillingService {
#taxRate;
#subtotal;
constructor(taxRate = 0.1) {
this.#taxRate = taxRate;
this.#subtotal = 0;
}
// 公開する入口はこれだけ
calculateTotal(subtotal) {
if (subtotal < 0) throw new Error('subtotal must be >= 0');
this.#subtotal = subtotal;
return Math.floor(this.#subtotal * (1 + this.#taxRate));
}
// 将来、内部実装を差し替えても呼び出し側は壊れにくい
updateTaxPolicy(nextRate) {
if (nextRate < 0 || nextRate > 1) throw new Error('invalid tax rate');
this.#taxRate = nextRate;
}
}
const billing = new BillingService();
const total = billing.calculateTotal(1000);
console.log(total);
// 外部は private へ直接アクセスできない。
// AIにも「calculateTotal を使う」以外の選択肢を与えない。
AI視点:触ってよい入口が少ないほど、AIは迷子になりにくく、
内部への誤依存や不要な改変提案を減らせます。
5. 境界線の引き方(実務手順)
- 公開APIを先に書く:「外部が何をしたいか」だけをメソッドで定義する。
- 内部状態を隠す:フィールド・補助メソッドは原則 private にする。
- 状態遷移を閉じ込める:順番依存のロジックを外に漏らさない。
- AI依頼時はAPI契約だけ渡す:内部実装の全文を毎回渡さない。
- レビューで境界違反を止める:外部コードが内部へ触れていないかを最優先で確認。
ポイントは、「変更されやすいものほど隠す」「長期契約にするものだけ公開する」です。
6. 図解:依存線がどこに刺さっているか
悪い設計は依存線が内部へ直刺し、良い設計は公開APIだけに刺さります。
悪い依存(内部へ直刺し)
公開層: public API
呼び出し側 → 内部実装へ直接アクセス
内部層: taxRate / workflowStep / cache
良い依存(公開APIのみ)
公開層: calculateTotal()
呼び出し側 → 公開APIだけを利用
内部層: 計算手順 / 状態遷移 / 最適化ロジック
7. AI生成コードのレビュー観点(短尺)
NG差分の典型
- orderService._cache[userId] = data
- paymentGateway.internalRetryCount = 0
- cart.total = recalc(cart.items) // 直接代入
+ // 境界違反: private相当の内部へ依存している
OK差分の典型
+ orderService.refreshUserOrder(userId)
+ paymentGateway.resetRetryPolicy()
+ cart.recalculateTotal()
+ // 公開API経由だけで要件を満たしている
AIワンポイント
AIには「使っていい入口」だけを見せるのがコツです。
公開APIが少ないほど、AIは実装候補を誤らず、レビューもしやすくなります。
- 依頼文に「公開API以外は触らない」と明記する。
- レビューでは「内部状態への直接アクセス」が混ざっていないかを最初に確認する。
まとめ:カプセル化はAI時代の設計ガードレール
- カプセル化は、AIに対する「立ち入り禁止サイン」をコードで表現する設計です。
- インターフェースだけを公開すると、AIの誤依存・誤提案・手戻りを減らせます。
- 人間が境界を設計し、AIが中身を実装する分担にすると、実装差し替え耐性が上がります。
要するに、AI時代の設計力は「どれだけ上手くカプセルを作れるか」で決まります。
おまけ:コピペで使えるチェックリスト
- 公開APIは「外部ユースケース」を1文で説明できる単位まで絞れている。
- フィールドや補助メソッドが不用意に public になっていない。
- 外部コードが内部状態(_xxx / #xxx 相当)へ直接アクセスしていない。
- 状態遷移の順序や検証ロジックがクラス外へ漏れていない。
- AIへの依頼文に「触ってよい境界」と「触ってはいけない境界」を書いている。
- 内部実装を差し替えても公開API契約が維持される設計になっている。