設計原則 レッスン5

凝集度と結合度

モジュール内部のまとまりを示す凝集度と、モジュール間の依存の強さを示す結合度の関係と設計指針を学ぶ

凝集度とは

凝集度(Cohesion)は、 1つのモジュール(クラスや関数)の内部にある要素同士が、 どれだけ強く関連し合っているかを示す指標です。 凝集度が高いほど「そのモジュールが1つのまとまった役割を果たしている」と言えます。

高凝集の例

「税込価格を計算する」「割引を適用する」「送料を計算する」という、 すべて「注文金額の計算」という1つの目的に関連するメソッドを持つクラス

低凝集の例

「注文金額の計算」「メール送信」「ログイン認証」など、 互いに関連の薄い処理が1つの「Utilクラス」にまとめて詰め込まれている状態

結合度とは

結合度(Coupling)は、 モジュール同士がどれだけ強く依存し合っているかを示す指標です。 結合度が低いほど、あるモジュールの変更が他のモジュールに影響を与えにくくなります。

// 結合度が高い例: OrderServiceがMySqlOrderRepositoryの実装詳細を直接知っている
class OrderService {
  private repository = new MySqlOrderRepository()

  getOrder(id) {
    return this.repository.findByIdWithRawSql(id)
  }
}

// 結合度が低い例: OrderServiceは抽象(インターフェース)にのみ依存する
interface OrderRepository {
  findById(id): Order
}

class OrderService {
  constructor(private repository: OrderRepository) {}

  getOrder(id) {
    return this.repository.findById(id)
  }
}

ポイント

結合度を下げる代表的な手段が、前のレッスンで学んだ依存性逆転の原則(DIP)です。 抽象への依存に切り替えることで、実装の詳細を変更してもOrderServiceに影響が及びません。

凝集度の種類(強い順の目安)

凝集度には段階があり、代表的なものを強い順に紹介します。 すべてを厳密に覚える必要はありませんが、「機能的凝集」に近づけることを目指すと 設計の指針になります。

機能的凝集(強い・望ましい)

モジュール内のすべての要素が、1つの明確な役割を果たすために協調している

逐次的凝集

あるステップの出力が次のステップの入力になるような処理の集まり

論理的凝集

似た種類の処理をフラグで分岐させてまとめただけの集まり

偶発的凝集(弱い・避けたい)

特に関連のない処理が、たまたま同じモジュールに置かれているだけの集まり

高凝集・低結合を実現する設計指針

凝集度を高める

  • • 「このクラスの目的を一言で説明できるか」を問う(SRPと同じ視点)
  • • 関連の薄い処理を「Utilクラス」に寄せ集めない

結合度を下げる

  • • 具体的な実装ではなく抽象(インターフェース)に依存する(DIP)
  • • モジュール間でやり取りするデータを必要最小限にする

高凝集・低結合という2つの指標は、SOLID原則をはじめとする様々な設計原則が 目指している最終的なゴールでもあります。個別の原則に迷ったときは、 「これは凝集度を上げる方向か、結合度を下げる方向か」を基準に判断すると整理しやすくなります。

ポイント

  • • 凝集度はモジュール内部の要素同士の関連の強さ、結合度はモジュール間の依存の強さを示す
  • • 高凝集・低結合が望ましく、変更の影響範囲を狭く保つことができる
  • • 凝集度には機能的凝集(強い)から偶発的凝集(弱い)まで段階がある
  • • 結合度を下げる代表的な手段は抽象(インターフェース)への依存(DIP)
  • • SOLIDなどの設計原則は、最終的に高凝集・低結合を実現するための道具と捉えられる

確認クイズ

1 / 3

凝集度と結合度の説明として正しいものはどれか?