概要
「全部 public にしておけば便利」
この設計判断が、のちにソフトウェアの柔軟性・拡張性・安全性を破壊することは珍しくない。
カプセル化と情報隠蔽は、オブジェクト指向の中でも最も誤解されやすい概念の一つである。
単なる「private変数」の話ではなく、責務の境界を定義し、システムの破綻を防ぐための戦略的な設計原理である。
本稿ではこの2つの違い・本質・正しい使い方について、構造と事例を交えて論理的に解説する。
1. カプセル化とは何か?
「オブジェクト内部の状態や処理を、外部から直接アクセスできないように封じ込めること」
class BankAccount {
private balance: number
constructor(initial: number) {
this.balance = initial
}
deposit(amount: number) {
this.balance += amount
}
getBalance(): number {
return this.balance
}
}
-
balance
は直接触れない - すべての操作は定義されたメソッド経由で行う
→ カプセル化は「アクセス経路を制御する構造的枠組み」
2. 情報隠蔽とは何か?
「変更の必要がある情報や構造を、外部に影響を及ぼさずに改修可能にするための設計方針」
// 内部表現を隠蔽
class Temperature {
private celsius: number
constructor(celsius: number) {
this.celsius = celsius
}
getFahrenheit(): number {
return this.celsius * 1.8 + 32
}
}
→ 将来的に摂氏→華氏の変換ロジックや内部表現が変わっても、外部には影響しない。
3. なぜ "publicだらけ" が破綻を生むのか?
✅ 問題1:依存の増殖
user.balance = 100000 // 外部から直接書き換え可能
→ 他のモジュールが balance
に依存するようになると、
→ 変更=波及=壊れる
✅ 問題2:変更耐性の喪失
- publicな内部構造に直接依存されると、
- リファクタができない
- 不変条件(例:マイナス残高禁止)も崩れる
✅ 問題3:意図のない利用とバグの温床
- 外部に expose されたものは「使っていい」と解釈される
- 意図しない使われ方が仕様化 → 偶発的依存が生まれる
4. アクセスレベルは設計レベル
アクセス修飾子 | 目的 | 設計的意図 |
---|---|---|
private |
実装の詳細を隠す | 変更容易性・安全性の担保 |
protected |
派生先に拡張を許容 | 継承を通じた拡張性確保 |
public |
外部利用の契約(インターフェース) | 明示的な責務の表明 |
5. 設計判断フロー:アクセス可否は責務の境界
① そのフィールドは外部に知られるべきものか? → YES → getter/setter or インターフェース
② その処理はオブジェクトの責務か? → YES → メソッドとしてカプセル化
③ 実装の詳細は将来変更されるか? → YES → 外部非公開にすべき(private)
④ 拡張ポイントが必要か? → YES → protected か interface を通じて公開
よくある誤解と対策
❌ public にしておけば柔軟になる
→ ✅ 柔軟性ではなく、脆弱性が増えるだけ。インターフェースとして設計すべきは「本質的な振る舞い」だけ
❌ getter/setter を付ければ OK
→ ✅ それは「手続き型の皮を被った公開変数」にすぎない場合もある。状態操作の意図も設計すべき
❌ private だとテストしにくくなる
→ ✅ それはテスト設計の責任。テストのために設計を壊してはいけない
結語
カプセル化と情報隠蔽は、
**「アクセスの遮断」ではなく、「設計責務の明示」**である。
- 何を外に出し
- 何を内に秘め
- 何を契約とし
- 何を変更可能に保つのか
この設計的判断が、拡張可能で壊れにくいソフトウェアを支える。
オブジェクト指向における隠蔽とは、
“実装の詳細を守ることではなく、設計の自由を未来に残すための知的な封印である。”