1. 概要(Overview)
Push Down Field は、スーパークラスに定義されているフィールドを、
実際に必要なサブクラスへ移動(押し下げ) するリファクタリングです。
スーパークラスは共通の抽象を表すべきですが、現実には 一部のサブクラスだけが使う状態(フィールド) が混入しがちです。
その場合、当該フィールドを必要なサブクラスに移すことで、階層の責務が明確になります。
目的は以下の通り:
- スーパークラスをより抽象的・汎用的に保つ
- サブクラス固有の状態をサブクラス側に隔離する
- 継承階層の可読性・保守性を向上させる
2. 適用シーン(When to Use)
- スーパークラスのフィールドが 一部のサブクラスでしか参照されていない
- フィールドが特定サブクラスのふるまい・制約に強く依存している
- 「共通の状態」を装っているが、実は 汎用性がない(=抽象の侵食)
よくある匂い:
- Refused Bequest(不要な継承)
- Large Class(巨大クラス)
-
Intimacy between classes(不適切な親密さ):不要な
protected共有など
3. 手順(Mechanics / Steps)
- 対象フィールドを特定(実際にどのクラスが使っているかリファレンスを確認)
- フィールドを 必要なサブクラス に移動
- それに伴う アクセサ/依存メソッド を合わせて押し下げ(必要なら Push Down Method も併用)
- スーパークラスからフィールドを削除
- テストで動作確認(生成・参照・シリアライズなどもチェック)
4. Kotlin 例(Before → After)
Before:共通クラスにサブクラス専用フィールドがある
open class Employee(
val name: String
) {
// 実は営業にしか使われない
protected var salesRegion: String? = null
}
class Engineer(name: String) : Employee(name)
class Salesman(name: String) : Employee(name) {
fun assignRegion(region: String) {
this.salesRegion = region
}
fun canVisit(region: String): Boolean {
return salesRegion == region
}
}
-
salesRegionはSalesmanだけが使うのにEmployeeに存在 -
Engineerにとって不要な状態が見えてしまう(責務の混在)
After:必要なサブクラスへフィールドを「押し下げ」
open class Employee(
val name: String
)
class Engineer(name: String) : Employee(name)
// 営業にだけ必要な状態はサブクラスに閉じ込める
class Salesman(name: String) : Employee(name) {
private var salesRegion: String? = null
fun assignRegion(region: String) {
this.salesRegion = region
}
fun canVisit(region: String): Boolean {
return salesRegion == region
}
}
-
salesRegionをSalesmanへ移動 -
Engineerから不要な状態が消え、Employeeはより抽象的に - アクセス修飾子も
privateにでき、カプセル化が向上
5. 効果(Benefits)
- スーパークラスから サブクラス固有の状態 が排除され、抽象がクリアに
- 不要な
protected共有が減り、カプセル化が強化 - 継承階層の意図が読みやすくなり、保守性とテスト容易性 が向上
6. 注意点(Pitfalls)
- 複数サブクラスが同じフィールドを必要とするなら、押し下げではなく Pull Up Field で共通化を検討
- 押し下げに伴い、関連メソッドやバリデーションも移動が必要(Push Down Method 併用)
- そのフィールドを使う外部コードがスーパークラス型を前提にしている場合、参照箇所の修正 が発生
- 継承自体が過剰な場合は、委譲(Composition) への設計転換も検討
まとめ
- Push Down Field は「スーパークラスの不要なフィールドを、必要なサブクラスへ移す」リファクタリング
- 判断基準:その状態は本当に全サブクラスに共通か? それとも特定のサブクラスだけの事情か?
- 基本思想:抽象は軽く、特化は局所化。状態は必要な場所で閉じ込める