@nqounetです。
「Mooで作るゴーストギャラリー・ビューワ」シリーズの第2回です。前回はVirtual Proxyを使って画像の遅延初期化を実装しました。
今回は「呪いの絵は誰でも見ていいのか?」という問いに答えます。一部の呪いの絵は、見た人に不幸をもたらすかもしれません。そこで、アクセス制御を導入します。
今回のゴール
ギャラリーに「鍵付き」の呪いの絵を追加します。鍵付きの絵は、適切な権限を持つユーザーだけが閲覧できるようにします。Protection Proxy(アクセス制御プロキシ)を使って、権限チェックを実装します。
前回までの状態
前回作成したコードでは、Virtual Proxyを使って遅延初期化を実装しました。今回は、このコードに「権限」の概念を追加していきます。
新しい要件
ギャラリーのオーナーから新しい要求が来ました。
- 一部の絵には「鍵」がかかっている
- 鍵付きの絵は「管理者」または「VIP会員」しか見られない
- 一般ユーザーが鍵付きの絵を見ようとしたらブロックする
最初は、既存のコードに直接権限チェックを追加しようと思いました。
直接追加する方法(破綻パターン)
| |
出力は以下です。
| |
一見動いていますが、この設計には大きな問題があります。動けばいいってものじゃない。
破綻のポイント
- 画像クラス(
GhostImage)にアクセス制御の責務が混入している(まずい) renderメソッドの引数として$userを渡す必要がある(インターフェースが変わると既存コード全滅)- 権限のロジックが複雑になると、画像クラスが肥大化する(太ったクラスは悪)
- 今後、ロギングやキャッシュなど別の機能を追加するたびに、同じクラスが変更される(閉じてない!)
これは単一責任の原則(SRP)に違反しています。画像クラスは「画像を表現する」ことだけに集中すべきです。余計な仕事を押し付けられた画像クラスは、いつか限界を迎えます。
Protection Proxyで解決する
Protection Proxyを導入して、アクセス制御の責務を分離します。
Protection Proxyの構造
classDiagram
class GuardProxy {
+inner_proxy
+required_roles[]
+current_user
+render()
+render_full()
-_check_access()
}
class ImageProxy {
+name
+resolution
+render()
+render_full()
}
class GhostImage {
+name
+resolution
+render()
}
GuardProxy --> ImageProxy : wraps
ImageProxy --> GhostImage : lazy creates
note for GuardProxy "アクセス権限を\nチェックして通過/拒否"
アクセス制御のシーケンス
sequenceDiagram
participant User as ユーザー
participant Guard as GuardProxy
participant Proxy as ImageProxy
participant Real as GhostImage
User->>Guard: render_full()
Guard->>Guard: _check_access()
alt 権限なし (guest)
Guard-->>User: ⛔ アクセス拒否
else 権限あり (admin/vip)
Guard->>Proxy: render_full()
Proxy->>Real: render()
Real-->>Proxy: 画像データ
Proxy-->>Guard: 画像データ
Guard-->>User: 🖼️ 詳細表示
end
| |
実行結果は以下のようになります。
| |
何が変わったか
GhostImageクラスにはアクセス制御のコードがないGuardProxyがアクセス制御の責務を担うGuardProxyは内部にImageProxyを持ち、チェーンのように動作する- 権限要件は
required_rolesで柔軟に設定可能
Proxyのチェーン構造
今回のコードでは、2つのProxyがチェーンになっています。
| |
この構造のメリットは以下です。
- 各Proxyが単一の責務を持つ
- 必要に応じてProxyを追加・削除できる
- 既存のコードを変更せずに機能を拡張できる
Protection Proxyのポイント
権限チェックは呼び出し前に行う
| |
権限がなければ、内部のProxyにアクセスする前に処理を中断します。
権限要件を柔軟に設定する
| |
画像ごとに異なる権限要件を設定できます。VIP限定、管理者限定など、用途に応じて変更できます。
完成コード
最終的な完成コードを掲載します。
| |
まとめ
今回は、Protection Proxy(アクセス制御プロキシ)を学びました。
- 権限チェックを代理オブジェクトに分離
- 元のクラスはシンプルなまま維持
- 複数のProxyをチェーンして組み合わせ可能
次回は「何度も見るなら貯めたい」と題して、Caching Proxy(キャッシュプロキシ)を学びます。高解像度の再描画が遅い問題を、キャッシュで高速化します。
シリーズ全体の目次は以下をご覧ください。
