0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【リファクタリング】第24回 機能嫉妬(Feature Envy)

Posted at

はじめに

機能嫉妬(Feature Envy) とは、
あるクラスのメソッドが「自分のクラスのフィールド」ではなく、
他のクラスのデータばかり利用して処理している状態 を指します。

例:

  • OrderServiceOrder の内部データを直接操作して計算している
  • UserServiceUser のフィールドを読み取って、バリデーションや判定をしている

→ 本来は 利用しているクラス側に処理を移すべき であり、カプセル化違反のサインです。


24.1 特徴

  • あるメソッドが 他のクラスの getter を大量に呼び出す
  • 自分のフィールドをほとんど使わない
  • ビジネスロジックが ドメインオブジェクトではなくサービス層に偏在
  • 「このメソッドは別の場所にあるべきでは?」と感じる

24.2 解決手法

  • メソッド移動(Move Method)
    → 他クラスのデータに依存しているメソッドを、そのクラスに移動
  • データと振る舞いの統合
    → データを持つクラスに、そのデータを扱うロジックも持たせる
  • ドメインモデル化
    → サービスに偏ったロジックをエンティティや値オブジェクトに移す

24.3 Kotlin 例

Before:機能嫉妬のあるコード

data class Order(val items: List<Double>, val customerLevel: Int)

class OrderService {
    fun calculateTotal(order: Order): Double {
        val subtotal = order.items.sum()
        return if (order.customerLevel > 3) subtotal * 0.9 else subtotal
    }
}
  • calculateTotalOrder のデータばかり使っており、OrderService 自身の状態は不要
  • このロジックは Order にあるべき

After:メソッドを移動

data class Order(val items: List<Double>, val customerLevel: Int) {
    fun calculateTotal(): Double {
        val subtotal = items.sum()
        return if (customerLevel > 3) subtotal * 0.9 else subtotal
    }
}

Order が自分のデータを自分で扱うようになり、カプセル化が強化された。


After②:値オブジェクト化でさらに明確化

@JvmInline
value class Money(val value: Double) {
    init { require(value >= 0) { "金額は正数でなければならない" } }
    operator fun plus(other: Money) = Money(this.value + other.value)
    operator fun times(rate: Double) = Money(this.value * rate)
}

data class Order(val items: List<Money>, val customerLevel: Int) {
    fun calculateTotal(): Money {
        val subtotal = items.reduce { acc, m -> acc + m }
        return if (customerLevel > 3) subtotal * 0.9 else subtotal
    }
}

→ 「データ(Money)」と「振る舞い(演算)」を統合。
OrderService にあった処理は不要になった。


24.4 実務での指針

  • getter の連発は Feature Envy のサイン
  • ロジックは 最も関連の深いクラス に配置する
  • サービスにロジックを集めすぎると「貧血モデル」になりやすい
  • Kotlin では data class + value class を組み合わせ、データと振る舞いを一体化できる

まとめ

  • Feature Envy は「他のクラスのデータばかり欲しがるメソッド」
  • 解決策は Move Method / 値オブジェクト化 / ドメインモデル化
  • 基本思想データと振る舞いは一緒にあるべき

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?