1. 概要(Overview)
Extract Superclass は、複数のクラスに重複しているフィールド/メソッド を共通の親クラスへ切り出し、
継承関係を導入して 重複を排除 するリファクタリングです。
共通化により、コードの重複(Duplication)を減らし、一貫性と保守性 を高めます。
目的:
- 重複ロジックを一箇所に集約(DRY)
- 共通インタフェースを提供してクライアントを簡潔化
- 将来の機能追加・不具合修正を容易にする
2. 適用シーン(When to Use)
- 2つ以上のクラスに 同一/酷似のフィールドやメソッド が存在する
- 「コピペ改変」が増え、変更が複数箇所に波及 している
- 共通の抽象(概念)が見えているが、まだ明示されていない
よくある匂い:
- Duplicate Code(重複コード)
- Divergent Change(分岐的変更)
- Shotgun Surgery(散弾銃のような修正)
3. 手順(Mechanics / Steps)
- 重複しているメンバ(フィールド/メソッド)を洗い出す
- 新しいスーパークラスを作成
- 共通メンバをスーパークラスへ移動(必要なら抽象メソッド化)
- 既存クラスをスーパークラスのサブクラスにする
- クライアントコードを親型(スーパークラス)に寄せられる部分は寄せる
- テスト実行・回帰確認
4. Kotlin 例(Before → After)
Before:2つのクラスに重複ロジック
class OnlineOrder(
val id: String,
val items: List<Double>,
val shippingFee: Double
) {
fun subtotal(): Double = items.sum()
fun total(): Double = subtotal() + shippingFee
fun description(): String = "OnlineOrder #$id"
}
class StorePickupOrder(
val id: String,
val items: List<Double>,
val pickupStore: String
) {
fun subtotal(): Double = items.sum()
fun total(): Double = subtotal() // 受取なので送料なし
fun description(): String = "StorePickupOrder #$id@$pickupStore"
}
-
subtotal()が重複 -
total()も一部重複しつつ分岐 -
idとitemsも共通の状態
After:スーパークラスを抽出
open class Order(
val id: String,
val items: List<Double>
) {
fun subtotal(): Double = items.sum()
open fun total(): Double = subtotal()
open fun description(): String = "Order #$id"
}
class OnlineOrder(
id: String,
items: List<Double>,
private val shippingFee: Double
) : Order(id, items) {
override fun total(): Double = subtotal() + shippingFee
override fun description(): String = "OnlineOrder #$id"
}
class StorePickupOrder(
id: String,
items: List<Double>,
private val pickupStore: String
) : Order(id, items) {
// total() は親のデフォルト(= subtotal)のままでOK
override fun description(): String = "StorePickupOrder #$id@$pickupStore"
}
- 共通の状態(
id,items)とsubtotal()をOrderに集約 -
total()はデフォルト実装を提供し、必要なサブクラスだけ上書き -
description()も共通の枠を持ちつつ、サブクラスで差分化
5. 効果(Benefits)
- 重複コードの削減 → 修正点が一箇所に
- 共通の抽象を共有 → 型を揃えやすく、APIがスリムに
- テスト容易性の向上(共通部は一度のテストで担保可能)
6. 注意点(Pitfalls)
-
継承の乱用 に注意:共通化のためだけの不自然な継承は逆効果
- ふるまいの共通化だけが目的なら Extract Interface も検討
- 状態の継承が不要・関係が弱いなら Composition(委譲) を優先
- スーパークラスの責務が増えすぎると再び Large Class 化しやすい
- サブクラスが親の設計に 過度に縛られる リスク(将来の変更に硬い)
7. 実務Tips
- まずは 最小公倍数の共通点 だけを親へ。早すぎる抽象化は禁物
- オーバーライド増殖の兆候が出たら、設計を見直して Strategy/Composition へ
- 既存呼び出し側は段階的に親型(
Orderなど)へ寄せていく(安全なインターフェース収斂)
まとめ
- Extract Superclass は、複数クラスの重複を共通の親へ集約するリファクタリング
- 判断基準:状態とふるまいの“本当に”自然な共通性があるか?
- 基本思想:DRY と整理された抽象。無理な継承は避け、場合により Interface / Composition を選ぶ