@nqounetです。
「Mooで作るゴーストギャラリー・ビューワ」シリーズの最終回(第5回)です。前回はLogging Proxyを使って監査ログを実装しました。
今回は「遠隔アーカイブへ引っ越し」というテーマです。呪いの絵をローカルから外部のアーカイブサーバーに移動し、ネットワーク越しにアクセスできるようにします。
今回のゴール
画像データを外部のアーカイブサーバーに移動し、Remote Proxy(リモートプロキシ)を使ってネットワーク越しのアクセスを透過的に扱います。呼び出し側のコードは変更せずに、ローカルからリモートへの移行を実現します。
新しい要件
ギャラリーの運用に変更がありました。
- サーバーのストレージが逼迫している
- 高解像度の呪いの絵を外部のアーカイブサーバーに移動する
- ギャラリーの既存コードは変更したくない
直接HTTPアクセスパターン(破綻例)
まず、外部サーバーへの直接アクセスを実装してみます。
| |
破綻のポイント
- HTTPリクエストのコードがギャラリークラスに混入している(また混入パターン!)
- エラーハンドリングが複雑になる(ネットワークは信用できない)
- テスト時にHTTPをモックするのが困難(統合テストでしか確認できない地獄)
- オフライン時の動作を別途実装する必要がある
- 既存のローカル画像用コードと互換性がない(同じインターフェースなのに!)
ここまで来ると、パターンが見えてきましたね。「何か新しい機能を追加するたびに、本来関係ないクラスを変更している」——これこそがSRP違反・OCP違反の典型です。
Remote Proxyで解決する
Remote Proxyを導入して、ネットワークアクセスの詳細を隠蔽します。
Remote Proxyの構造
flowchart LR
subgraph Local["ローカル環境"]
Gallery[ギャラリー]
Remote[RemoteProxy]
Local_Img[GhostImage]
end
subgraph Cloud["外部アーカイブサーバー"]
Archive[(画像アーカイブ)]
end
Gallery --> Remote
Gallery --> Local_Img
Remote -->|HTTP| Archive
style Remote fill:#87CEEB
style Archive fill:#DDA0DD
ローカル/リモート透過的アクセス
sequenceDiagram
participant User as ユーザー
participant Gallery as ギャラリー
participant Remote as RemoteProxy
participant Client as ArchiveClient
participant Server as アーカイブサーバー
User->>Gallery: view_image(2)
Gallery->>Remote: render_full()
alt キャッシュあり
Remote-->>Gallery: キャッシュから返却
else キャッシュなし
Remote->>Client: fetch_image()
alt オンライン
Client->>Server: HTTP GET
Server-->>Client: 画像データ
Client-->>Remote: success
Remote->>Remote: キャッシュに保存
Remote-->>Gallery: 画像データ
else オフライン
Client-->>Remote: failure
Remote-->>Gallery: エラーメッセージ
end
end
Gallery-->>User: 表示結果
| |
実行結果は以下のようになります。
| |
何が変わったか
- ギャラリークラスはHTTPの詳細を知らない
- ローカル画像とリモート画像を同じインターフェースで扱える
- キャッシュ機能でネットワークアクセスを最小化
- オフライン時もキャッシュがあれば動作する
- 既存のコードを変更せずにリモート対応を追加できた
Remote Proxyのポイント
透過的なアクセス
| |
呼び出し側は、画像がローカルにあるかリモートにあるかを意識しません。
エラーハンドリングの一元化
| |
ネットワークエラーをProxyで処理し、呼び出し側に適切なメッセージを返します。
キャッシュとの組み合わせ
| |
Remote ProxyにCaching機能を組み込むことで、ネットワーク負荷を軽減できます。
完成コード
最終的な完成コードを掲載します。
| |
シリーズのまとめ
全5回にわたって、Proxyパターンの様々なバリエーションを学んできました。
| 回 | Proxy種類 | 主な目的 |
|---|---|---|
| 第1回 | Virtual Proxy | 遅延初期化 |
| 第2回 | Protection Proxy | アクセス制御 |
| 第3回 | Caching Proxy | キャッシュによる高速化 |
| 第4回 | Logging Proxy | 監査ログの集約 |
| 第5回 | Remote Proxy | リモートアクセスの抽象化 |
Proxyパターンの本質
Proxyパターンの本質は「代理を通じて責務を分離する」ことです。
- 元のクラス(RealSubject)はシンプルに保つ
- 追加の責務(遅延初期化、認可、キャッシュ、ログ、通信)は代理に任せる
- 呼び出し側は代理と実体を区別せずに使える
Proxyのチェーン
複数のProxyを組み合わせることで、様々な機能を透過的に追加できます。
flowchart TB
Client[呼び出し側] --> Guard
subgraph ProxyChain["Proxy チェーン"]
Guard[GuardProxy<br/>認可]
Cache[CacheProxy<br/>キャッシュ]
Audit[AuditProxy<br/>ログ]
Remote[RemoteProxy<br/>通信]
Guard --> Cache
Cache --> Audit
Audit --> Remote
end
Remote --> Real[GhostImage<br/>実体]
style Guard fill:#FFB6C1
style Cache fill:#90EE90
style Audit fill:#87CEEB
style Remote fill:#DDA0DD
style Real fill:#FFD700
この設計は、開放閉鎖の原則(OCP)に従っています。既存のコードを変更せずに、新しい機能を追加できます。
参考リンク
シリーズ全体の目次
シリーズ全体の目次は以下をご覧ください。
