メインコンテンツまでスキップ

密結合

説明(どんな問題か)

どんな状態か?

  • あるクラス(例:OrderService)が他のクラス(例:EmailService, InventoryService)を直接呼び出している
  • 依存関係が強いため、他のクラスを変更するとすぐ影響を受ける
  • テスト時にモックやスタブを差し替えづらい
  • 可読性・柔軟性・テスト性が低下

なぜ問題か?

  • 処理を追加・変更するたびに元のクラスにも手を入れる必要があり、修正コストが高い
  • 「動いているから触れない」状態になり、技術的負債が蓄積
  • 複数の責務を 1 クラスが持つようになり、責務の分離に失敗

アンチパターンのコード例

class Mailer {
send(email: string, content: string) {
console.log(`送信しました: ${email}${content}`);
}
}

class UserService {
private mailer: Mailer;

constructor() {
this.mailer = new Mailer(); // ← クラス内で直接生成(密結合)
}

notifyUser(email: string) {
const content = "ようこそ!";
this.mailer.send(email, content); // ← Mailer の存在を知りすぎている
}
}

問題点:

  • UserService は Mailer に 強く依存しており、Mailer の具体的な実装にロックインされている
  • 他のメールシステム(外部 API・Mock など)への切り替えが困難
  • たとえば「ログ付き Mailer」や「SMS への切り替え」などが必要になった場合、UserService 側の修正が必要
  • Mailer の機能を共有したい場合でも、毎回 new されるため、設定の共通化・一元管理が困難

パターン別のリファクタリング

対応可能なデザインパターン例

パターン概要主な解決アプローチ
Observerイベント通知で依存先を知らずに処理を委譲通知対象を外部登録、疎結合を実現
Mediator各コンポーネントのやりとりを中央調停役が仲介相互依存を 1 対 1 に減らす
Dependency Injection (DI)依存を外部から注入し、動的に差し替え可能にテストしやすく、拡張性を確保