変更要約: 初版
3.3デザインパターン
GoFデザインパターンの3分類(生成に関するパターン/構造に関するパターン/振る舞いに関するパターン)の代表例(Singleton/Factory Method/Adapter/Facade/Observer/Strategy)の意図を正確に理解し、目の前の設計課題にどのパターンを適用すべきかを判断する力を学びます。
デザインパターンは車輪の再発明を避けるための「名前の付いた定石」です。しかしシステムアーキテクトに求められるのはパターン名の暗記ではなく、目の前の設計課題(何を可変にしたいか、何を固定したいか)に対してどのパターンの意図が合致するかを判断することです。パターン名だけを流用し意図を無視すると、かえって不必要な複雑性を持ち込みます。
3.3.1生成に関するパターン
- Singleton=あるクラスのインスタンスがアプリケーション全体で唯一であることを保証し、そのインスタンスへのグローバルなアクセス経路を提供するパターン。設定情報の一元管理やログ出力先の共有など「同時に複数存在すると矛盾が生じる資源」に適用する。乱用するとテスト困難な隠れた大域状態を生み、事実上の共通結合になる副作用に注意。
- Factory Method=インスタンス生成の具体的なクラス選定をサブクラス(またはファクトリオブジェクト)に委ね、呼び出し側は生成されるオブジェクトの具体的な型を意識しないパターン。新しい種類の生成対象を追加してもクライアント側の生成呼び出しコードを変更せずに済み、開放閉鎖原則を実現する典型手段。
3.3.2構造に関するパターン
- Adapter=インタフェースに互換性のない既存クラス(多くは変更できない外部ライブラリ)を、呼び出し側が期待するインタフェースに変換して橋渡しするパターン。「動作は正しいが呼び出し方が合わない」既存資産をそのまま活かしたいときに使う。
- Facade=複数のサブシステム(多数のクラス群)への複雑な呼び出し手順を、単純化された単一の窓口インタフェースにまとめて隠蔽するパターン。呼び出し側がサブシステム内部の詳細を知らなくても使えるようにし、利用側とサブシステム間の結合度を下げる。
3.3.3振る舞いに関するパターン
- Observer=ある対象(Subject)の状態変化を、登録された複数の監視者(Observer)へ自動的に通知する一対多の依存関係を定義するパターン。通知元は監視者の具体的な数や型を知る必要がなく、新しい監視者を追加しても通知元のコードを変更せずに済む。
- Strategy=アルゴリズム(振る舞い)を実行時に切り替え可能なオブジェクトとしてカプセル化し、それを利用するクラスから分離するパターン。if-elseで振る舞いを分岐させる代わりに、多態を用いて振る舞いをまるごと差し替え可能にする。
「Adapterは既存の互換性のないインタフェースを変換する橋渡し」「Facadeは複数サブシステムへの複雑な呼び出しを単一窓口に単純化」「Observerは状態変化を複数の監視者へ自動通知」「Strategyはアルゴリズムを差し替え可能なオブジェクトとしてカプセル化」が最頻出です。AdapterとFacadeを混同しない(Adapterは"インタフェース変換"、Facadeは"複雑さの隠蔽・単純化"が核)ことが急所です。
あるシステムアーキテクトが、ECサイトの在庫更新機能を設計しているとします。要件は「在庫数が変化したら、
①検索インデックスの更新、
②レコメンドエンジンへの通知、
③在庫アラートメールの送信、という3つの処理を行う。今後さらに監視対象の処理が増える見込みがある」というものです。ここで在庫管理クラスInventoryManagerのupdateStock()メソッド内に3つの処理を直接if文なしで列挙するコードを書いてしまうと、新しい監視処理(例:在庫予測AIへの通知)を追加するたびにInventoryManager自体を書き換える必要が生じ、開放閉鎖原則に反します。ここで適切なのはObserverパターンの適用です。InventoryManagerをSubjectとし、「検索インデックス更新」「レコメンド通知」「アラートメール送信」をそれぞれObserverとして登録すれば、InventoryManagerは「在庫が変化した」という通知を発行するだけで済み、新しい監視処理の追加はObserverを1つ登録するだけで完結し、InventoryManagerのコードは変更不要になります。一方、この在庫アラートメール送信処理の内部では、SMTP接続の確立・認証・MIMEエンコーディングなど複数の低レベルなライブラリ呼び出しが煩雑に絡み合っているとします。この煩雑さをAlertMailerという単一クラスのsend()メソッドの背後に隠蔽すれば、呼び出し側はAlertMailer.send(...)という単純な窓口だけを意識すればよくなります。これはFacadeパターンの適用です(複雑なサブシステム群への呼び出しを単一窓口へ単純化)。さらに、レコメンドエンジンへの通知方式が「即時Web APIコール」と「バッチ用メッセージキューへの投入」の2通りあり、将来この通知方式自体を運用中に切り替えたいという要件が出てきたら、通知アルゴリズムをNotificationStrategyインタフェースとして切り出し、ImmediateApiStrategy・QueueBatchStrategyという2つの実装を用意するStrategyパターンが適切です。「何が可変で何を固定したいか」を見極め、通知の相手が増減する(Observer)・複雑な呼び出しを隠したい(Facade)・アルゴリズムそのものを切り替えたい(Strategy)のどれに該当するかを判断してパターンを選ぶのがシステムアーキテクトの役割です。
| パターン | 分類 | 意図 |
|---|---|---|
| Singleton | 生成 | インスタンスをアプリ全体で唯一に保証する |
| Factory Method | 生成 | 生成する具象クラスの選定をサブクラス/ファクトリに委ねる |
| Adapter | 構造 | 互換性のないインタフェースを変換して橋渡しする |
| Facade | 構造 | 複数サブシステムへの複雑な呼び出しを単一窓口へ単純化する |
| Observer | 振る舞い | 状態変化を登録された複数の監視者へ自動通知する |
| Strategy | 振る舞い | アルゴリズムを差し替え可能なオブジェクトとしてカプセル化する |
ひっかけ: 「AdapterとFacadeはどちらも複雑な処理を単純化するので同じ意図のパターンである」は誤りです——Adapterの核心は"互換性のないインタフェースの変換"であり、Facadeの核心は"複数サブシステムへの複雑な呼び出しの単純化・隠蔽"で意図が異なります。また「Singletonは常に安全に使ってよい」も誤り=乱用は隠れた大域状態を生みテストや並行処理での不具合の温床になるため適用は慎重に判断すべきです。
3.3.4この節のまとめ
- 通知の相手(監視者)が増減する課題にはObserver、複雑な呼び出しを単一窓口に隠したい課題にはFacade、互換性のないインタフェースを橋渡ししたい課題にはAdapter
- アルゴリズムそのものを実行時に切り替えたい課題にはStrategy、生成する具象クラスの選定を委譲し開放閉鎖原則を実現したい課題にはFactory Method
- Singletonは唯一性の保証に有効だが、乱用は隠れた大域状態(実質的な共通結合)を生むため適用は慎重に判断する
進捗の記録にはログインが必要です。
理解度チェック
(軽い確認用)Q1. ECサイトで在庫変化時に「検索インデックス更新」「レコメンド通知」「アラートメール送信」の3処理を行い、今後も監視対象の処理が増える見込みがある。InventoryManagerのコードを変更せず新しい監視処理を追加できるようにするために最も適切なパターンはどれか。
Q2. アラートメール送信処理の内部でSMTP接続確立・認証・MIMEエンコーディングなど複数の低レベルなライブラリ呼び出しが煩雑に絡み合っている。この複雑さを単一クラスのsend()メソッドの背後に隠蔽し、呼び出し側の結合度を下げたい。最も適切なパターンはどれか。
Q3. レコメンドエンジンへの通知方式が「即時Web APIコール」と「バッチ用メッセージキュー投入」の2通りあり、運用中にこの通知方式自体を切り替えたいという要件が出てきた。最も適切なパターンはどれか。

