Proxy パターンは、あるオブジェクトと同じように振る舞う代理クラスを用意する、という実装テクニックである。
代理クラスを用意するテクニックはいろいろなところで使われる。例えば、
- String の代わりに HTMLString, RawString といったクラスを用意する
- ユーザーが入力した文字列を直接表示してしまうことによる XSS 攻撃を防ぐ
- Image の代わりに LazyImage クラスを用意し、実際に画像が必要になるまで読み込みを遅らせる
- 本物のオブジェクトの代わりに Mock オブジェクトを使う
- 実際のオブジェクトの代わりにそのオブジェクトの参照と参照カウンタを持つオブジェクトを使う
- 読み込むのに時間がかかるデータを待たずに Future オブジェクトを返す
- 代理人を用意するという点で Proxy パターンの亜種ではあるが、クラス図や振る舞いが異なるので同じパターンと言って良いかは微妙
クラス図
実装例
String の代わりに HTMLString, RawString といったクラスを用意する
trait SmartString {
def get: String
}
class RawString(val original: String) extends SmartString {
def get: String = original
}
class HtmlString[T <: SmartString](val original: T) extends SmartString {
lazy val escapedString = original.get
.replaceAll("<", "<")
.replaceAll(">", ">")
def get: String = escapedString
}
// 引数に HtmlString を指定することで、Escape されていない RawString が表示されるのを防ぐ
def showText[T <: SmartString](text: HtmlString[T]): Unit = {
println(text.get)
}
val x = new RawString("<script>alert(\"this is raw string\");</script>")
val y = new HtmlString(x)
// showText(x) // コンパイルエラー
showText(y) // <script>alert(\"this is raw string\");</script>
実装 in real world
- shared_ptr
- 直接オブジェクトの生成/破棄を管理する代わりに、参照カウントを保持するオブジェクトを生成して管理させる
- リファレンス https://isocpp.org/wiki/faq/cpp11-library#shared-ptr
- ライブラリ https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr.h#L103
- 画像の遅延ロード
- 実際に画像が必要になるまで読み込みを遅らせる
- https://github.com/tuupola/jquery_lazyload/blob/master/jquery.lazyload.js#L99
他のパターンとの関係
- Adaptor パターン
- Adapter パターンは、既存クラスを変更することなくインターフェースを変えるテクニックである。
- 同じ機能を持つ複数のサードパーティ製ライブラリを、ユーザーの好みで切り替えて使えるようにしたいが、インターフェースが各社バラバラで、サードパーティ製なのでインターフェースをいじれない。
- そういった時に使えるデザインパターン
- サードパーティ製ライブラリの「代わりに」使うという点で Proxy パターンと似ているが、本物クラスとプロキシクラスのインターフェースは共通していないという点で異なる。目的も違うので間違うことは少ないだろう
- Adapter パターンは、既存クラスを変更することなくインターフェースを変えるテクニックである。
- Decorator パターン
- Decorator パターンは、既存クラスを変更することなく新たな機能を加えるテクニックである。
- デコレートしたオブジェクトを、実際のオブジェクトの「代わりに」使うという点で、 Proxy パターンとよく似ている。
- 目的が異なる
- Decorator パターンは機能の追加に主眼をおいている
- Proxy パターンは本物のオブジェクトが何らかの理由(負荷やまだ存在しない、など)で利用出来ない場合にその代理を行う