GoF in OCaml for Advent Calendar 2012 #6
OCaml Advent Calendar用の記事、第六弾目です。構造に関するパターンの最後、Proxyパターンやります。
http://ja.wikipedia.org/wiki/Proxy_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
Proxyパターンは、インターフェイス経由で実体にアクセスすることで、間に一つ中間層を入れ込もうというパターンです。他のパターンでもそうでしたが、JavaでのインターフェイスはOCamlではレコードにするとよいでしょう。
(* おまじない的補助関数. *) let print s = Printf.printf "%s\n" s let tee f x = f x; x let (+>) f g = g f (* Imageインターフェイスの代わり. 共通の操作を列挙する. *) type image = { display : unit -> unit; } module Image = struct type t = { fname : string; } (* 何か画像を表現するデータは省略.*) let loadImageFromDisk fname = (* なにか時間のかかるロード処理. *) print ("loading " ^ fname) let make fname = loadImageFromDisk fname; { fname } let display t = print ("displaying " ^ t.fname) (* 共通操作を抜き出す. *) let writer t = { display = fun () -> display t } end module ProxyImage = struct type t = { fname : string; (* Image.makeに渡す引数を取っておく. *) mutable cache : Image.t option; (* キャッシュ. *) } let make fname = { fname; cache = None } (* 共通操作を抜き出す. *) let writer t = { display = (fun () -> let image = match t.cache with | None -> (* キャッシュがなければ生成. *) Image.make t.fname +> tee (fun i -> t.cache <- Some i) | Some image -> image in Image.display image) } end let _ = let image1 = ProxyImage.writer (ProxyImage.make "HiRes_10MB_Photo1") in let image2 = ProxyImage.writer (ProxyImage.make "HiRes_10MB_Photo2") in let image3 = ProxyImage.writer (ProxyImage.make "HiRes_10MB_Photo3") in image1.display (); (* ここでロードされる. *) image2.display (); image2.display () (* ロード不要で表示.*) (* image3はロードされずに破棄される. *)
第一級のモジュールでやってもできますが、その場合には型tとdisplay関数を持つシグネチャーを定義する事になるでしょう。その型tがImage.tとProxyImage.tの二種類の型に対応する訳です。しかし、この型tには特に意味がない(他の型とユニファイする訳でもなく、戻り値の型になる訳でもない)ので、実際不要です。そこでdisplay関数だけのレコードで十分という判断になります。
レコードには型を隠す意味もあるんですね。
次回に続く!-> http://d.hatena.ne.jp/osiire/20121213