1. 概要(Overview)
Replace Subclass with Fields は、サブクラスの違いが 単なる定数値の違い にすぎない場合、
サブクラスをやめて フィールド(プロパティ) にまとめるリファクタリングです。
目的:
- 不要に増えすぎたサブクラスを整理
- 継承をやめてシンプルに
- 可読性と保守性を向上
2. 適用シーン(When to Use)
- サブクラス間の違いが 定数値(名前、料金、種類など)だけ
- 振る舞い(メソッド)は同じで、データの違いしかない
- 新しい種類を追加するときに サブクラスを増やすのが冗長
- 継承ツリーが複雑化している
よくある匂い:
- Speculative Generality(過剰な一般化)
- Parallel Inheritance Hierarchies(並行した継承階層)
3. 手順(Mechanics / Steps)
- サブクラス間の違いが「定数値」にすぎないことを確認
- スーパークラスにフィールドを追加
- サブクラスごとに異なっていた値を、そのフィールドに割り当てる
- サブクラスを削除し、インスタンス生成時に値を設定する
- ファクトリメソッドを導入して利用側の可読性を確保
4. Kotlin 例(Before → After)
Before:サブクラスで表現
abstract class Person(val name: String)
class Male(name: String) : Person(name)
class Female(name: String) : Person(name)
fun main() {
val alice: Person = Female("Alice")
val bob: Person = Male("Bob")
println("${alice.name} is Female")
println("${bob.name} is Male")
}
- サブクラスの違いは「性別」だけ
- 振る舞いはすべて同じ → サブクラスが冗長
After:フィールドに置き換え
class Person(val name: String, val gender: Gender)
enum class Gender { MALE, FEMALE }
fun main() {
val alice = Person("Alice", Gender.FEMALE)
val bob = Person("Bob", Gender.MALE)
println("${alice.name} is ${alice.gender}")
println("${bob.name} is ${bob.gender}")
}
-
Genderフィールドで違いを表現 - サブクラス不要、シンプルで拡張しやすい
After②:ファクトリメソッドを導入
class Person private constructor(val name: String, val gender: Gender) {
companion object {
fun male(name: String) = Person(name, Gender.MALE)
fun female(name: String) = Person(name, Gender.FEMALE)
}
}
enum class Gender { MALE, FEMALE }
fun main() {
val alice = Person.female("Alice")
val bob = Person.male("Bob")
println("${alice.name} is ${alice.gender}")
}
→ 生成意図が明確 になり、可読性がさらに向上。
5. 効果(Benefits)
- サブクラス乱立を防ぎ、設計がシンプル に
- 継承階層が浅くなり、可読性・保守性が向上
- 新しい種類の追加が容易(サブクラス追加 → enum/フィールド値追加で済む)
- データと生成方法を集中管理できる
6. 注意点(Pitfalls)
- 本当に「データの違いだけ」かを確認する(振る舞いも異なるなら Replace Type Code with Subclasses の方が適切)
- enum/フィールドに寄せすぎると God Class 化 のリスクがある
- 新しい種類ごとに異なるロジックが将来追加されそうなら、Strategy/State パターン を検討
まとめ
- Replace Subclass with Fields は「違いが値だけのサブクラス」を フィールドにまとめる リファクタリング
- 判断基準:このサブクラスの違いは振る舞いか?データか?
- 基本思想:データの違いに過ぎないなら、フィールドで十分