責任とは何か?

「責任」の定義

ソフトウェア設計における「責任」とは、あるモジュールが果たすべき役割や機能のことです。より正確には、「そのモジュールを変更する理由」と定義できます。

責任を識別する観点

  • 何をするモジュールか(機能)
  • 誰のためのモジュールか(アクター)
  • いつ変更が必要になるか(変更理由)

責任が混在したコードの問題

❌ 責任が混在している例
class OrderService {
    calculateTotal(items) {
        let total = 0;
        for (const item of items) {
            total += item.price * item.quantity;
        }
        
        // 税金計算(ビジネスロジック)
        const tax = total * 0.1;
        const finalTotal = total + tax;
        
        // データベース保存(永続化)
        database.save('orders', { items, total: finalTotal });
        
        // メール送信(通知)
        emailService.send(customerEmail, 
            `注文合計: ${finalTotal}円`);
        
        // ログ記録(監査)
        logger.info(`Order created: ${finalTotal}`);
        
        return finalTotal;
    }
}
✅ 責任が分離された例
// 計算の責任
class OrderCalculator {
    calculateTotal(items) {
        const subtotal = items.reduce(
            (sum, item) => sum + item.price * item.quantity, 0
        );
        const tax = subtotal * 0.1;
        return { subtotal, tax, total: subtotal + tax };
    }
}

// 永続化の責任
class OrderRepository {
    save(orderData) {
        return database.save('orders', orderData);
    }
}

// 通知の責任
class OrderNotifier {
    notifyCustomer(email, orderTotal) {
        emailService.send(email, `注文合計: ${orderTotal}円`);
    }
}

責任混在がもたらす問題

  • 理解の困難さ:一つのクラスを理解するために、複数の概念を同時に把握する必要がある
  • テストの複雑化:データベースやメールサービスをモック化しないと計算ロジックのテストができない
  • 変更の波及:税率が変わったときに、なぜかメール送信部分も再テストが必要になる
  • 再利用の困難:計算ロジックだけを別の場所で使いたくても、不要な依存を引きずる

凝集度と結合度

高凝集・疎結合の原則

良い設計の基本は「高凝集・疎結合」です。

凝集度(Cohesion)

  • モジュール内の要素がどれだけ関連しているか
  • 高凝集 = 関連する機能が一つのモジュールにまとまっている
  • 低凝集 = 無関係な機能が一つのモジュールに混在している

結合度(Coupling)

  • モジュール間の依存関係の強さ
  • 疎結合 = モジュール間の依存が少ない、または抽象的
  • 密結合 = モジュール間が具体的な実装に強く依存している

責任分離の歴史と進化

設計思想の発展

責任分離の考え方は、ソフトウェア工学の発展とともに洗練されてきました。

  • 1970年代:構造化プログラミング - 機能の分割
  • 1980年代:オブジェクト指向 - データと振る舞いのカプセル化
  • 1990年代:デザインパターン - 再利用可能な設計の型
  • 2000年代:SOLID原則 - オブジェクト指向設計の指針
  • 2010年代〜:マイクロサービス - システムレベルの責任分離