1. 概要(Overview)
Encapsulate Field は、クラス外から直接アクセスされているフィールドを、
getter / setter(アクセサ) を経由して操作するように変更するリファクタリングです。
目的は以下の通り:
- 不変条件やバリデーションを アクセサ内で一元管理
- 将来の仕様変更に強い 安定した API にする
- 外部からの直接変更を防ぎ、カプセル化 を強化
2. 適用シーン(When to Use)
-
public varが外部から 無制限に書き換え られている - 値の設定に 検証/副作用 を入れたい
- コレクションを 丸見えで渡してしまっている(外部から直接 add/remove 可能)
- 将来フィールドの表現を変えたいが、外部 API は保ちたい
よくある匂い:
- Mutable Data(可変データの垂れ流し)
- Inappropriate Intimacy(過度な親密さ)
3. 手順(Mechanics / Steps)
- フィールドを 非公開(private) にする、または private set に変更
-
getXxx()/setXxx()または Kotlin のカスタム getter/setter を導入 - 外部の直接アクセスを アクセサ経由 に置き換え
- コレクションは 読み取り専用ビュー を返し、変更は 明示メソッド に限定
- テストで不変条件・副作用が機能することを確認
4. Kotlin 例(Before → After)
Before:外部から直接書き換え
class Account {
var balance: Int = 0 // どこからでも書き換え可
}
After①:setter に検証を追加(カプセル化)
class Account {
var balance: Int = 0
private set // 直接は書き換え不可(クラス外)
fun deposit(amount: Int) {
require(amount > 0) { "amount must be positive" }
balance += amount
}
fun withdraw(amount: Int) {
require(amount > 0 && amount <= balance) { "invalid withdraw" }
balance -= amount
}
}
→ 外部は deposit/withdraw でのみ変更可能。
→ 不変条件をクラス内に閉じ込められる。
5. 効果(Benefits)
- 値の検証・副作用を 単一点 に集中 → 一貫性が上がる
- 外部 API を保ちながら 内部表現の変更が容易
- コレクションの 無秩序な外部変更 を防止
- テストが 仕様レベル(アクセサ単位) で組みやすい
6. 注意点(Pitfalls)
- アクセサ乱立で API が肥大化 しないように(意味のある操作にまとめる)
- パフォーマンスがクリティカルな箇所では 余計なコピーや変換 に注意
- コレクション返却は 不変ビュー/コピー のポリシーを統一
- 既存公開フィールドの移行は 段階的に(@Deprecated で誘導) 行う
まとめ
- Encapsulate Field は「直接アクセスされるフィールドを、意図のあるアクセサと操作メソッドに包む」リファクタリング
- 判断基準:この値は自由に書き換えられて良いか? 不変条件をどこで守るか?
- 基本思想:データは守り、変更は意図を通して行わせる