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

Facade:「作成+通知+履歴記録」の簡易窓口

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

📋 この記事の目次
  1. Facadeは何のためにあるか
  2. Before:呼び出し側が3つのサブシステムを個別に操作する
  3. After:Facadeで3つの呼び出しを1つにまとめる
  4. こんな場面で使う/使わない
  5. まとめ
  6. 🎓 理解度テスト

TicketCraftでチケットを1件登録するとき、実は裏側で「データの保存」「担当者への通知」「監査ログへの記録」という3つの処理が必要です。今は、呼び出し側がこの3つを個別に呼び出しています。

public class TicketRepository {
    public void save(String title) {
        System.out.println("[Repository] 保存: " + title);
    }
}

public class NotificationService {
    public void notify(String title) {
        System.out.println("[Notification] 通知: " + title);
    }
}

public class AuditLog {
    public void record(String title) {
        System.out.println("[AuditLog] 記録: " + title);
    }
}

Webフォーム、バッチ取り込み、社外API連携など、チケットを登録する場所は複数あります。それぞれの場所で、この3つを正しい順番で呼び忘れなく実行する責任を負っています。

Facadeは何のためにあるか

Facadeは、「複数の複雑なサブシステムをまとめて呼び出す、シンプルな窓口を1つ用意する」 パターンです。役所の手続きで、複数の課をたらい回しにされるのではなく、「総合窓口」に行けば必要な手続きをまとめて代行してもらえるようなものです。

Before:呼び出し側が3つのサブシステムを個別に操作する

public class Main {
    public static void main(String[] args) {
        TicketRepository repository = new TicketRepository();
        NotificationService notificationService = new NotificationService();
        AuditLog auditLog = new AuditLog();

        String title = "ログイン画面が真っ白になる";
        // 3つのサブシステムを、呼び出し側が正しい順番で自分で呼ぶ必要がある
        repository.save(title);
        notificationService.notify(title);
        auditLog.record(title);
    }
}

実行結果:

[Repository] 保存: ログイン画面が真っ白になる
[Notification] 通知: ログイン画面が真っ白になる
[AuditLog] 記録: ログイン画面が真っ白になる

新しくチケット登録の呼び出し元(例えばモバイルアプリ向けAPI)を追加するときも、この3行をそのままコピーする必要があります。順番を間違えたり、うっかり1つ呼び忘れたりするリスクも呼び出し元の数だけ増えていきます。

After:Facadeで3つの呼び出しを1つにまとめる

public class TicketRegistrationFacade {
    private final TicketRepository repository = new TicketRepository();
    private final NotificationService notificationService = new NotificationService();
    private final AuditLog auditLog = new AuditLog();

    public void registerTicket(String title) {
        repository.save(title);
        notificationService.notify(title);
        auditLog.record(title);
    }
}
public class Main {
    public static void main(String[] args) {
        TicketRegistrationFacade facade = new TicketRegistrationFacade();
        facade.registerTicket("ログイン画面が真っ白になる");
    }
}

実行結果:

[Repository] 保存: ログイン画面が真っ白になる
[Notification] 通知: ログイン画面が真っ白になる
[AuditLog] 記録: ログイン画面が真っ白になる

呼び出し側はfacade.registerTicket(title)と1行呼ぶだけになりました。3つのサブシステムをどの順番で呼ぶべきかという知識はTicketRegistrationFacadeの中に集約され、新しい呼び出し元を追加するときも呼び忘れの心配がありません。

🧓 ベテランの現場コラム Facadeは「複雑さを隠す」ことが目的で、サブシステム自体を置き換えるものではありません。TicketRepositoryNotificationServiceを直接使いたい特殊な場面(例えば、通知だけ止めて保存だけしたいバッチ処理)では、Facadeを経由せず個別に呼び出すことも引き続き可能です。「普段はFacade経由、特殊な場合は個別に」という併用を許容する設計にしておくと柔軟性を保てます。

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

  • 使う: 複数のサブシステムを、決まった順番・組み合わせで呼び出す場面が複数箇所にある場合
  • 使わない: サブシステムが1つしかない、またはそれぞれ独立していて組み合わせる必要がない場合

まとめ

  • Facadeは「複数の複雑なサブシステムをまとめて呼び出す、シンプルな窓口」を提供するパターン
  • 呼び出し側の呼び忘れ・順序間違いのリスクを1箇所に集約できる
  • サブシステムへの個別アクセスを完全に禁止するものではなく、あくまで「簡単な経路」を提供するもの

🎓 理解度テスト

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