Javaを鍛える|デザインパターン全23回 #4

Abstract Factory:通知チャネル一式の差し替え

  • Java
  • デザインパターン
  • Abstract Factory
  • Javaを鍛える

📋 この記事の目次
  1. Abstract Factoryは何のためにあるか
  2. Before:部品を個別に選ぶため、組み合わせの整合性を保証できない
  3. After:関連オブジェクトをまとめて作るFactoryを用意する
  4. こんな場面で使う/使わない
  5. まとめ
  6. 🎓 理解度テスト

TicketCraftには、チケットの概要を伝えるSummaryNotifierと、緊急事態を知らせるAlertNotifierがあります。それぞれ「通常用」(Email/Slack)と「緊急用」(SMS/ポケベル)の実装が用意されていますが、呼び出し側で個別に選んでいるため、こんな事故が起きました。

public class Main {
    public static void main(String[] args) {
        // 緊急対応のつもりが、通常用のSummaryNotifierと緊急用のAlertNotifierを
        // 別々に選んでしまい、組み合わせがちぐはぐになってしまった
        SummaryNotifier summary = new EmailSummaryNotifier();
        AlertNotifier alert = new PagerAlertNotifier();

        summary.sendSummary("本番サーバーがダウンしています");
        alert.sendAlert("本番サーバーがダウンしています");
    }
}

実行結果:

[Email通常通知] 本番サーバーがダウンしています
[ポケベル緊急アラート] 本番サーバーがダウンしています

緊急事態なのに、概要通知だけ気づかれにくい通常のEmailのままになってしまっています。「緊急用の組み合わせで揃える」というルールが、コード上のどこにも表現されていないことが原因です。

Abstract Factoryは何のためにあるか

Abstract Factoryは、「関連する複数のオブジェクトを、matchした組み合わせ(ファミリー)として、まとめて作る」 パターンです。家具を「北欧風のシリーズ」「モダン風のシリーズ」のようにセットで購入すれば、椅子とテーブルのデザインが食い違う心配がありません。Abstract Factoryは、この「セットで揃える」発想をコードで保証します。

Before:部品を個別に選ぶため、組み合わせの整合性を保証できない

先ほどのコードのように、SummaryNotifierAlertNotifierをそれぞれ個別にnewしていると、「通常用」と「緊急用」を正しい組み合わせで選ぶ責任がすべて呼び出し側に委ねられてしまいます。呼び出し側が増えるほど、組み合わせ間違いのリスクも増えていきます。

After:関連オブジェクトをまとめて作るFactoryを用意する

public interface NotificationFactory {
    SummaryNotifier createSummaryNotifier();
    AlertNotifier createAlertNotifier();
}
public class StandardNotificationFactory implements NotificationFactory {
    @Override
    public SummaryNotifier createSummaryNotifier() {
        return new EmailSummaryNotifier();
    }

    @Override
    public AlertNotifier createAlertNotifier() {
        return new SlackAlertNotifier();
    }
}
public class UrgentNotificationFactory implements NotificationFactory {
    @Override
    public SummaryNotifier createSummaryNotifier() {
        return new SmsSummaryNotifier();
    }

    @Override
    public AlertNotifier createAlertNotifier() {
        return new PagerAlertNotifier();
    }
}
public class Main {
    public static void main(String[] args) {
        NotificationFactory factory = new UrgentNotificationFactory();
        SummaryNotifier summary = factory.createSummaryNotifier();
        AlertNotifier alert = factory.createAlertNotifier();

        summary.sendSummary("本番サーバーがダウンしています");
        alert.sendAlert("本番サーバーがダウンしています");
    }
}

実行結果:

[SMS緊急通知] 本番サーバーがダウンしています
[ポケベル緊急アラート] 本番サーバーがダウンしています

呼び出し側はUrgentNotificationFactoryを1つ選ぶだけで、SummaryNotifierAlertNotifierが自動的に「緊急用」で揃います。StandardNotificationFactoryに切り替えれば、今度は両方とも「通常用」に揃います。組み合わせを間違えようがない状態になりました。

🧓 ベテランの現場コラム Abstract Factoryは、Factory Methodと似ていますが、「1つのオブジェクトを作る」か「複数の関連するオブジェクトをセットで作る」かという点で目的が異なります。「この2つ(またはそれ以上)は常に組み合わせで使われるべきだ」というルールがコード上に見当たらないとき、Abstract Factoryの出番かもしれません。

こんな場面で使う/使わない

  • 使う: 複数の関連オブジェクトが、常にセットとして一貫性を保つ必要がある場合
  • 使わない: オブジェクト同士に組み合わせの制約がなく、自由に混ぜて使ってよい場合。無理に揃える仕組みを作ると、かえって柔軟性を失う

まとめ

  • Abstract Factoryは「関連する複数のオブジェクトを、matchした組み合わせでまとめて作る」パターン
  • 個別にnewしていると、呼び出し側の責任で組み合わせ間違いが起きるリスクが残る
  • 「常にセットで使われるべきもの」があるときに検討する

🎓 理解度テスト

学んだ内容を確認しましょう。全3問。