GoF in OCaml for Advent Calendar 2012 #8
OCaml Advent Calendar用の記事、第八弾目です。commandパターンいってみましょう。
http://en.wikipedia.org/wiki/Command_pattern
上記のリンクは英語版です。(日本語版にはサンプルコードが載ってなかったので。@mrgchrさん情報ありがとうございます!)
コマンドパターンの「コマンド」とは、何か実行したい処理とそのパラメーターをカプセル化したものです。そのオブジェクトのコレクションを扱う事で、undoを統一的に処理したり、優先順位を付けて処理したり、後で再生して利用したりする事ができるようになるらしいです。
とりあえず例として載っているコードをOCaml化してみましょう。
(* コマンドが持つべき関数. これのコレクションを扱う. *) type command = { execute : unit -> unit; } module Switch = struct type t = command Queue.t (* OCamlの標準ライブラリにあるキューは破壊的. *) let make () = Queue.create () let storeAndExecute t command = command.execute (); (* コマンドを実行して、キューに入れておく. *) Queue.push command t end module Light = struct type t = unit (* 何か明かりを表現するものだけど、今回は省略 *) let make () = () let turnOn t = Printf.printf "the light is on\n" let turnOff t = Printf.printf "the light is off\n" end module FlipUp = struct type t = Light.t let make t = t let execute t = Light.turnOn t let command t = { (* FlipUp.tからcommandレコードの値を作る. *) execute = fun () -> execute t } end module FlipDown = struct type t = Light.t let make t = t let execute t = Light.turnOff t let command t = { execute = fun () -> execute t } end let _ = let lamp = Light.make () in let switchUp = FlipUp.command (FlipUp.make lamp) in let switchDown = FlipDown.command (FlipDown.make lamp) in let switch = Switch.make () in match Sys.argv.(1) with | "ON" -> Switch.storeAndExecute switch switchUp | "OFF" -> Switch.storeAndExecute switch switchDown | _ -> ()
共通する部分をレコードとして切り出して使う構造としては、Proxyパターンと殆ど一緒です。
Switchモジュールが持つコマンド履歴が破壊的なところに注目してください。こういう状態を示すものはOCamlでも破壊的に扱う事がよくあります。もちろんStateモナドにしても構いませんが、プログラム全体がデータフローで完結するとき以外は使わないかもしれません。
次回に続く!